From patchwork Tue Jan 4 16:26:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33063 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp17131450iog; Tue, 4 Jan 2022 08:28:46 -0800 (PST) X-Google-Smtp-Source: ABdhPJwBcMdvYyHeWW1V2c7k+LwUgkrWe46GuH5mqcwZ784tyGOue6/rdgrd2xwPNAIG7+1nepDh X-Received: by 2002:a17:906:1953:: with SMTP id b19mr39768880eje.530.1641313726179; Tue, 04 Jan 2022 08:28:46 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641313726; cv=none; d=google.com; s=arc-20160816; b=uWZpBKZErTAkJgJURJSjdKBhdns0m0NLceslOg7P484XEyrIyGrSL5h5EqAUTjzUik EUys1XzHGmaKjTUeqCNCMYb853xfwArO+54+Fj1UdVglJEoLWtG5qK3cAaVkDcTaz2EI K2ofFBuZ7wBQJtOChgBA7nhecSq4qTx+VB0rQAbpoHxZJ3geUA3eOMmNHYgBbZUnx4Yv s0Xq4HCmQ0ZxgGpkmw6NyvmvcPGvpjE+gVzrcQ2t35bqRloG2+SIIJLwo7X3ZIXUr9Er LLu36kYjUyVO/GjTabDM/X1bnInxJq3fh4TyVhFkr8BliogpvdkrqrrQXUx/YLtuCRSI Z7GQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=j5q/1K/tOu2hAVY+riwkT0aqh3iViRM8Fa2fthrz3fM=; b=H8TNWS0227S+M50xCHSgIxAyBT0oKmcorK69GpHhZq5S1QunXtiVDPpLH/BicCZ0S2 OvvGYLkMn5gynmVotToS6F/tKrVMM0l91odkzlogY2bbpzSJaCfV4D49KUASGKKjN3ht l5QnXubTh2tCkz7Mh2ioy6TjTEO+yi/8wQjsVk7sEQEQ8E7UNzk6vxu1HCM4plPN5mz7 4qKriAzkPIPlgIaFm5oHTHiDutbxyu2QD1GMFdwsfEWNV+Ae140WGqw+CJ4d7nqpsse1 tLEDWFu/H2BEpLhnBTczTVIM35xTb8bPNUQXUgukNxC79XiOO7TYMVMkGMP+HnCbJbiG Tmuw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=dcF5WgT7; 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 b2si18174767eds.103.2022.01.04.08.28.42; Tue, 04 Jan 2022 08:28:46 -0800 (PST) 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=dcF5WgT7; 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 DAF0068B274; Tue, 4 Jan 2022 18:28:37 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5486268B1E6 for ; Tue, 4 Jan 2022 18:28:31 +0200 (EET) Received: by mail-qk1-f181.google.com with SMTP id de30so35424839qkb.0 for ; Tue, 04 Jan 2022 08:28:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=VrLhPqQAezuE5hWDo+bfuRqg9fXJ3Gg023pDDbUDcvc=; b=dcF5WgT7tcqComTtH4UQBHMVZMKo83vQa4AEkUiWh6M37fUkWb6H0OPAdmvTHnJ9Wa EQfb36jJgHZICtQdlg325Y2iDZ6mHw4tICtOCZmpswIAwoDOZRqUNYL8dKTOYPAmIu82 7cfSyE36Uk+WbOqBEP+bXNND/RanHr28t10se8qCuHJBIzvO2/jOoR2WK4rJBsQUfovK 6dLmD0iqjnLtPx82ds6/Qwuux/A4dOFTPXpyFysAI/wlLB+4PkMGX3y84eH76UvT3Dtm 2nscrOZ3dp1iBrevxfYCdLnJCOWwZxvnN7dp/W7u+aYP0offnQ0fr+8AINxiH1YDYBmm Bn+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=VrLhPqQAezuE5hWDo+bfuRqg9fXJ3Gg023pDDbUDcvc=; b=Pn3KbWJlcZTgdXLRmE8TLL07PTFMld7ksSHyj9DGA4fyl3PTFWkXfa/w9QoHWZ7Gbm OZnqsKnzwt0l4D8xsfj9qQ921Md2L0FmVsTnP5wjo11XcogT+pE/imNNjHBvHGInjtvL p6lHgqyvOTriL9rhyzvSWal++jbh47ae7vHY8FbuMTiZ0EdiCop8HFfxZdGOmzQXyW7E gyVTE8JVw/JvxCYFz4oOltGkqA8yOrqSP8FcOCLkOwVEAQ9WpADHpE240yZBMXLlXkBm 810C6KOJVh0AqUeZIV/AQVvJ0IZ1BHZ8DwrPctYb8kPl4XAtbXnUBUEAfMGj4q2peCxR Cvdw== X-Gm-Message-State: AOAM531SGM+Qz7hqbmqfHvqUPbyN7HU1VDA+kUN22Sye3Ehdi/Uhyvua +L91/GyQYUZ10fuZuDH1C8eY6Q6DL08= X-Received: by 2002:a05:620a:2890:: with SMTP id j16mr35960067qkp.437.1641313709723; Tue, 04 Jan 2022 08:28:29 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id v5sm32623567qkp.126.2022.01.04.08.28.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 08:28:29 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 4 Jan 2022 11:26:28 -0500 Message-Id: <20220104162632.52767-1-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 1/5] avcodec/jpegxl: add Jpeg XL image codec and parser 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: JmT37GBk/djI This commit adds support to libavcodec to read and parse encoded Jpeg XL images. Jpeg XL is intended to be an extended-life replacement to legacy mjpeg. --- MAINTAINERS | 2 + libavcodec/Makefile | 1 + libavcodec/codec_desc.c | 9 + libavcodec/codec_id.h | 1 + libavcodec/jpegxl.h | 204 ++++++++++ libavcodec/jpegxl_parser.c | 794 +++++++++++++++++++++++++++++++++++++ libavcodec/parsers.c | 1 + libavcodec/version.h | 4 +- 8 files changed, 1014 insertions(+), 2 deletions(-) create mode 100644 libavcodec/jpegxl.h create mode 100644 libavcodec/jpegxl_parser.c diff --git a/MAINTAINERS b/MAINTAINERS index c065e94498..17c0104672 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -187,6 +187,7 @@ Codecs: interplayvideo.c Mike Melanson jni*, ffjni* Matthieu Bouron jpeg2000* Nicolas Bertrand + jpegxl.h, jpegxl_parser.c Leo Izen jvdec.c Peter Ross lcl*.c Roberto Togni, Reimar Doeffinger libcelt_dec.c Nicolas George @@ -615,6 +616,7 @@ Haihao Xiang (haihao) 1F0C 31E8 B4FE F7A4 4DC1 DC99 E0F5 76D4 76FC 437F Jaikrishnan Menon 61A1 F09F 01C9 2D45 78E1 C862 25DC 8831 AF70 D368 James Almer 7751 2E8C FD94 A169 57E6 9A7A 1463 01AD 7376 59E0 Jean Delvare 7CA6 9F44 60F1 BDC4 1FD2 C858 A552 6B9B B3CD 4E6A +Leo Izen (thebombzen) B6FD 3CFC 7ACF 83FC 9137 6945 5A71 C331 FD2F A19A Loren Merritt ABD9 08F4 C920 3F65 D8BE 35D7 1540 DAA7 060F 56DE Lynne FE50 139C 6805 72CA FD52 1F8D A2FE A5F0 3F03 4464 Michael Niedermayer 9FF2 128B 147E F673 0BAD F133 611E C787 040B 0FAB diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9577062eec..4a41bb2825 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -43,6 +43,7 @@ OBJS = ac3_parser.o \ dv_profile.o \ encode.o \ imgconvert.o \ + jpegxl_parser.o \ jni.o \ mathtables.o \ mediacodec.o \ diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 0974ee03de..0f3d0f910b 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1862,6 +1862,15 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("GEM Raster image"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_JPEGXL, + .type = AVMEDIA_TYPE_VIDEO, + .name = "jpegxl", + .long_name = NULL_IF_CONFIG_SMALL("JPEG XL"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | + AV_CODEC_PROP_LOSSLESS, + .mime_types= MT("image/jxl"), + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index ab265ec584..551a516446 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -308,6 +308,7 @@ enum AVCodecID { AV_CODEC_ID_SIMBIOSIS_IMX, AV_CODEC_ID_SGA_VIDEO, AV_CODEC_ID_GEM, + AV_CODEC_ID_JPEGXL, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/jpegxl.h b/libavcodec/jpegxl.h new file mode 100644 index 0000000000..434247a85d --- /dev/null +++ b/libavcodec/jpegxl.h @@ -0,0 +1,204 @@ +/* + * JPEG XL header + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL header + */ + +#ifndef AVCODEC_JPEGXL_H +#define AVCODEC_JPEGXL_H + +#include + +#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff +#define FF_JPEGXL_CODESTREAM_SIGNATURE_BE 0xff0a +#define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000 +#define FF_JPEGXL_CONTAINER_SIGNATURE_BE 0x0000000c4a584c20 + +enum JpegXLExtraChannelType { + FF_JPEGXL_CT_ALPHA = 0, + FF_JPEGXL_CT_DEPTH, + FF_JPEGXL_CT_SPOT_COLOR, + FF_JPEGXL_CT_SELECTION_MASK, + FF_JPEGXL_CT_BLACK, + FF_JPEGXL_CT_CFA, + FF_JPEGXL_CT_THERMAL, + FF_JPEGXL_CT_NON_OPTIONAL = 15, + FF_JPEGXL_CT_OPTIONAL +}; + +enum JpegXLColorSpace { + FF_JPEGXL_CS_RGB = 0, + FF_JPEGXL_CS_GRAY, + FF_JPEGXL_CS_XYB, + FF_JPEGXL_CS_UNKNOWN +}; + +enum JpegXLWhitePoint { + FF_JPEGXL_WP_D65 = 1, + FF_JPEGXL_WP_CUSTOM, + FF_JPEGXL_WP_E = 10, + FF_JPEGXL_WP_DCI = 11 +}; + +enum JpegXLPrimaries { + FF_JPEGXL_PR_SRGB = 1, + FF_JPEGXL_PR_CUSTOM, + FF_JPEGXL_PR_2100 = 9, + FF_JPEGXL_PR_P3 = 11, +}; + +enum JpegXLTransferFunction { + FF_JPEGXL_TF_709 = 1, + FF_JPEGXL_TF_UNKNOWN, + FF_JPEGXL_TF_LINEAR = 8, + FF_JPEGXL_TF_SRGB = 13, + FF_JPEGXL_TF_PQ = 16, + FF_JPEGXL_TF_DCI, + FF_JPEGXL_TF_HLG +}; + +enum JpegXLRenderingIntent { + FF_JPEGXL_RI_PERCEPTUAL = 0, + FF_JPEGXL_RI_RELATIVE, + FF_JPEGXL_RI_SATURATION, + FF_JPEGXL_RI_ABSOLUTE +}; + +typedef struct JpegXLExtraChannelInfo { + enum JpegXLExtraChannelType type; + uint32_t bits_per_sample; + uint32_t exp_bits_per_sample; + uint32_t dim_shift; + size_t name_len; + /* utf-8 */ + char *name; + int alpha_associated; + float red; + float green; + float blue; + float solidity; + uint32_t cfa_channel; +} JpegXLExtraChannelInfo; + +typedef struct JpegXLHeader { + uint32_t width; + uint32_t height; + int orientation; + /* zero if not present */ + uint32_t intrinsic_width; + uint32_t intrinsic_height; + uint32_t preview_width; + uint32_t preview_height; + /* BEGIN animation header */ + uint32_t anim_tb_num; + uint32_t anim_tb_denom; + uint32_t anim_loop_count; + int anim_have_pts; + /* END animation header */ + + uint32_t bits_per_sample; + uint32_t exp_bits_per_sample; + + int modular_16bit_buffers; + + uint32_t num_extra_channels; + + /* + * NULL if no extra channels + * otherwise an array of extra channel info + * with length num_extra_channels + */ + JpegXLExtraChannelInfo *extra_channel_info; + + int xyb_encoded; + + /* BEGIN color encoding bundle */ + int have_icc_profile; + enum JpegXLColorSpace color_space; + enum JpegXLWhitePoint white_point; + uint32_t white_ux; + uint32_t white_uy; + enum JpegXLPrimaries primaries; + uint32_t red_ux; + uint32_t red_uy; + uint32_t green_ux; + uint32_t green_uy; + uint32_t blue_ux; + uint32_t blue_uy; + /* + * if this is less than 1 << 24, + * then interpret it as a gamma value + * If this is greater than 1 << 24, then + * subtract 1 << 24 and interpret it as a + * an enum JpegXLTransferFunction + */ + uint32_t transfer_function; + enum JpegXLRenderingIntent rendering_intent; + /* END color encoding bundle */ + + /* BEGIN tone mapping bundle */ + float intensity_target; + float min_nits; + int relative_to_max_display; + float linear_below; + /* END tone mapping bundle */ + + uint64_t extensions; + /* if extensions is nonzero, this will be length 64 */ + uint64_t *extension_bits; + + int default_transform; + + /* if present, an array of length 16 */ + /* NULL if not present */ + float *opsin_inverse_matrix; + + uint32_t cw_mask; + + /* if these are not present + * use NULL for these pointers + * otherwise up2_weight is an + * array of length 15, up4_weight + * is length 55, and up8_weight is + * length 210 + */ + float *up2_weight; + float *up4_weight; + float *up8_weight; + + /* + * this is not provided by the header, + * but rather, by the container + * raw Jpeg XL Codestreams are level 5 + * the container can choose to up it to 10 + */ + int level; + +} JpegXLHeader; + +/** + * @return 0 upon valid, nonzero upon some parse error + */ +int avpriv_jpegxl_verify_codestream_header(void *avctx, uint8_t *buf, size_t buflen, int level); + +#endif /* AVCODEC_JPEGXL_H */ diff --git a/libavcodec/jpegxl_parser.c b/libavcodec/jpegxl_parser.c new file mode 100644 index 0000000000..3efbc71a53 --- /dev/null +++ b/libavcodec/jpegxl_parser.c @@ -0,0 +1,794 @@ +/* + * JPEG XL parser + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL parser + */ + +#include +#include +#include + +#ifndef BITSTREAM_READER_LE +#define BITSTREAM_READER_LE +#endif +#ifdef CACHED_BITSTREAM_READER +#undef CACHED_BITSTREAM_READER +#endif +#define CACHED_BITSTREAM_READER 1 + +#include "libavutil/error.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#include "get_bits.h" +#include "codec_id.h" +#include "parser.h" +#include "jpegxl.h" +#include "config.h" + +#if CONFIG_JPEGXL_PARSER + +typedef struct JpegXLParseContext { + ParseContext pc; + GetBitContext gb; + size_t buflen; + size_t bits_read; +} JpegXLParseContext; + +#define jxl_bits(n) jpegxl_get_bits(jxlr, (n)) +#define jxl_enum() jpegxl_u32(jxlr, (uint32_t[]){0, 1, 2, 18}, (uint32_t[]){0, 0, 4, 6}) + +#define jxl_parse_errv(type, value) av_log(avctx, AV_LOG_DEBUG, \ + "Invalid " type " at position: %zu", \ + value, jxlr->bits_read) + +#define jxl_parse_errvv(type, v1, v2) av_log(avctx, AV_LOG_DEBUG, \ + "Invalid " type " at position: %zu", \ + v1, v2, jxlr->bits_read) + +#define jxl_parse_err(type) jxl_parse_errv("%s", type) + +static void jpegxl_init_pc(JpegXLParseContext *jxlr, const uint8_t *buf, size_t buflen) +{ + memset(&jxlr->pc, 0, sizeof(ParseContext)); + jxlr->buflen = buflen; + jxlr->bits_read = 0; + init_get_bits8(&jxlr->gb, buf, buflen); +} + +static uint64_t jpegxl_get_bits(JpegXLParseContext *jxlr, size_t bits) +{ + if (!bits) + return 0; + if (bits > INT_MAX) + /* what are you doing with all those bits... */ + return 0; + jxlr->bits_read += bits; + /* extra buffer size of 8 bytes */ + if ((jxlr->bits_read + 1) / 8 + 8 > jxlr->buflen - 1) + /* overflowing buffer */ + return 0; + while (bits > 64) { + size_t bitcount = (bits - 1) % 64 + 1; + get_bits_long(&jxlr->gb, bitcount); + bits -= bitcount; + } + return get_bits_long(&jxlr->gb, bits); +} + +static uint32_t jpegxl_u32(JpegXLParseContext *jxlr, + uint32_t *constants, uint32_t *ubits) +{ + uint32_t ret, choice = jxl_bits(2); + ret = constants[choice]; + if (ubits[choice]) + ret += jxl_bits(ubits[choice]); + return ret; +} + +static uint64_t jpegxl_u64(JpegXLParseContext *jxlr) +{ + uint64_t shift = 12, ret; + switch (jxl_bits(2)) { + case 0: + ret = 0; + break; + case 1: + ret = 1 + jxl_bits(4); + break; + case 2: + ret = 17 + jxl_bits(8); + break; + case 3: + ret = jxl_bits(12); + while (jxl_bits(1)){ + if (shift < 60) { + ret |= jxl_bits(8) << shift; + shift += 8; + } else { + ret |= jxl_bits(4) << shift; + break; + } + } + break; + } + return ret; +} + +static float jpegxl_f16(JpegXLParseContext *jxlr) +{ + float ret; + uint32_t mantissa = jxl_bits(10) << 13; + uint32_t biased_exponent = jxl_bits(5); + if (biased_exponent == 31) + mantissa |= 0xFF << 23; + else + mantissa |= ((biased_exponent - 15 + 127) & 0xFF) << 23; + memcpy(&ret, &mantissa, sizeof(float)); + return ret; +} + +static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio) +{ + switch (ratio){ + case 1: + return height; + case 2: + return (height * 12) / 10; + case 3: + return (height * 4) / 3; + case 4: + return (height * 3) / 2; + case 5: + return (height * 16) / 9; + case 6: + return (height * 5) / 4; + case 7: + return height * 2; + default: + /* manual width */ + return 0; + } +} + + +static int jpegxl_parse_size_header(JpegXLParseContext *jxlr, + uint32_t *width, uint32_t *height) +{ + uint32_t w, h; + if (jxl_bits(1)) { + /* small size header */ + h = (jxl_bits(5) + 1) << 3; + w = jpegxl_width_from_ratio(h, jxl_bits(3)); + if (!w) + w = (jxl_bits(5) + 1) << 3; + } else { + /* large size header */ + h = 1 + jpegxl_u32(jxlr, + (uint32_t[]){0, 0, 0, 0}, (uint32_t[]){9, 13, 18, 30}); + w = jpegxl_width_from_ratio(h, jxl_bits(3)); + if (!w) + w = 1 + jpegxl_u32(jxlr, + (uint32_t[]){0, 0, 0, 0}, (uint32_t[]){9, 13, 18, 30}); + } + *width = w, *height = h; + return 0; +} + +static int jpegxl_parse_preview_header(JpegXLParseContext *jxlr, + uint32_t *width, uint32_t *height) +{ + uint32_t w, h; + if (jxl_bits(1)) { + /* div8 */ + h = jpegxl_u32(jxlr, + (uint32_t[]){16, 32, 1, 33}, (uint32_t[]){0, 0, 5, 9}) << 3; + w = jpegxl_width_from_ratio(h, jxl_bits(3)); + if (!w) + w = jpegxl_u32(jxlr, + (uint32_t[]){16, 32, 1, 33}, (uint32_t[]){0, 0, 5, 9}) << 3; + } else { + /* full */ + h = jpegxl_u32(jxlr, + (uint32_t[]){1, 65, 321, 1345}, (uint32_t[]){6, 8, 10, 12}); + w = jpegxl_width_from_ratio(h, jxl_bits(3)); + if (!w) + w = jpegxl_u32(jxlr, + (uint32_t[]){1, 65, 321, 1345}, (uint32_t[]){6, 8, 10, 12}); + } + *width = w, *height = h; + return 0; +} + +static int jpegxl_parse_animation_header(JpegXLParseContext *jxlr, + uint32_t *num, uint32_t *denom, uint32_t *count, int *have_pts) +{ + uint32_t n, d, c; + int p; + n = jpegxl_u32(jxlr, + (uint32_t[]){100, 1000, 1, 1}, (uint32_t[]){0, 0, 10, 30}); + d = jpegxl_u32(jxlr, + (uint32_t[]){1, 1001, 1, 1}, (uint32_t[]){0, 0, 8, 10}); + c = jpegxl_u32(jxlr, + (uint32_t[]){0, 0, 0, 0}, (uint32_t[]){0, 3, 16, 32}); + p = jxl_bits(1); + *num = n, *denom = d, *count = c, *have_pts = p; + return 0; +} + +static int jpegxl_parse_bit_depth(JpegXLParseContext *jxlr, + uint32_t *depth, uint32_t *exp_depth) +{ + uint32_t d, e; + if (jxl_bits(1)) { + /* float samples */ + d = jpegxl_u32(jxlr, + (uint32_t[]){32, 16, 24, 1}, (uint32_t[]){0, 0, 0, 6}); + e = jxl_bits(4) + 1; + } else { + /* integer samples */ + d = jpegxl_u32(jxlr, + (uint32_t[]){8, 10, 12, 1}, (uint32_t[]){0, 0, 0, 6}); + e = 0; + } + *depth = d, *exp_depth = e; + return 0; +} + +static int jpegxl_parse_extra_channel_info(JpegXLParseContext *jxlr, + JpegXLExtraChannelInfo *info, int level) +{ + int status = 0; + int all_default = jxl_bits(1); + + if (!all_default) { + info->type = jxl_enum(); + if (info->type > 63) + /* enum types cannot be 64+ */ + return 1; + status = jpegxl_parse_bit_depth(jxlr, &info->bits_per_sample, &info->exp_bits_per_sample); + if (!status) + return status; + info->dim_shift = jpegxl_u32(jxlr, (uint32_t[]){0, 3, 4, 1}, (uint32_t[]){0, 0, 0, 3}); + info->name_len = jpegxl_u32(jxlr, (uint32_t[]){0, 0, 16, 48}, (uint32_t[]){0, 4, 5, 10}); + } else { + info->type = FF_JPEGXL_CT_ALPHA; + info->bits_per_sample = 8; + info->exp_bits_per_sample = 0; + } + + info->name = av_malloc(info->name_len + 1); + if (!info->name) + return AVERROR(ENOMEM); + + for (uint32_t i = 0; i < info->name_len; i++) + /* there is no byte-alignment guarantee so no memcpy */ + info->name[i] = jxl_bits(8); + + /* null-terminate it for string operations */ + /* even though we have name_len */ + info->name[info->name_len] = '\0'; + + info->alpha_associated = + !all_default && info->type == FF_JPEGXL_CT_ALPHA && jxl_bits(1); + + if (info->type == FF_JPEGXL_CT_SPOT_COLOR) { + info->red = jpegxl_f16(jxlr); + info->green = jpegxl_f16(jxlr); + info->blue = jpegxl_f16(jxlr); + info->solidity = jpegxl_f16(jxlr); + } + + if (info->type == FF_JPEGXL_CT_CFA) + info->cfa_channel = jpegxl_u32(jxlr, (uint32_t[]){1, 0, 3, 19}, (uint32_t[]){0, 2, 4, 8}); + else + info->cfa_channel = 1; + + if (info->type == FF_JPEGXL_CT_BLACK && level < 10) + return 1; + + return 0; +} + +static void jpegxl_free_header(JpegXLHeader *header) +{ + if (header) { + if (header->extra_channel_info) { + for (uint32_t i = 0; i < header->num_extra_channels; i++) { + if (header->extra_channel_info + i) + av_freep(&header->extra_channel_info[i].name); + } + av_freep(&header->extra_channel_info); + } + if (header->extension_bits) + av_freep(&header->extension_bits); + if (header->opsin_inverse_matrix) + av_freep(&header->opsin_inverse_matrix); + if (header->up2_weight) + av_freep(&header->up2_weight); + if (header->up4_weight) + av_freep(&header->up4_weight); + if (header->up8_weight) + av_freep(&header->up8_weight); + av_freep(&header); + } +} + +/** + * Parse a JpegXL Codestream Header and read it into the argument Header + * @param level Codestream level provided by the container, 5 if raw codestream + * @return 0 upon success, negative upon error, and positive if the buffer overran + */ +static int jpegxl_parse_codestream_header(void *avctx, JpegXLParseContext *jxlr, JpegXLHeader **headerp, int level) +{ + JpegXLHeader *header = NULL; + int all_default, extra_fields = 0, status; + + header = av_mallocz(sizeof(JpegXLHeader)); + if (!header) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate JpegXLHeader"); + status = AVERROR(ENOMEM); + goto fail; + } + + /* signature check */ + if (jxl_bits(16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) { + av_log(avctx, AV_LOG_DEBUG, "Failed JPEG XL Signature Check"); + goto fail; + } + + status = jpegxl_parse_size_header(jxlr, + &header->width, &header->height); + if (status) { + jxl_parse_err("size header"); + goto fail; + } + + /* level 5 codestream */ + if (level < 10) { + if (header->width > (1 << 18) || header->height > (1 << 18) + || (header->width >> 4) * (header->height >> 4) > (1 << 20)) { + jxl_parse_err("width or height or both"); + goto fail; + } + header->level = 5; + } else { + if (header->width > (1 << 30) || header->height > (1 << 30) + || (header->width >> 14) * (header->height >> 14) > (1 << 12)) { + jxl_parse_err("width or height or both"); + goto fail; + } + header->level = 10; + } + + all_default = jxl_bits(1); + + if (!all_default) + extra_fields = jxl_bits(1); + + if (extra_fields) { + header->orientation = jxl_bits(3); + /* intrinstic size */ + if (jxl_bits(1)) { + status = jpegxl_parse_size_header(jxlr, + &header->intrinsic_width, &header->intrinsic_height); + if (status) { + jxl_parse_err("intrinstic size header"); + goto fail; + } + } + + /* preview header */ + if (jxl_bits(1)) { + status = jpegxl_parse_preview_header(jxlr, + &header->preview_width, &header->preview_height); + if (status) { + jxl_parse_err("preview header"); + goto fail; + } + if (header->preview_width > 4096 || header->preview_height > 4096) { + jxl_parse_errvv("preview header size %" PRIu32 ", %" PRIu32, + header->preview_width, header->preview_height); + goto fail; + } + } + + /* animation header */ + if (jxl_bits(1)) { + status = jpegxl_parse_animation_header(jxlr, + &header->anim_tb_num, &header->anim_tb_denom, + &header->anim_loop_count, &header->anim_have_pts); + if (status) { + jxl_parse_err("animation header"); + goto fail; + } + } + + } + + if (!all_default) { + status = jpegxl_parse_bit_depth(jxlr, + &header->bits_per_sample, &header->exp_bits_per_sample); + if (status) { + jxl_parse_err("bit depth header"); + goto fail; + } + + header->modular_16bit_buffers = jxl_bits(1); + + if (!header->modular_16bit_buffers && level < 10) { + jxl_parse_err("modular 16bit buffers"); + goto fail; + } + + header->num_extra_channels = jpegxl_u32(jxlr, + (uint32_t[]){0, 1, 2, 1}, (uint32_t[]){0, 0, 4, 12}); + if (header->num_extra_channels > 256 || + level < 10 && header->num_extra_channels > 4) { + jxl_parse_err("too many extra channels"); + goto fail; + } + if (header->num_extra_channels) { + header->extra_channel_info = + av_calloc(header->num_extra_channels + 1, sizeof(JpegXLExtraChannelInfo)); + for (uint32_t i = 0; i < header->num_extra_channels; i++) { + status = jpegxl_parse_extra_channel_info(jxlr, header->extra_channel_info + i, level); + if (status) { + jxl_parse_errv("extra channel number %" PRIu32, i); + goto fail; + } + } + } + + header->xyb_encoded = jxl_bits(1); + + if (jxl_bits(1)) { + /* all_default for color encoding */ + header->have_icc_profile = 0; + header->color_space = FF_JPEGXL_CS_RGB; + header->white_point = FF_JPEGXL_WP_D65; + header->primaries = FF_JPEGXL_PR_SRGB; + header->transfer_function = (1 << 24) + FF_JPEGXL_TF_SRGB; + header->rendering_intent = FF_JPEGXL_RI_RELATIVE; + } else { + header->have_icc_profile = jxl_bits(1); + header->color_space = jxl_enum(); + if (header->color_space > 63) { + jxl_parse_errv("color space enum %" PRIu32, header->white_point); + goto fail; + } + if (header->color_space != FF_JPEGXL_CS_XYB + && !header->have_icc_profile) { + header->white_point = jxl_enum(); + if (header->white_point > 63) { + jxl_parse_errv("white point enum %" PRIu32, header->white_point); + goto fail; + } + } else { + header->white_point = FF_JPEGXL_WP_D65; + } + if (header->white_point == FF_JPEGXL_WP_CUSTOM) { + header->white_ux = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->white_uy = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + } + if (header->color_space != FF_JPEGXL_CS_XYB + && header->color_space != FF_JPEGXL_CS_GRAY + && !header->have_icc_profile) { + header->primaries = jxl_enum(); + if (header->primaries > 63) { + jxl_parse_errv("primaries enum %" PRIu32, header->primaries); + goto fail; + } + } else { + header->primaries = FF_JPEGXL_PR_SRGB; + } + if (header->primaries == FF_JPEGXL_PR_CUSTOM) { + header->red_ux = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->red_uy = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->green_ux = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->green_uy = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->blue_ux = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + header->blue_uy = jpegxl_u32(jxlr, (uint32_t[]){0, 524288, 1048576, 2097152}, (uint32_t[]){19, 19, 20, 21}); + } + if (!header->have_icc_profile) { + if (jxl_bits(1)) { + /* this is gamma */ + header->transfer_function = jxl_bits(24); + } else { + header->transfer_function = jxl_enum(); + if (header->transfer_function > 63) { + jxl_parse_errv("transfer function enum %" PRIu32, header->transfer_function); + goto fail; + } + /* + * higher than the highest possible gamma value + * marks it as an enum isntead of gamma + */ + header->transfer_function += 1 << 24; + } + header->rendering_intent = jxl_enum(); + if (header->rendering_intent > 63) { + jxl_parse_errv("rendering intent enum %" PRIu32, header->rendering_intent); + goto fail; + } + } else { + header->transfer_function = (1 << 24) + FF_JPEGXL_TF_SRGB; + header->rendering_intent = FF_JPEGXL_RI_RELATIVE; + } + } + + /* lazy && works with this macro */ + if (extra_fields && !jxl_bits(1)) { + header->intensity_target = jpegxl_f16(jxlr); + header->min_nits = jpegxl_f16(jxlr); + header->relative_to_max_display = jxl_bits(1); + header->linear_below = jpegxl_f16(jxlr); + } else { + header->intensity_target = 255; + } + + header->extensions = jpegxl_u64(jxlr); + if (header->extensions) { + header->extension_bits = av_calloc(64, sizeof(uint64_t)); + if (!header->extension_bits) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate extension bit array"); + status = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < 64; i++) { + if (header->extensions & (UINT64_C(1) << i)) + header->extension_bits[i] = jpegxl_u64(jxlr); + } + } + + } else { + header->modular_16bit_buffers = 1; + header->xyb_encoded = 1; + } + + header->default_transform = jxl_bits(1); + + /* lazy && works with this macro */ + if (!header->default_transform && header->xyb_encoded && !jxl_bits(1)) { + header->opsin_inverse_matrix = av_malloc_array(16, sizeof(float)); + if (!header->opsin_inverse_matrix) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate Opsin Inverse Matrix"); + status = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < 16; i++) { + header->opsin_inverse_matrix[i] = jpegxl_f16(jxlr); + } + } + + if (!header->default_transform) { + header->cw_mask = jxl_bits(3); + } + + if (header->cw_mask & 1) { + header->up2_weight = av_malloc_array(15, sizeof(float)); + if (!header->up2_weight) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate up2_weight"); + status = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < 15; i++) { + header->up2_weight[i] = jpegxl_f16(jxlr); + } + } + if (header->cw_mask & 2) { + header->up4_weight = av_malloc_array(55, sizeof(float)); + if (!header->up2_weight) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate up4_weight"); + status = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < 55; i++) { + header->up4_weight[i] = jpegxl_f16(jxlr); + } + } + if (header->cw_mask & 4) { + header->up8_weight = av_malloc_array(210, sizeof(float)); + if (!header->up8_weight) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate up8_weight"); + status = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < 210; i++) { + header->up8_weight[i] = jpegxl_f16(jxlr); + } + } + + /* zero pad to byte */ + if (jxl_bits(7 - ((jxlr->bits_read - 1) % 8))) { + jxl_parse_err("zero padding to byte"); + goto fail; + } + + /* bytes consumed > buflen */ + if ((jxlr->bits_read + 1) / 8 - 1 > jxlr->buflen) { + jxl_parse_err("unexpected end of file"); + goto fail; + } + + *headerp = header; + return 0; + +fail: + jpegxl_free_header(header); + if (status >= 0 && (jxlr->bits_read + 1) / 8 - 1 > jxlr->buflen) + return FFMIN(jxlr->bits_read, INT_MAX); + if (status > 0) + return -status; + return status || -1; +} + +int avpriv_jpegxl_verify_codestream_header(void *avctx, uint8_t *buf, size_t buflen, int level) +{ + JpegXLParseContext jxlri; + JpegXLHeader *header = NULL; + int status; + jpegxl_init_pc(&jxlri, buf, buflen); + status = jpegxl_parse_codestream_header(avctx, &jxlri, &header, level); + if (header) + jpegxl_free_header(header); + return status; +} + +static enum AVPixelFormat jpegxl_header_get_pixfmt(JpegXLHeader *header) { + int alpha = 0; + for (int i = 0; i < header->num_extra_channels; i++) { + if ((header->extra_channel_info + i)->type == FF_JPEGXL_CT_ALPHA) { + alpha = 1; + break; + } + } + if (header->color_space == FF_JPEGXL_CS_GRAY) { + if (header->bits_per_sample <= 8) + return alpha ? AV_PIX_FMT_YA8 : AV_PIX_FMT_GRAY8; + if (header->bits_per_sample > 16 || header->exp_bits_per_sample) + return alpha ? AV_PIX_FMT_NONE : AV_PIX_FMT_GRAYF32LE; + return alpha ? AV_PIX_FMT_YA16LE : AV_PIX_FMT_GRAY16LE; + } else if (header->color_space == FF_JPEGXL_CS_RGB + || header->color_space == FF_JPEGXL_CS_XYB) { + if (header->bits_per_sample <= 8) + return alpha ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; + if (header->bits_per_sample > 16 || header->exp_bits_per_sample) + return alpha ? AV_PIX_FMT_GBRAPF32LE : AV_PIX_FMT_GBRPF32LE; + return alpha ? AV_PIX_FMT_RGBA64LE : AV_PIX_FMT_RGB48LE; + } + return AV_PIX_FMT_NONE; +} + +static av_cold int jpegxl_parse_init(AVCodecParserContext *s1) +{ + s1->pict_type = AV_PICTURE_TYPE_NONE; + return 0; +} + +static int jpegxl_parse(AVCodecParserContext *s1, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + JpegXLParseContext *jxlr = s1->priv_data; + JpegXLHeader *header = NULL; + int next = END_NOT_FOUND, container = 0, status = 0; + size_t i = 0; + + *poutbuf_size = 0; + *poutbuf = NULL; + + if (buf_size == 0 || s1->flags & PARSER_FLAG_COMPLETE_FRAMES) { + /* eof is a frame boundary */ + next = buf_size; + } else if (!jxlr->pc.frame_start_found) { + /* look for stream signature */ + uint64_t state64 = jxlr->pc.state64; + for (; i < buf_size; i++) { + state64 = (state64 << 8) | buf[i]; + if (state64 == FF_JPEGXL_CONTAINER_SIGNATURE_BE) + container = 1; + if ((state64 & 0xFFFF) == FF_JPEGXL_CODESTREAM_SIGNATURE_BE + || state64 == FF_JPEGXL_CONTAINER_SIGNATURE_BE) { + jxlr->pc.frame_start_found = 1; + break; + } + } + jxlr->pc.state64 = state64; + } + + if (jxlr->pc.frame_start_found && s1->pict_type == AV_PICTURE_TYPE_NONE) { + jpegxl_init_pc(jxlr, buf, buf_size); + if (container) { + for (;;) { + uint64_t size = 0; + uint32_t tag = 0; + for (int k = 0; k < 4; k++) + size = (size << 8) | jxl_bits(8); + for (int k = 0; k < 4; k++) + tag = (tag << 8) | jxl_bits(8); + if (tag == MKBETAG('j','x','l','p')) { + jxl_bits(32); + break; + } + if (tag == MKBETAG('j','x','l','c')) + break; + if (size == 1) { + size = 0; + for (int k = 0; k < 8; k++) + size = (size << 8) | jxl_bits(8); + if (size > INT_MAX) + break; + size -= 8; + } + if (jxlr->bits_read / 8 > buf_size) + break; + } + } + status = jpegxl_parse_codestream_header(NULL, jxlr, &header, 5); + if (status == 0) { + /* parsed successfully */ + s1->pict_type = AV_PICTURE_TYPE_I; + s1->key_frame = 1; + s1->width = avctx->width = avctx->coded_width = header->width; + s1->height = avctx->height = avctx->coded_height = header->height; + s1->format = avctx->pix_fmt = jpegxl_header_get_pixfmt(header); + jxlr->pc.frame_start_found = 1; + } + if (header) + jpegxl_free_header(header); + header = NULL; + } + + if (ff_combine_frame(&jxlr->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + jxlr->pc.frame_start_found = 0; + + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +const AVCodecParser ff_jpegxl_parser = { + .codec_ids = { AV_CODEC_ID_JPEGXL }, + .priv_data_size = sizeof(JpegXLParseContext), + .parser_init = jpegxl_parse_init, + .parser_parse = jpegxl_parse, + .parser_close = ff_parse_close, +}; + +#else /* CONFIG_JPEGXL_PARSER */ + +int avpriv_jpegxl_verify_codestream_header(void *avctx, uint8_t *buf, size_t buflen, int level) +{ + /* parser disabled at compile-time */ + return AVERROR(ENOSYS); +} + +#endif /* CONFIG_JPEGXL_PARSER */ diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index 6b40c18d80..18a40eceea 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -52,6 +52,7 @@ extern const AVCodecParser ff_h264_parser; extern const AVCodecParser ff_hevc_parser; extern const AVCodecParser ff_ipu_parser; extern const AVCodecParser ff_jpeg2000_parser; +extern const AVCodecParser ff_jpegxl_parser; extern const AVCodecParser ff_mjpeg_parser; extern const AVCodecParser ff_mlp_parser; extern const AVCodecParser ff_mpeg4video_parser; diff --git a/libavcodec/version.h b/libavcodec/version.h index 580b099b36..e2499b505b 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 59 -#define LIBAVCODEC_VERSION_MINOR 15 -#define LIBAVCODEC_VERSION_MICRO 101 +#define LIBAVCODEC_VERSION_MINOR 16 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ From patchwork Tue Jan 4 16:26:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33064 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp17131605iog; Tue, 4 Jan 2022 08:28:59 -0800 (PST) X-Google-Smtp-Source: ABdhPJwaqgzItVaF01VlebpC50UM5uwFdhdK0C9XRvfUzvi3NUmYXJFJGhsj2RowRpcqextz69Ix X-Received: by 2002:a17:906:3ed0:: with SMTP id d16mr39059429ejj.636.1641313739343; Tue, 04 Jan 2022 08:28:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641313739; cv=none; d=google.com; s=arc-20160816; b=AKnOQo0u+FE+FrGOFeWDStht5ESYgVFlM4Vg2L+3FykaqZg0wmeCTySxwZN/Hy3wwk ricP+OxQAymzbypTpdWQVWtX4nVybc1jAmgpZZ/eD5KT4BHFwFywwVuhF/Jykfmc23KE 6jHljmZJgNgF8lE0OaKcmIAqbeRIlQ/wmnWSYPS1uexe+nBP3iai0a2r9va6JhaVYWvA HgJ0tMEth26O0EfhrlHlIPnkC1ZjOSj/C3tXaQUu1z8IvR6nT8wMkL1lEInu6Rq7kWwN Gqv4QpccQcHeID4r6uS0yQxzFLB7xGL2o6PMGtKULFqiRd4r7WWkwIc865gaXxGmXwJH IIJw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=RCL/DW1bfzbJq+vVHYvvi7d1PDskOiMVmrxtSbIpciI=; b=fN17yYiWi91+kqxICeELDA7wF3gaQJfH/YRQx9AdYNNbewZMfKAVuJv8XnKKJaqguR /CN9f/r02eleCl0yhYk31m03AZGXsXl3OEtHYorALGhKeJyXFDxRn3KJtjF4RrZXOWnd l0h3gOpRGRqzDLDDpmwKIcKHCHuJh7Wzdd7b3N+UTMMnHFjpZy/I3I1XWdK81mSb5fI0 bdw6uuBKd8/BiJGf5S9OPtkJ9fNBLMNI4ooTJwKk/Qj3lsAIKyJUZAFPF1Stt8kkBsro kqfnO4sefnKpDNB3dcLdc9khKqk9rl0gWUrG95cmDTzNXuPrhye9d32VWLtckpunHSC4 9+Qg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=h3CrFfY3; 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 d11si22762205edm.263.2022.01.04.08.28.58; Tue, 04 Jan 2022 08:28:59 -0800 (PST) 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=h3CrFfY3; 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 F27AB68B245; Tue, 4 Jan 2022 18:28:55 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f172.google.com (mail-qt1-f172.google.com [209.85.160.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D001168B2A6 for ; Tue, 4 Jan 2022 18:28:49 +0200 (EET) Received: by mail-qt1-f172.google.com with SMTP id v4so32024027qtk.0 for ; Tue, 04 Jan 2022 08:28:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=e5Fwymch23QQf/du4tVIKtTI4A7hvK+Son+Qq688afY=; b=h3CrFfY3UbQMfXWCtKlXKsSzP785MBlS6Tq+fFugFGoBRP++fz6Js88DIts+sfGZVm hrIc/wlIa9PjE0F1vf7aLAeAjJvGPP82Bh0VhUbzyU8tX8efWQq7TdJh0aAtoSwBy0/C KrXd3rs+AWUAiLMWmHTW332VS1CwYKNthLVESXbqWDTEPqRqnWd2UhazYTn5FUkekC6A zqZi9CBFIN+USX2jkI61boEIMbGb641eisemuCWqR8WD9Ta6MgXWR8GvnhBG3syhFMu8 rC9BfZZd2fYeIGLcpusBUUU+BR2pXUFN/KxeZqG56lHlpQIJVbNHQNUVC5sZrmS1LP7Q a9ow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=e5Fwymch23QQf/du4tVIKtTI4A7hvK+Son+Qq688afY=; b=b//NVf75eLLU3MtabWWiEnxZv6FbWjRVFJlOVNmg0d72HIae3GJVRwosPZnldD3Ar0 r+y199yclP+gMBEX+PpBlUwik8Myq6AeuHkDyId7vGP1EK30EkNAqVT0Mgu+lLvXuJkR HG4CfGJS6EDUkredR4DgEnXCX9r4x180wjuxp76kf4vH3znI1HG4fPqgfzxEdzMLT01t Q70gxLvt+jOEmrZE0WKXfTH0Do8292JhkbsYijCCHIZ9XGZdDCD93KycrfX9S37tm40/ CsT0EVyzoZA7YBVgF6bf8wek5pKk1e0UuHgFajlRUNXtkh/+nSwgsVrxosM/YgXLUPgT DEdQ== X-Gm-Message-State: AOAM533fbm94zvV99M8wr7zyoYgkUsk46Y3magahPuOaMkQCSM8AKgdX GHieJ8NIbp0W2i53f617aNpz1+eQVEg= X-Received: by 2002:a05:622a:292:: with SMTP id z18mr45189189qtw.205.1641313728068; Tue, 04 Jan 2022 08:28:48 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id v5sm32623567qkp.126.2022.01.04.08.28.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 08:28:47 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 4 Jan 2022 11:26:29 -0500 Message-Id: <20220104162632.52767-2-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 2/5] avcodec/libjxl: add Jpeg XL decoding via libjxl 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: frMLsIP5iBOG This commit adds decoding support to libavcodec for Jpeg XL images via the external library libjxl. --- MAINTAINERS | 1 + configure | 5 + doc/general_contents.texi | 7 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/libjxl.c | 70 ++++++++++ libavcodec/libjxl.h | 48 +++++++ libavcodec/libjxldec.c | 276 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 409 insertions(+) create mode 100644 libavcodec/libjxl.c create mode 100644 libavcodec/libjxl.h create mode 100644 libavcodec/libjxldec.c diff --git a/MAINTAINERS b/MAINTAINERS index 17c0104672..a649223bcb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -194,6 +194,7 @@ Codecs: libcodec2.c Tomas Härdin libdirac* David Conrad libdavs2.c Huiwen Ren + libjxl*.c, libjxl.h Leo Izen libgsm.c Michel Bardiaux libkvazaar.c Arttu Ylä-Outinen libopenh264enc.c Martin Storsjo, Linjie Fu diff --git a/configure b/configure index 23ef2abc9b..9317485131 100755 --- a/configure +++ b/configure @@ -241,6 +241,7 @@ External library support: --enable-libiec61883 enable iec61883 via libiec61883 [no] --enable-libilbc enable iLBC de/encoding via libilbc [no] --enable-libjack enable JACK audio sound server [no] + --enable-libjxl enable JPEG XL decoding via libjxl [no] --enable-libklvanc enable Kernel Labs VANC processing [no] --enable-libkvazaar enable HEVC encoding via libkvazaar [no] --enable-liblensfun enable lensfun lens correction [no] @@ -1832,6 +1833,7 @@ EXTERNAL_LIBRARY_LIST=" libiec61883 libilbc libjack + libjxl libklvanc libkvazaar libmodplug @@ -3328,6 +3330,7 @@ libgsm_ms_decoder_deps="libgsm" libgsm_ms_encoder_deps="libgsm" libilbc_decoder_deps="libilbc" libilbc_encoder_deps="libilbc" +libjxl_decoder_deps="libjxl libjxl_threads" libkvazaar_encoder_deps="libkvazaar" libmodplug_demuxer_deps="libmodplug" libmp3lame_encoder_deps="libmp3lame" @@ -6539,6 +6542,8 @@ enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do check_lib libgsm "${gsm_hdr}" gsm_create -lgsm && break; done || die "ERROR: libgsm not found"; } enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc $pthreads_extralibs +enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/decode.h JxlDecoderVersion && + require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 0.8.1" kvazaar.h kvz_api_get enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_new diff --git a/doc/general_contents.texi b/doc/general_contents.texi index df1692c8df..2778e20091 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -171,6 +171,13 @@ Go to @url{https://github.com/TimothyGu/libilbc} and follow the instructions for installing the library. Then pass @code{--enable-libilbc} to configure to enable it. +@section libjxl + +JPEG XL is an image format intended to fully replace legacy JPEG for an extended +period of life. See @url{https://jpegxl.info/} for more information, and see +@url{https://github.com/libjxl/libjxl} for the library source. You can pass +@code{--enable-libjxl} to configure in order enable the libjxl wrapper. + @section libvpx FFmpeg can make use of the libvpx library for VP8/VP9 decoding and encoding. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 4a41bb2825..b3dcc7dcb1 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1038,6 +1038,7 @@ OBJS-$(CONFIG_LIBGSM_MS_DECODER) += libgsmdec.o OBJS-$(CONFIG_LIBGSM_MS_ENCODER) += libgsmenc.o OBJS-$(CONFIG_LIBILBC_DECODER) += libilbc.o OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o +OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d1e10197de..b41da3b0de 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -744,6 +744,7 @@ extern const AVCodec ff_libgsm_ms_encoder; extern const AVCodec ff_libgsm_ms_decoder; extern const AVCodec ff_libilbc_encoder; extern const AVCodec ff_libilbc_decoder; +extern const AVCodec ff_libjxl_decoder; extern const AVCodec ff_libmp3lame_encoder; extern const AVCodec ff_libopencore_amrnb_encoder; extern const AVCodec ff_libopencore_amrnb_decoder; diff --git a/libavcodec/libjxl.c b/libavcodec/libjxl.c new file mode 100644 index 0000000000..204d91d8a8 --- /dev/null +++ b/libavcodec/libjxl.c @@ -0,0 +1,70 @@ +/* + * JPEG XL de/encoding via libjxl, common support implementation + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL via libjxl common support implementation + */ + +#include "libavutil/cpu.h" +#include "libavutil/mem.h" + +#include +#include "libjxl.h" + +size_t ff_libjxl_get_threadcount(int threads) +{ + if (threads <= 0) + return av_cpu_count(); + if (threads == 1) + return 0; + return threads; +} + +/** + * Wrapper around av_malloc used as a jpegxl_alloc_func. + * + * @param opaque opaque pointer for jpegxl_alloc_func, always ignored + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if it cannot be allocated + */ +static void *libjxl_av_malloc(void *opaque, size_t size) +{ + return av_malloc(size); +} + +/** + * Wrapper around av_free used as a jpegxl_free_func. + * + * @param opaque opaque pointer for jpegxl_free_func, always ignored + * @param address Pointer to the allocated block, to free. `NULL` permitted as a no-op. + */ +static void libjxl_av_free(void *opaque, void *address) +{ + av_free(address); +} + +void ff_libjxl_init_memory_manager(JxlMemoryManager *manager) +{ + manager->opaque = NULL; + manager->alloc = &libjxl_av_malloc; + manager->free = &libjxl_av_free; +} diff --git a/libavcodec/libjxl.h b/libavcodec/libjxl.h new file mode 100644 index 0000000000..5387c438fd --- /dev/null +++ b/libavcodec/libjxl.h @@ -0,0 +1,48 @@ +/* + * JPEG XL de/encoding via libjxl, common support header + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL via libjxl common support header + */ + +#ifndef AVCODEC_LIBJXL_H +#define AVCODEC_LIBJXL_H + +#include + +/** + * Transform threadcount in ffmpeg to one used by libjxl. + * + * @param threads ffmpeg's threads AVOption + * @return thread count for libjxl's parallel runner + */ +size_t ff_libjxl_get_threadcount(int threads); + +/** + * Initialize and populate a JxlMemoryManager + * with av_malloc() and av_free() so libjxl will use these + * functions. + * @param manager a pointer to a JxlMemoryManager struct + */ +void ff_libjxl_init_memory_manager(JxlMemoryManager *manager); + +#endif /* AVCODEC_LIBJXL_H */ diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c new file mode 100644 index 0000000000..ba4a3c4f3a --- /dev/null +++ b/libavcodec/libjxldec.c @@ -0,0 +1,276 @@ +/* + * JPEG XL decoding support via libjxl + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL decoder using libjxl + */ + +#include "libavutil/avassert.h" +#include "libavutil/common.h" +#include "libavutil/error.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/frame.h" +#include "libavutil/version.h" + +#include "avcodec.h" +#include "internal.h" + +#include +#include +#include "libjxl.h" + +typedef struct LibJxlDecodeContext { + void *runner; + JxlDecoder *decoder; + JxlBasicInfo basic_info; + JxlPixelFormat jxl_pixfmt; + JxlDecoderStatus events; +} LibJxlDecodeContext; + +static int libjxl_init_jxl_decoder(AVCodecContext *avctx) +{ + LibJxlDecodeContext *ctx = avctx->priv_data; + + ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME; + if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events"); + return AVERROR_EXTERNAL; + } + + if (JxlDecoderSetParallelRunner(ctx->decoder, JxlThreadParallelRunner, ctx->runner) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner"); + return AVERROR_EXTERNAL; + } + + memset(&ctx->basic_info, 0, sizeof(JxlBasicInfo)); + memset(&ctx->jxl_pixfmt, 0, sizeof(JxlPixelFormat)); + return 0; +} + +static av_cold int libjxl_decode_init(AVCodecContext *avctx) +{ + LibJxlDecodeContext *ctx = avctx->priv_data; + JxlMemoryManager manager; + + ff_libjxl_init_memory_manager(&manager); + ctx->decoder = JxlDecoderCreate(&manager); + if (!ctx->decoder) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlDecoder"); + return AVERROR_EXTERNAL; + } + + ctx->runner = JxlThreadParallelRunnerCreate(&manager, ff_libjxl_get_threadcount(avctx->thread_count)); + if (!ctx->runner) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlThreadParallelRunner"); + return AVERROR_EXTERNAL; + } + + return libjxl_init_jxl_decoder(avctx); +} + +static enum AVPixelFormat libjxl_get_pix_fmt(AVCodecContext *avctx, JxlBasicInfo *basic_info, JxlPixelFormat *format) +{ + format->endianness = JXL_LITTLE_ENDIAN; + format->num_channels = basic_info->num_color_channels + (basic_info->alpha_bits > 0); + /* av_malloc handles alignment already */ + format->align = 1; + /* Gray */ + if (basic_info->num_color_channels == 1) { + if (basic_info->bits_per_sample <= 8) { + format->data_type = JXL_TYPE_UINT8; + return basic_info->alpha_bits ? AV_PIX_FMT_YA8 : AV_PIX_FMT_GRAY8; + } + if (basic_info->exponent_bits_per_sample || basic_info->bits_per_sample > 16) { + if (basic_info->alpha_bits) + return AV_PIX_FMT_NONE; + format->data_type = JXL_TYPE_FLOAT; + return AV_PIX_FMT_GRAYF32LE; + } + format->data_type = JXL_TYPE_UINT16; + return basic_info->alpha_bits ? AV_PIX_FMT_YA16LE : AV_PIX_FMT_GRAY16LE; + } + /* rgb only */ + /* libjxl only supports packed RGB and gray output at the moment */ + if (basic_info->num_color_channels == 3) { + if (basic_info->bits_per_sample <= 8) { + format->data_type = JXL_TYPE_UINT8; + return basic_info->alpha_bits ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; + } + if (basic_info->bits_per_sample > 16) + av_log(avctx, AV_LOG_WARNING, "Downsampling larger integer to 16-bit via libjxl\n"); + if (basic_info->exponent_bits_per_sample) + av_log(avctx, AV_LOG_WARNING, "Downsampling float to 16-bit integer via libjxl\n"); + format->data_type = JXL_TYPE_UINT16; + return basic_info->alpha_bits ? AV_PIX_FMT_RGBA64LE : AV_PIX_FMT_RGB48LE; + } + return AV_PIX_FMT_NONE; +} + +static void libjxl_row_fill(void *avframe, size_t x, size_t y, size_t num_pixels, const void *pixels) +{ + AVFrame *frame = avframe; + int bytes = av_get_padded_bits_per_pixel(av_pix_fmt_desc_get(frame->format)) / 8; + size_t offset = y * frame->linesize[0] + x * bytes; + memcpy(frame->data[0] + offset, pixels, num_pixels * bytes); +} + +static int libjxl_decode_frame(AVCodecContext *avctx, void *avframe, int *got_frame, AVPacket *avpkt) +{ + LibJxlDecodeContext *ctx = avctx->priv_data; + uint8_t *buf = avpkt->data; + size_t remaining = avpkt->size; + AVFrame *frame = avframe; + JxlDecoderStatus status; + int ff_status; + *got_frame = 0; + + for (;;) { + /* + * it only returns JXL_DEC_ERROR here if the input + * was not released since the last time this was called + * if this happens, it's a programmer error + */ + status = JxlDecoderSetInput(ctx->decoder, buf, remaining); + av_assert0(status != JXL_DEC_ERROR); + + status = JxlDecoderProcessInput(ctx->decoder); + /* + * JxlDecoderReleaseInput returns the number + * of bytes remaining to be read, rather than + * the number of bytes that it did read + */ + remaining = JxlDecoderReleaseInput(ctx->decoder); + buf = avpkt->data + avpkt->size - remaining; + + switch(status) { + case JXL_DEC_ERROR: + av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); + return AVERROR_EXTERNAL; + case JXL_DEC_NEED_MORE_INPUT: + if (remaining == 0) { + av_log(avctx, AV_LOG_WARNING, "Unexpected end of JXL codestream\n"); + return AVERROR(EAGAIN); + } + av_log(avctx, AV_LOG_DEBUG, "NEED_MORE_INPUT event emitted\n"); + continue; + case JXL_DEC_BASIC_INFO: + av_log(avctx, AV_LOG_DEBUG, "BASIC_INFO event emitted\n"); + if (JxlDecoderGetBasicInfo(ctx->decoder, &ctx->basic_info) != JXL_DEC_SUCCESS) { + /* + * this should never happen + * if it does it is likely a libjxl decoder bug + */ + av_log(avctx, AV_LOG_ERROR, "Bad libjxl basic info event\n"); + return AVERROR_EXTERNAL; + } + avctx->pix_fmt = libjxl_get_pix_fmt(avctx, &ctx->basic_info, &ctx->jxl_pixfmt); + if (avctx->pix_fmt == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, "Bad libjxl pixel format\n"); + return AVERROR_EXTERNAL; + } + ff_status = ff_set_dimensions(avctx, ctx->basic_info.xsize, ctx->basic_info.ysize); + if (ff_status < 0) + return ff_status; + /* + * We rewind the decoder and ask for everything again + * This futureproofs the decoder since it will make + * adding a parser or a dedicated demuxer much easier + */ + buf = avpkt->data; + remaining = avpkt->size; + JxlDecoderRewind(ctx->decoder); + ctx->events &= ~JXL_DEC_BASIC_INFO; + if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events after rewind\n"); + return AVERROR_EXTERNAL; + } + continue; + case JXL_DEC_FRAME: + case JXL_DEC_NEED_IMAGE_OUT_BUFFER: + /* + * We don't do this at basic info time + * because it will happen again when we + * rewind anyway + */ + av_log(avctx, AV_LOG_DEBUG, "%s event emitted\n", status == JXL_DEC_FRAME ? "FRAME" : "NEED_IMAGE_OUT_BUFFER"); + ff_status = ff_get_buffer(avctx, frame, 0); + if (ff_status < 0) + return ff_status; + if (JxlDecoderSetImageOutCallback(ctx->decoder, &ctx->jxl_pixfmt, &libjxl_row_fill, frame) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec need image out buffer event\n"); + return AVERROR_EXTERNAL; + } + continue; + case JXL_DEC_FULL_IMAGE: + /* full image is one frame, even if animated */ + av_log(avctx, AV_LOG_DEBUG, "FULL_IMAGE event emitted\n"); + *got_frame = 1; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + return avpkt->size - remaining; + case JXL_DEC_SUCCESS: + av_log(avctx, AV_LOG_DEBUG, "SUCCESS event emitted\n"); + /* + * The file has finished decoding + * reset the decoder to let us + * reuse it again for the next image + */ + JxlDecoderReset(ctx->decoder); + libjxl_init_jxl_decoder(avctx); + buf = avpkt->data; + remaining = avpkt->size; + continue; + default: + av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", status); + return AVERROR_EXTERNAL; + } + } +} + +static av_cold int libjxl_decode_close(AVCodecContext *avctx) +{ + LibJxlDecodeContext *ctx = avctx->priv_data; + if (ctx->runner) + JxlThreadParallelRunnerDestroy(ctx->runner); + ctx->runner = NULL; + if (ctx->decoder) + JxlDecoderDestroy(ctx->decoder); + ctx->decoder = NULL; + return 0; +} + +const AVCodec ff_libjxl_decoder = { + .name = "libjxl", + .long_name = NULL_IF_CONFIG_SMALL("libjxl JPEG XL decoder"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_JPEGXL, + .priv_data_size = sizeof(LibJxlDecodeContext), + .init = libjxl_decode_init, + .decode = libjxl_decode_frame, + .close = libjxl_decode_close, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP, + .wrapper_name = "libjxl", +}; From patchwork Tue Jan 4 16:26:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33065 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp17131695iog; Tue, 4 Jan 2022 08:29:09 -0800 (PST) X-Google-Smtp-Source: ABdhPJyhtSz7mMhx8mdRCSXW+BHBY27ecuxYdWdBNmEla7eu2c0Vyd7WEwz8FG0PKUqTtiDmp19i X-Received: by 2002:a17:906:b6d1:: with SMTP id ec17mr19808799ejb.21.1641313749153; Tue, 04 Jan 2022 08:29:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641313749; cv=none; d=google.com; s=arc-20160816; b=ZXf8xx1oGRWECnTnWrmYTDvwVdg2T7cz3OXOW3WUvOnG2iSokRhM9OTwp8gMSXdSVg 9ntBdZ1lX7PgS5bhmX25x1cXVwnelZPG6L2MfV6BvkX/cBaf+JoXGRitIi0+yBGpJiXk Sz7p3bcflxRSAr77OlugTHYKYGP/a+nNF2bh/sgp2ihBtEIxuyijEPh9LQExO77k0Skx QMjoDpNpPlZXvNCtBTmdp5PFPnw+GqP9NWcxz/MDqBIALq+jpSImfsABo0fknQZfa2P9 aj+FyC+uuTgHpODtdpdNJLy+ng80yWmxNaCc7jVXtQjCNk8lFGMSiHwvrB/SG/g198vv Oz7Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=BEQGQiaYSHQ4eucMyZEaNWTWv4Io5L4KlANCKhHhgNk=; b=dCL5WaU6DJIMVWNE8atqUIwwlbzf+6+P3gM0eQsQTw7bQJPhPnZOS1W845ii6X1BOS v8jjv4sRfc4A5e2IZzBRVxIhkwhJ/JK4sV/LHb1nYQbJkitNq/ACIMCnoA03QSFZmEp0 rXMON84NvxoVMElN6uzU9dfLgefDVhDz1zj5O58pqpJNKOxTpGafRhd6lXj1kRRwqgQo 9LRrDGx4DMEpzNndSzkE3TqyCW2eMVuxEdxKDtpG7nwLgyGp/mLJSNh+ZZOuf+ziT9cO o4175rM1f42G6cT/ToBU6zkxjTrbhYdf+y7mbaubpTd6TwO73SrHArvNRS4CPDsTXn5w 046w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="jv/RErvK"; 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 a22si19758450eda.34.2022.01.04.08.29.08; Tue, 04 Jan 2022 08:29:09 -0800 (PST) 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="jv/RErvK"; 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 01BD068B2C1; Tue, 4 Jan 2022 18:28:57 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qk1-f174.google.com (mail-qk1-f174.google.com [209.85.222.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DE4FC68B2A7 for ; Tue, 4 Jan 2022 18:28:49 +0200 (EET) Received: by mail-qk1-f174.google.com with SMTP id t66so23910691qkb.4 for ; Tue, 04 Jan 2022 08:28:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=fwqeJzfc1QjtZWIzQ8lkz2g/9UOSs6oK6aOCIte6moU=; b=jv/RErvK1WRTKyn6KtdIVzFgWPe0qq2PtQdVdV4bs5BZ7dQHWM64ntFn5w8/vF5TVK dMylTQqBhBpkz50+IILos/Jna42/vDV4NKPxas3z9V3zOdvfPdrtPol5Tuy71sEoCXMU S4sRCAUgDQeSBfpl8ucUpIzEWd8m1piwY19W9UC4QKY1PGhs6uSOlhVKoRuIoa1zJho2 yGgDOtq5zsIrB5hq0uVFNB3SWNF1pszpxBztKgQt4T16trFNYeAbcmOAUD/FlSMGz/nO 3XwWW2FdSQoAxB992+hQeei2vzcPr58DB7Ukk7xJzeHev6wyfTzhv3LMylsuxo51JZHp 50PQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=fwqeJzfc1QjtZWIzQ8lkz2g/9UOSs6oK6aOCIte6moU=; b=qr5loCATpInXEhccYWfdEyLkshUqggQ0vyOEmyIUqRlrd59U5cfnvzsOXNKP0CltOE bVZj1lwheT45Vtej/GQI9XFSNEMWumdbZ8IsvFC0n02Cw4JXOmwsX0VJHBmVsn6GKlEz /oD6TqAO97J5LHlChd4a+Lt3MEnIMtuy3iiDEVOd2TM+d6GKchYmePXArZQUMbISSM1D LyHuUeeOR39v3LyQe4NlsTpwpA8RSohEkATYnImgKKBPSdvnM2YRk3jvvbugAR4YdNBb 8llJLUW++IOgHeSdN3aso+m9cg23kPByKGI41/Rq8E21ou/xRgjPjVhagOkn9C5oyw+b 0L7A== X-Gm-Message-State: AOAM531990A8l8RRvdmXh2yOKC5cFD6N9TJ/NyrEhSFaXUBx2Xy71gxA UIKhnCyGssgAjcWv2XLyw6+xtAuRSNc= X-Received: by 2002:a05:620a:290c:: with SMTP id m12mr33231898qkp.209.1641313728559; Tue, 04 Jan 2022 08:28:48 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id v5sm32623567qkp.126.2022.01.04.08.28.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 08:28:48 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 4 Jan 2022 11:26:30 -0500 Message-Id: <20220104162632.52767-3-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 3/5] avcodec/libjxl: add Jpeg XL encoding via libjxl 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: +P1b06dGVSUT This commit adds encoding support to libavcodec for Jpeg XL images via the external library libjxl. --- configure | 3 +- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/libjxlenc.c | 383 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 libavcodec/libjxlenc.c diff --git a/configure b/configure index 9317485131..9188e56fb5 100755 --- a/configure +++ b/configure @@ -241,7 +241,7 @@ External library support: --enable-libiec61883 enable iec61883 via libiec61883 [no] --enable-libilbc enable iLBC de/encoding via libilbc [no] --enable-libjack enable JACK audio sound server [no] - --enable-libjxl enable JPEG XL decoding via libjxl [no] + --enable-libjxl enable JPEG XL de/encoding via libjxl [no] --enable-libklvanc enable Kernel Labs VANC processing [no] --enable-libkvazaar enable HEVC encoding via libkvazaar [no] --enable-liblensfun enable lensfun lens correction [no] @@ -3331,6 +3331,7 @@ libgsm_ms_encoder_deps="libgsm" libilbc_decoder_deps="libilbc" libilbc_encoder_deps="libilbc" libjxl_decoder_deps="libjxl libjxl_threads" +libjxl_encoder_deps="libjxl libjxl_threads" libkvazaar_encoder_deps="libkvazaar" libmodplug_demuxer_deps="libmodplug" libmp3lame_encoder_deps="libmp3lame" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index b3dcc7dcb1..946deacdcd 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1039,6 +1039,7 @@ OBJS-$(CONFIG_LIBGSM_MS_ENCODER) += libgsmenc.o OBJS-$(CONFIG_LIBILBC_DECODER) += libilbc.o OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o +OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b41da3b0de..2e50991652 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -745,6 +745,7 @@ extern const AVCodec ff_libgsm_ms_decoder; extern const AVCodec ff_libilbc_encoder; extern const AVCodec ff_libilbc_decoder; extern const AVCodec ff_libjxl_decoder; +extern const AVCodec ff_libjxl_encoder; extern const AVCodec ff_libmp3lame_encoder; extern const AVCodec ff_libopencore_amrnb_encoder; extern const AVCodec ff_libopencore_amrnb_decoder; diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c new file mode 100644 index 0000000000..4309a91e22 --- /dev/null +++ b/libavcodec/libjxlenc.c @@ -0,0 +1,383 @@ +/* + * JPEG XL encoding support via libjxl + * Copyright (c) 2021 Leo Izen + * + * 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 + * JPEG XL encoder using libjxl + */ + +#include "libavutil/avutil.h" +#include "libavutil/error.h" +#include "libavutil/frame.h" +#include "libavutil/libm.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/version.h" + +#include "avcodec.h" +#include "internal.h" + +#include +#include +#include "libjxl.h" + +typedef struct LibJxlEncodeContext { + AVClass *class; + void *runner; + JxlEncoder *encoder; + JxlEncoderFrameSettings *options; + int effort; + float distance; + int modular; + uint8_t *buffer; + size_t buffer_size; +} LibJxlEncodeContext; + +/** + * Map a quality setting for -qscale roughly from libjpeg + * quality numbers to libjxl's butteraugli distance for + * photographic content. + * + * Setting distance explicitly is preferred, but this will + * allow qscale to be used as a fallback. + * + * This function is continuous and injective on [0, 100] which + * makes it monotonic. + * + * @param quality 0.0 to 100.0 quality setting, libjpeg quality + * @return Butteraugli distance between 0.0 and 15.0 + */ +static float quality_to_distance(float quality){ + if (quality >= 100.0) { + return 0.0; + } else if (quality >= 90.0) { + return (100.0 - quality) * 0.10; + } else if (quality >= 30.0) { + return 0.1 + (100.0 - quality) * 0.09; + } else if (quality > 0.0) { + return 15.0 - (59.0 * quality - 4350.0) * quality / 9000.0; + } else { + return 15.0; + } +} + +/** + * Initalize the decoder on a per-frame basis. All of these need to be set + * once each time the decoder is reset, which it must be each frame to make + * the image2 muxer work. + * + * @return 0 upon success, negative on failure. + */ +static int libjxl_init_jxl_encoder(AVCodecContext *avctx) +{ + + LibJxlEncodeContext *ctx = avctx->priv_data; + + /* reset the encoder every frame for image2 muxer */ + JxlEncoderReset(ctx->encoder); + + ctx->options = JxlEncoderFrameSettingsCreate(ctx->encoder, NULL); + if (!ctx->options) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoderOptions"); + return AVERROR_EXTERNAL; + } + + /* This needs to be set each time the decoder is reset */ + if (JxlEncoderSetParallelRunner(ctx->encoder, JxlThreadParallelRunner, ctx->runner) + != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner"); + return AVERROR_EXTERNAL; + } + + /* these shouldn't fail, libjxl bug notwithstanding */ + if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_EFFORT, ctx->effort) + != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set effort to: %d", ctx->effort); + return AVERROR_EXTERNAL; + } + + /* check for negative zero, our default */ + if (1.0f / ctx->distance == 1.0f / -0.0f) { + /* use ffmpeg.c -q option if passed */ + if (avctx->flags & AV_CODEC_FLAG_QSCALE) + ctx->distance = quality_to_distance((float)avctx->global_quality / FF_QP2LAMBDA); + else + /* default 1.0 matches cjxl */ + ctx->distance = 1.0; + } + + /* + * 0.01 is the minimum distance accepted for lossy + * interpreting any positive value less than this as minimum + */ + if (ctx->distance > 0.0 && ctx->distance < 0.01) + ctx->distance = 0.01; + if (JxlEncoderOptionsSetDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set distance: %f", ctx->distance); + return AVERROR_EXTERNAL; + } + + /* + * In theory the library should automatically enable modular if necessary, + * but it appears it won't at the moment due to a bug. This will still + * work even if that is patched. + */ + if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_MODULAR, + ctx->modular || ctx->distance <= 0.0 ? 1 : -1) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set modular"); + return AVERROR_EXTERNAL; + } + + return 0; +} + +/** + * Global encoder initialization. This only needs to be run once, + * not every frame. + */ +static av_cold int libjxl_encode_init(AVCodecContext *avctx) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlMemoryManager manager; + + ff_libjxl_init_memory_manager(&manager); + ctx->encoder = JxlEncoderCreate(&manager); + if (!ctx->encoder) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoder"); + return AVERROR_EXTERNAL; + } + + ctx->runner = JxlThreadParallelRunnerCreate(&manager, ff_libjxl_get_threadcount(avctx->thread_count)); + if (!ctx->runner) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlThreadParallelRunner"); + return AVERROR_EXTERNAL; + } + + ctx->buffer_size = 4096; + ctx->buffer = av_malloc(ctx->buffer_size); + + if (!ctx->buffer){ + av_log(avctx, AV_LOG_ERROR, "Could not allocate encoding buffer"); + return AVERROR(ENOMEM); + } + + return 0; +} + +/** + * Encode an entire frame. Currently animation, is not supported by + * this encoder, so this will always reinitialize a new still image + * and encode a one-frame image (for image2 and image2pipe). + */ +static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); + JxlBasicInfo info; + JxlColorEncoding jxl_color; + JxlPixelFormat jxl_fmt; + JxlEncoderStatus status; + int ff_status; + size_t available = ctx->buffer_size; + size_t bytes_written = 0; + uint8_t *next_out = ctx->buffer; + + ff_status = libjxl_init_jxl_encoder(avctx); + if (ff_status){ + av_log(avctx, AV_LOG_ERROR, "Error frame-initializing JxlEncoder"); + return ff_status; + } + + /* populate the basic info settings */ + JxlEncoderInitBasicInfo(&info); + jxl_fmt.num_channels = pix_desc->nb_components; + info.xsize = frame->width; + info.ysize = frame->height; + info.num_extra_channels = (jxl_fmt.num_channels + 1) % 2; + info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels; + info.bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; + info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample; + if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { + info.exponent_bits_per_sample = info.bits_per_sample > 16 ? 8 : 5; + info.alpha_exponent_bits = info.alpha_bits ? info.exponent_bits_per_sample : 0; + jxl_fmt.data_type = info.bits_per_sample > 16 ? JXL_TYPE_FLOAT : JXL_TYPE_FLOAT16; + JxlColorEncodingSetToLinearSRGB(&jxl_color, info.num_color_channels == 1); + } else { + info.exponent_bits_per_sample = 0; + info.alpha_exponent_bits = 0; + jxl_fmt.data_type = info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; + JxlColorEncodingSetToSRGB(&jxl_color, info.num_color_channels == 1); + } + + if (info.bits_per_sample > 16 + || info.xsize > (1 << 18) || info.ysize > (1 << 18) + || (info.xsize << 4) * (info.ysize << 4) > (1 << 20)) { + /* + * must upgrade codestream to level 10, from level 5 + * the encoder will not do this automatically + */ + if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Could not upgrade JXL Codestream level."); + return AVERROR_EXTERNAL; + } + } + + /* bitexact lossless requires there to be no XYB transform */ + info.uses_original_profile = ctx->distance <= 0.0; + + /* + * the color encoding is not used if uses_original_profile is false + * this just works around a bug in libjxl 0.7.0 and lower + */ + if (info.uses_original_profile){ + if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set JxlColorEncoding"); + return AVERROR_EXTERNAL; + } + } + + if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo"); + return AVERROR_EXTERNAL; + } + + jxl_fmt.endianness = JXL_LITTLE_ENDIAN; + jxl_fmt.align = frame->linesize[0]; + + if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0], jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame: %d", status); + return AVERROR_EXTERNAL; + } + + /* + * Run this after the last frame in the image has been passed. + * TODO support animation + */ + JxlEncoderCloseInput(ctx->encoder); + + for (;;){ + status = JxlEncoderProcessOutput(ctx->encoder, &next_out, &available); + if (status == JXL_ENC_ERROR) { + av_log(avctx, AV_LOG_ERROR, "Unspecified libjxl error occurred"); + return AVERROR_EXTERNAL; + } + bytes_written = ctx->buffer_size - available; + if (status == JXL_ENC_SUCCESS) { + /* all data passed has been encoded */ + break; + } + if (status == JXL_ENC_NEED_MORE_OUTPUT) { + /* + * at the moment, libjxl has no way to + * tell us how much space it actually needs + * so we need to malloc loop + */ + ctx->buffer_size = bytes_written * 2; + next_out = av_malloc(ctx->buffer_size); + if (!next_out) { + av_log(avctx, AV_LOG_ERROR, "Error reallocated encoder buffer"); + return AVERROR(ENOMEM); + } + memcpy(next_out, ctx->buffer, bytes_written); + av_freep(&ctx->buffer); + ctx->buffer = next_out; + next_out += bytes_written; + available = ctx->buffer_size - bytes_written; + continue; + } + } + /* + * This buffer will be copied when the generic + * code makes this packet refcounted, + * so we can use the buffer again. + */ + pkt->data = ctx->buffer; + pkt->size = bytes_written; + *got_packet = 1; + return 0; +} + +static av_cold int libjxl_encode_close(AVCodecContext *avctx) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + + if (ctx->runner) + JxlThreadParallelRunnerDestroy(ctx->runner); + ctx->runner = NULL; + + /* + * destroying the decoder also frees + * ctx->options so we don't need to + */ + if (ctx->encoder) + JxlEncoderDestroy(ctx->encoder); + ctx->encoder = NULL; + + if (ctx->buffer) + av_freep(&ctx->buffer); + ctx->buffer = NULL; + ctx->buffer_size = 0; + + return 0; +} + +#define OFFSET(x) offsetof(LibJxlEncodeContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM + +static const AVOption libjxl_encode_options[] = { + { "effort", "Encoding effort", OFFSET(effort), AV_OPT_TYPE_INT, { .i64 = 7 }, 1, 9, VE }, + { "distance", "Maximum Butteraugli distance (quality setting, " + "lower = better, zero = lossless, default 1.0)", OFFSET(distance), AV_OPT_TYPE_FLOAT, { .dbl = -0.0 }, 0.0, 15.0, VE }, + { "modular", "Force modular mode", OFFSET(modular), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { NULL }, +}; + +static const AVClass libjxl_encode_class = { + .class_name = "libjxl", + .item_name = av_default_item_name, + .option = libjxl_encode_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVCodec ff_libjxl_encoder = { + .name = "libjxl", + .long_name = NULL_IF_CONFIG_SMALL("libjxl JPEG XL encoder"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_JPEGXL, + .priv_data_size = sizeof(LibJxlEncodeContext), + .init = libjxl_encode_init, + .encode2 = libjxl_encode_frame, + .close = libjxl_encode_close, + .capabilities = AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP, + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGBA64LE, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, + AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_YA16LE, + AV_PIX_FMT_GRAYF32LE, + AV_PIX_FMT_NONE + }, + .priv_class = &libjxl_encode_class, + .wrapper_name = "libjxl", +}; From patchwork Tue Jan 4 16:26:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33066 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp17131783iog; Tue, 4 Jan 2022 08:29:19 -0800 (PST) X-Google-Smtp-Source: ABdhPJzzZYfcFrBClciefYv83KvwR9a0YTFBu/6WgnkKrgZLVDKHw0TRpZopP1sUD3OX/rBRR+BZ X-Received: by 2002:aa7:d0c5:: with SMTP id u5mr49161115edo.202.1641313759245; Tue, 04 Jan 2022 08:29:19 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641313759; cv=none; d=google.com; s=arc-20160816; b=HbXkrhRVu6RkanN2eDoTJhVeRVgX7Aw5JLAwMh+mZqxsL8vWoKzoLJkTKjCHzmIL2x yVBcpsA2CO6nzOij6nU3qFBIXrkDE34MP111bOMHtE8Z2kq2BYy5tWFwozhlfLqPVbn6 QuQV2YW/8IwrflLY7V39NOCT9e49JezIqVpNYiANF80r1Hg6lPXDeicbcAkoMlr7ZLpS T7FwNXB6Rh892qE4ngm0p72DmrfHoD3Y7gQpAJAII4yhkNj4dw23Lnj0z2DkNecOjRMq 6ncivMGgXH+IR7nPD59Gg2xNulmWYr5orezBryR4cUGCAJOnxKgQqnOsziKg6t0w3A2R IHbg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=O4DKs9ZT21kBYzn3P7x8h9oDm+cEi+YmcpSl4yUGMOQ=; b=zYgPh8d00F0wNkMsZiZx8BhrvCbQw1v8XfS3xMEPbWhcCrNWXTqcPGwOV0PnZQEEwO rgxd9mSQto8cXwWBwPxbsnEsB6rwh592vR64z1+dJqqrTuhpDwPrP9iLzurUWSimtq5v d/ZN371WBp+G5wKa55eZHC5k213FPxF0xpK5szxrDt965zoYjW3GRBEGOj/VTc2KtGQF pmckaXBaisk4283gR7EqJrSbFUA7d1IcyTaoeFq0vzQffHBl7QOA62mQEVv2XZE1R26p NZDyGdumj4+qt78iN/U6kaT9ysJ2es25FAhgAj7AUZsZHPObgBdA6+hZSnuN6CwsZfdq ZokA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=F+M09ne+; 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 dd9si19649752ejc.169.2022.01.04.08.29.18; Tue, 04 Jan 2022 08:29:19 -0800 (PST) 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=F+M09ne+; 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 01B7F68B2C7; Tue, 4 Jan 2022 18:28:58 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f181.google.com (mail-qt1-f181.google.com [209.85.160.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3405B68B1A6 for ; Tue, 4 Jan 2022 18:28:50 +0200 (EET) Received: by mail-qt1-f181.google.com with SMTP id z9so34602482qtj.9 for ; Tue, 04 Jan 2022 08:28:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qzhgsGplTd7VPTQDT4X62UcSwZAo/evDoj2eplVKzOs=; b=F+M09ne+ffgMEUytLWYlgSxuJuME/ct/cOXFw+SYOGjUkvDB5PCMSLouaUBtgFuPWJ jrD3sbHCIl58rX7jaPMNwiUNQ0mInxmo56r2nrHevUQG0Vx3l8wj+c00n/7FeGGJlH13 Izfu2VH6W5rC7QR//C5Gdc7Jj2HI6h1fH66k0qO+ep2RmvGoqXTxlKe4LTPZHOwM2eXr 7OwdWVxwbCqtZIQUCmT5xrJJWlR9WbiuHgEQXPiIYyGdcNrUql4O+hqmsA+y1xhk3niA QTtq/NVKmWG95Nts5glUElDt0y4PjDN2NShgRt6qXe9LO52vr05JcSpA0g3Rh0a7fMqk k3Ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qzhgsGplTd7VPTQDT4X62UcSwZAo/evDoj2eplVKzOs=; b=MZuimSu9+DREiVxWGLHertXF4urH8Kq8sdZlcblmAX39cpB9jsR85vGlDr7jN8VhS2 PNfoZSzSEfujxov9fmEd+1+qWsrnZn2YhdM0qJLPvb6KKgiW9jgz6Z+CrdOsC2C0fYBj 7AtOsPBpAxo9IIZy+b+GxJgg2Uxb33Hr+5G0u1t7PIssEiud6L7qcbTmXm7Jk8MnVtF6 fioHGeNWlsmeXVBTxEtMea510sKoVm4PbAdR+tWnlfvktqGAZH7TvI5h/U3+OM7r6gD+ 3kB1FVHdkNsDFUFl47apoBTT+yUIC5LQQlkvGCWHZ+8kxKe9Uc0dVJbPLhHLUd3Tsv87 SVeA== X-Gm-Message-State: AOAM533X90Uh3gOb9wX4O+980I6Ub1o3/E/UpyG+ygBgb0/yY1Abibsv Rl0Rp9iv52GNVv6zWyOoIUjJ3RIvT/Y= X-Received: by 2002:a05:622a:3ca:: with SMTP id k10mr45007915qtx.130.1641313729013; Tue, 04 Jan 2022 08:28:49 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id v5sm32623567qkp.126.2022.01.04.08.28.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 08:28:48 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 4 Jan 2022 11:26:31 -0500 Message-Id: <20220104162632.52767-4-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 4/5] avformat/image2: add Jpeg XL as image2 format 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: p/C2sNcoyk64 This commit adds support to libavformat for muxing and demuxing Jpeg XL images as image2 streams. --- libavformat/allformats.c | 1 + libavformat/img2.c | 1 + libavformat/img2dec.c | 19 +++++++++++++++++++ libavformat/img2enc.c | 6 +++--- libavformat/mov.c | 1 + libavformat/version.h | 2 +- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 1054ac9667..0fa1c82ddb 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -506,6 +506,7 @@ extern const AVInputFormat ff_image_gif_pipe_demuxer; extern const AVInputFormat ff_image_j2k_pipe_demuxer; extern const AVInputFormat ff_image_jpeg_pipe_demuxer; extern const AVInputFormat ff_image_jpegls_pipe_demuxer; +extern const AVInputFormat ff_image_jpegxl_pipe_demuxer; extern const AVInputFormat ff_image_pam_pipe_demuxer; extern const AVInputFormat ff_image_pbm_pipe_demuxer; extern const AVInputFormat ff_image_pcx_pipe_demuxer; diff --git a/libavformat/img2.c b/libavformat/img2.c index 4153102c92..13b1b997b8 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -87,6 +87,7 @@ const IdStrMap ff_img_tags[] = { { AV_CODEC_ID_GEM, "img" }, { AV_CODEC_ID_GEM, "ximg" }, { AV_CODEC_ID_GEM, "timg" }, + { AV_CODEC_ID_JPEGXL, "jxl" }, { AV_CODEC_ID_NONE, NULL } }; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index a6084ceef0..44336d812d 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -30,6 +30,7 @@ #include "libavutil/parseutils.h" #include "libavutil/intreadwrite.h" #include "libavcodec/gif.h" +#include "libavcodec/jpegxl.h" #include "avformat.h" #include "avio_internal.h" #include "internal.h" @@ -834,6 +835,23 @@ static int jpegls_probe(const AVProbeData *p) return 0; } +static int jpegxl_probe(const AVProbeData *p) +{ + const uint8_t *b = p->buf; + + /* ISOBMFF-based container */ + /* 0x4a584c20 == "JXL " */ + if (AV_RL64(b) == FF_JPEGXL_CONTAINER_SIGNATURE_LE) + return AVPROBE_SCORE_EXTENSION + 1; + /* Raw codestreams all start with 0xff0a */ + if (AV_RL16(b) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) + return 0; + if (avpriv_jpegxl_verify_codestream_header(NULL, p->buf, p->buf_size, 5) == 0) + return AVPROBE_SCORE_MAX - 2; + else + return 0; +} + static int pcx_probe(const AVProbeData *p) { const uint8_t *b = p->buf; @@ -1153,6 +1171,7 @@ IMAGEAUTO_DEMUXER(gif, AV_CODEC_ID_GIF) IMAGEAUTO_DEMUXER(j2k, AV_CODEC_ID_JPEG2000) IMAGEAUTO_DEMUXER(jpeg, AV_CODEC_ID_MJPEG) IMAGEAUTO_DEMUXER(jpegls, AV_CODEC_ID_JPEGLS) +IMAGEAUTO_DEMUXER(jpegxl, AV_CODEC_ID_JPEGXL) IMAGEAUTO_DEMUXER(pam, AV_CODEC_ID_PAM) IMAGEAUTO_DEMUXER(pbm, AV_CODEC_ID_PBM) IMAGEAUTO_DEMUXER(pcx, AV_CODEC_ID_PCX) diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index ded91d6b98..6ac430c6dd 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -261,9 +261,9 @@ static const AVClass img2mux_class = { const AVOutputFormat ff_image2_muxer = { .name = "image2", .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), - .extensions = "bmp,dpx,exr,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv,png," - "ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8,im24," - "sunras,xbm,xface,pix,y", + .extensions = "bmp,dpx,exr,jls,jpeg,jpg,jxl,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv," + "png,ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8," + "im24,sunras,xbm,xface,pix,y", .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, diff --git a/libavformat/mov.c b/libavformat/mov.c index 2aed6e80ef..4dcae73c3b 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -7463,6 +7463,7 @@ static int mov_probe(const AVProbeData *p) if (tag == MKTAG('f','t','y','p') && ( AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ') || AV_RL32(p->buf + offset + 8) == MKTAG('j','p','x',' ') + || AV_RL32(p->buf + offset + 8) == MKTAG('j','x','l',' ') )) { score = FFMAX(score, 5); } else { diff --git a/libavformat/version.h b/libavformat/version.h index 379a68cc7c..9b90bbf156 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 59 -#define LIBAVFORMAT_VERSION_MINOR 10 +#define LIBAVFORMAT_VERSION_MINOR 11 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ From patchwork Tue Jan 4 16:26:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33067 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp17131873iog; Tue, 4 Jan 2022 08:29:29 -0800 (PST) X-Google-Smtp-Source: ABdhPJxvxwY64cg4WvziLM1BGKPIpR3DG/TPjVva0aBQL0i0ZiIhE3DLr2zcPx5ARFxw8osY8q9O X-Received: by 2002:a17:906:5d01:: with SMTP id g1mr39704910ejt.219.1641313769034; Tue, 04 Jan 2022 08:29:29 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641313769; cv=none; d=google.com; s=arc-20160816; b=uRfw2rhLGUAYoOVQJeV1Z/n3/2H2EQrAGEiGt+PAnhCZmzr0JlkHTtnWjQTmw6qCWc TZL+v9k/bE7O+OzebXYOQPvGIxOJaJTEPccsUvhVarFjbtX84OWroQw30WLK2QDyO7Dm 2fuwagfU96ZnIYDze0ToTNxhyvAOc7tWlH9WPFOu7/LWpsCebeFWh3UU2B+olaqioZIp LBLMIatgHp8nfr3fX5gzHRfj2jt9X6llLcYLlAkWGWMyrFy1k6JzTVjmDAsAn7ar8hdy Xx8u7meLQkrkXcwHf+/QfP/r9gJhvXOLnT2JAAyv8fh84fTxZGJ2hQakRCM1iG3xCMBS hHtw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=PkCkw2VGhc5aSZXoijBD9fUVA9lhNdS/7ty7nc26T4w=; b=iRg/DN+0LaIw1kRyhSCC38d96aQPv//kLPZO2TjS88SRyTsfq9ckOYh+ut2Yxu1b/e RHWCJ3/f939FDXpTnGXe2o3VBpcIOOPCOAb9F+jmW0ZOQ6JjTuYZUmKjZTyO0atMVswl f2MGirdmlaXqGfylT+KzJASWvVXizK22v719PxPuzaP6MYmoTPuckMY3Eo7kqXmn6Fny u4jdKTzjQMkKh55yRyD5dpEMRprO9Wukc1GuIPXhi4xLKnj1UjRFd2y8PyyyOGU1tyEn k66fDGlL0GV8EXrlH2BtAUDyTKPSuSowINOQPD59PesIZzci14I6GYZxDEfbtRdUzYgc 9XIQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=dbhpRi2l; 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 hb12si18441394ejc.650.2022.01.04.08.29.28; Tue, 04 Jan 2022 08:29:29 -0800 (PST) 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=dbhpRi2l; 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 0C58268B2CD; Tue, 4 Jan 2022 18:28:59 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f42.google.com (mail-qv1-f42.google.com [209.85.219.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B57BD68B2B5 for ; Tue, 4 Jan 2022 18:28:50 +0200 (EET) Received: by mail-qv1-f42.google.com with SMTP id kj16so34820811qvb.2 for ; Tue, 04 Jan 2022 08:28:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=NlulufVn30kk4HsEIjKVJ923MgLx0RB+ZVM6qhEn0Zw=; b=dbhpRi2l3oRLQ7CO/6M0TefBOKsyaMaFkG1oEje0yEd8w0z0pGRnKsxR9cFfMlP4lq TUZIQgOyz42UXiXvnW6G7hSBX9+ymr9G8dQqr9SkYYoHb6U/2MUyIKQPL4m5QEDwHDbF 0CfJ09Q8IUUydRsC3GmsK+EjyqRtITzYH6skdROjyN9BlC9/agYB9U8i7C0mV0C4U+zx cIGTzT27NRe2LNH2bhrEWzQ5vjOa8/oWMg8V8PuKEwL7/pxbrtG+3PcrYG4gns0wclR0 6bFJpmTGLDGijX+YZxYQIyr4x/J1pbveWPaCp+PjHYvTzJ1Nq8aFjw9EFl5Pt0CEWBP0 3KrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=NlulufVn30kk4HsEIjKVJ923MgLx0RB+ZVM6qhEn0Zw=; b=zj3FAsJhMdw3b2OHLgEYS6KEW/tTW164tpWfdtzk/1ZN0szqYYOyPy/afxEyWAKhus MLhHOTkybaKcmC5SSRL7Cb6JsoQKntkP3qkkFOsBY7jE0rNsqal9gc3i/EzfEtk1xIqI J+JpoiwpTFHUDuQvM9GK3z6XYseSxbeTVhGAcLDrimEdl4MQvHNaSfIUrm1Cys8YGgNL Vb5ezK7HCHXSRkfDjYYdGqERlKtkq1wUYV/xoFkmtdA0d8qzPPGChJDnZNhxalpAzL1d dYKwvYEVnqweoLSQlq/lAw4v7fGTjSXq/lxp1D3lkQIDyEaye3Kk8k0Tcuc5liOIuFyd M66w== X-Gm-Message-State: AOAM530SvqcehEL8pUOIw+24DbrPrPQY85/p6cm215yuVjG2kYz35s2W lb9Bx5zcB8EovgAm5nolqGeTMMVQup4= X-Received: by 2002:a05:6214:29c1:: with SMTP id gh1mr45879737qvb.108.1641313729479; Tue, 04 Jan 2022 08:28:49 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id v5sm32623567qkp.126.2022.01.04.08.28.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 08:28:49 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 4 Jan 2022 11:26:32 -0500 Message-Id: <20220104162632.52767-5-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 5/5] fate/jpegxl: add JPEG XL demux and parse FATE test 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 8PJlP/d+laSG Add a fate test for the JPEG XL parser in libavcodec and its image2 wrapper inside libavformat. --- tests/fate/image.mak | 10 ++++++++++ tests/ref/fate/jxl-parse-codestream | 6 ++++++ tests/ref/fate/jxl-parse-container | 6 ++++++ 3 files changed, 22 insertions(+) create mode 100644 tests/ref/fate/jxl-parse-codestream create mode 100644 tests/ref/fate/jxl-parse-container diff --git a/tests/fate/image.mak b/tests/fate/image.mak index 573d398915..15b6145c58 100644 --- a/tests/fate/image.mak +++ b/tests/fate/image.mak @@ -357,6 +357,16 @@ FATE_JPEGLS-$(call DEMDEC, IMAGE2, JPEGLS) += $(FATE_JPEGLS) FATE_IMAGE += $(FATE_JPEGLS-yes) fate-jpegls: $(FATE_JPEGLS-yes) +FATE_JPEGXL += fate-jxl-parse-codestream +fate-jxl-parse-codestream: CMD = framecrc -i $(TARGET_SAMPLES)/jxl/belgium.jxl -c:v copy + +FATE_JPEGXL += fate-jxl-parse-container +fate-jxl-parse-container: CMD = framecrc -i $(TARGET_SAMPLES)/jxl/lenna-256.jxl -c:v copy + +FATE_JPEGXL-$(call DEMDEC, IMAGE2) += $(FATE_JPEGXL) +FATE_IMAGE += $(FATE_JPEGXL-yes) +fate-jxl: $(FATE_JPEGXL-yes) + FATE_IMAGE-$(call DEMDEC, IMAGE2, QDRAW) += fate-pict fate-pict: CMD = framecrc -i $(TARGET_SAMPLES)/quickdraw/TRU256.PCT -pix_fmt rgb24 diff --git a/tests/ref/fate/jxl-parse-codestream b/tests/ref/fate/jxl-parse-codestream new file mode 100644 index 0000000000..b2fe5035ac --- /dev/null +++ b/tests/ref/fate/jxl-parse-codestream @@ -0,0 +1,6 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: jpegxl +#dimensions 0: 768x512 +#sar 0: 0/1 +0, 0, 0, 1, 32, 0xa2930a20 diff --git a/tests/ref/fate/jxl-parse-container b/tests/ref/fate/jxl-parse-container new file mode 100644 index 0000000000..99233d612a --- /dev/null +++ b/tests/ref/fate/jxl-parse-container @@ -0,0 +1,6 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: jpegxl +#dimensions 0: 256x256 +#sar 0: 0/1 +0, 0, 0, 1, 8088, 0xbbfea9bd