From patchwork Fri Jan 7 09:01:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33149 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp209607iog; Fri, 7 Jan 2022 01:02:34 -0800 (PST) X-Google-Smtp-Source: ABdhPJySnYoNlVDrFuCZnoIUHdlxP4vObi5r6OsX8gdEjTA0osWS5vcd87n5468MaNypDdak9M4x X-Received: by 2002:a17:906:3e4b:: with SMTP id t11mr6023593eji.744.1641546154398; Fri, 07 Jan 2022 01:02:34 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641546154; cv=none; d=google.com; s=arc-20160816; b=GDgJIQOk6Jwn7tafbvmq3kLdz5sVhKAP29I4WsRUPzgQl10MVA9nt+/+TUewfMTKsd 90HW6ysZ41fEsXKJFCOAxYZnF60v7ET/Y3D9mWOjmF8wYDrSV57TzbQMwmA2XwWZvH3/ Rbv+6cKhZrUZB7jcjymSN97ZNxZmKwAVCSCfxgk2Gp9BvhIPg5ZjfkSC2GMaGrcKVrCl oXRQv63hQJ0VSnC+Z89DR1bnboqbAWnfdZjWOtu0OgQwf06VmqfRqApgvbXflPkr/Xn8 VGUlhmFqslj142/o7TSc8nXHl42VNJgM9xBo2+gE///sg3GnKGrHIdYQm7E1OTYIbuNB LGag== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=gBBvnmZhrh3+v6i3N5CVLTEJTs4J+kj2G9e8JlcVmhY=; b=d4vXLLXunjY0FD4MYs3ZlyA5JiUOssRq8p3ikQ2x8AEyU2wVW+O7rFDzG9KSV1oNev PFrzPay6Olq2MzpTidpVxjtKWZVWOzjwYZ8v+KQfU4aDD8RtfGhrUMQqLvIHzOkEE6LN e5Th0IoLTJ557qa1TcWEu+CdlCtXXDvBdB7Hsm/6CXJG6vq/YniwOUbmpyAMq1YuzaET ZfrCcpU0mBxm6tGyWhbjlE7gRYCqQMIo7NaPEM/PaWxQ+3yGjHoVY+/uJGHmPOsK861j EUl5sd2AK1fvn5L3rMfAte/gQXKtR/qptxaJFiho7AL5NAVkXvTgPLQB1wZrvDx5WiCA ld+Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=NJvDWPpp; 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 ne39si2370410ejc.102.2022.01.07.01.02.33; Fri, 07 Jan 2022 01:02:34 -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=NJvDWPpp; 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 4BA1168AB82; Fri, 7 Jan 2022 11:02:03 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f173.google.com (mail-qt1-f173.google.com [209.85.160.173]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9072068AAC1 for ; Fri, 7 Jan 2022 11:01:55 +0200 (EET) Received: by mail-qt1-f173.google.com with SMTP id v7so1648156qtw.13 for ; Fri, 07 Jan 2022 01:01:55 -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:in-reply-to:references :mime-version:content-transfer-encoding; bh=4uzGgYdzAHCTJgcEVrcVJGGsMO41zA8/nME8m9J83T4=; b=NJvDWPppi48IqxvdpWth8+OiHuPuBCMHvE1oOOAhuTBvQOaB0vA6LeaxcGl68/ur8+ lirWZYXx5OgC2/LIM+rMsWCY7CWEwbGXf0ZOpSMvcasGaGDTJq55ynqksXfB++MEmK9t HpPHn4xcBfNT3v41iD/FWLqtMigUntcdXZI30goyeBXYhli0V9l7XDxOYLSnp0RzON4g GOmM48YuvMsvZRtjfgSrnDNWE1zs+1ZhVrPZji2C7LLyl72AQ+PZRc703VMg1iTAwc9l IU9FMpjodNmMN2OBWG6nrVbQ1dxMBibP+C1FljBNA8Iu3hhmaMs7pnYCDBMsPn7KMvCC 1Pmg== 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=4uzGgYdzAHCTJgcEVrcVJGGsMO41zA8/nME8m9J83T4=; b=vHfH2OmWY434F2DndDMcfSmEL4Otr6bP1TKJyF/r2PGwzGbeD52XtrDOfUvINKadf+ EBnFkB2WL90nLISsFOUr4YvTeJJxUAfzig+uofh1/nARcbSZd/q149DTBNZKwtacY0L8 aMPKGmnKcbr2vHghrIYmD69iGaxQ7KZrn3GcojWZG7bsctS+EcYOvmhxmabIcdSoa+Wk FYl2PEA/bkVxL+yOdM7x1CzppVFMMgubIVICjNdmUF3kP0EOWNxfwO8DSbQJytnFQO4j WTpRjtdZOEShklF5yZi3v+HWrv49N9MYkd1LDAl1ZuwL8pQU78ugD3tDs/64h4e/kQHy vO7g== X-Gm-Message-State: AOAM5321bXkvd2lwmy+W1/t+/3VB3eX5jFUpYT4kTR+mNGzNx0yCdk7j JT1nvBk4Vf5f8hRRvjKg4PFsfLYPiWk= X-Received: by 2002:ac8:5796:: with SMTP id v22mr53432786qta.299.1641546112997; Fri, 07 Jan 2022 01:01:52 -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 u17sm3077071qki.2.2022.01.07.01.01.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 01:01:52 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Jan 2022 04:01:07 -0500 Message-Id: <20220107090111.243853-2-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220107090111.243853-1-leo.izen@gmail.com> References: <20220107090111.243853-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: FxNhoCSZbcK4 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 | 206 ++++++++++ libavcodec/jpegxl_parser.c | 809 +++++++++++++++++++++++++++++++++++++ libavcodec/parsers.c | 1 + libavcodec/version.h | 2 +- 8 files changed, 1030 insertions(+), 1 deletion(-) 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 cfc70a3eaf..423022e714 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..cbfb33f74c --- /dev/null +++ b/libavcodec/jpegxl.h @@ -0,0 +1,206 @@ +/* + * 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 or equal to 1 << 24, + * then subtract 1 << 24 and interpret it as a + * an enum JpegXLTransferFunction + */ + int have_gamma; + 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 */ + /* otherwise it will be NULL */ + 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..3355ca603e --- /dev/null +++ b/libavcodec/jpegxl_parser.c @@ -0,0 +1,809 @@ +/* + * 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 "codec_id.h" +#include "config.h" +#include "get_bits.h" +#include "jpegxl.h" +#include "parser.h" + +#if CONFIG_JPEGXL_PARSER + +typedef struct JpegXLParseContext { + ParseContext pc; + GetBitContext gb; + const uint8_t *buf; + 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_reset_pc(JpegXLParseContext *jxlr) +{ + memset(&jxlr->gb, 0, sizeof(GetBitContext)); + jxlr->bits_read = 0; + init_get_bits8(&jxlr->gb, jxlr->buf, jxlr->buflen); +} + +static void jpegxl_init_pc(JpegXLParseContext *jxlr, const uint8_t *buf, size_t buflen) +{ + memset(&jxlr->pc, 0, sizeof(ParseContext)); + memset(&jxlr->gb, 0, sizeof(GetBitContext)); + jxlr->buf = buf; + 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_bits64(&jxlr->gb, bitcount); + bits -= bitcount; + } + return get_bits64(&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->up4_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; +} + +static int jpegxl_parse_header(void *avctx, JpegXLParseContext *jxlr, JpegXLHeader **headerp, int level) +{ + uint64_t sig = jxl_bits(64); + jpegxl_reset_pc(jxlr); + if (sig == FF_JPEGXL_CONTAINER_SIGNATURE_LE) { + 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 > jxlr->buflen) + break; + } + } + return jpegxl_parse_codestream_header(avctx, jxlr, headerp, level); +} + +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, 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 & 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); + status = jpegxl_parse_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 a46fb05f1a..b5867ad041 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 59 -#define LIBAVCODEC_VERSION_MINOR 20 +#define LIBAVCODEC_VERSION_MINOR 21 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ From patchwork Fri Jan 7 09:01:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33147 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp209367iog; Fri, 7 Jan 2022 01:02:14 -0800 (PST) X-Google-Smtp-Source: ABdhPJyvp6k641em/Dsg0duyO6G7h5N2Yt699tGU+T2N1ZTFDRDLVC2m9D95VQKx1WCoMiMRbJew X-Received: by 2002:a50:9fc6:: with SMTP id c64mr8543735edf.260.1641546134300; Fri, 07 Jan 2022 01:02:14 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641546134; cv=none; d=google.com; s=arc-20160816; b=AScdzRhdFGBZQ9pCme1IfYE7TY9yWwA6AcmsiWAdF6x/oHjECS1a+nRXgTRfAA0vmv HQDo9AonHH/lpmDVgYvI/eSVgZ5E/RsPdIMHGgBGSrvrHcQYuUyJ8SyMhIo0CTeK4MuD tn0o0HCGcnYV0HYp+sKw3g5oH4QslWlqGQ4d7ZOrPWfJodTeVWaRGyjEHRt8hruUffGt hr0kZEGN1R4MqF3/p96IzEHQjMezRa7aAAcfa5/6xxU50j5ecimJuy/JmjIi0b5bOjYE i8yHSyMCLWYO31QKPo/EVmZFGjxwuBCgiEhJPOrS3ssB9J0nU3EgJMRP1OeFAN5dwTtI UUbA== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=RrmisbZm3MjXcEZfFNtv+pB/rc7IJlmt0eNEkDsr0mU=; b=zbW4D6UogSI5vvQzz78fhQig10uGIGkwrJYtnKY5qui7oAkhUotSH4fcfNxfhhHNTR n6dUjTE1/J+okYuJkfyl1sxXr02LBMk9kOOEKVXC8RlVRqRqsn3lDlaPsg3SMSk+Ap+7 HeoRqftYaxzsmTbrbNCDWu7xbcRP8M320KwNzah215ZlATmtz9dZ96G1WkQk+sdZfdjG ka4s6GUbO13ovB5k7GbRObGZreaex4WKEm/3hkLPZfWB1Wi67pewWdQ5qWnBtmXo8Npo NuK7sWx0dunbfvd14l864E9k8eZ2e6YDUvg8942lQ23fb61+vBZY0b9xxZFonWvF/HIC 90/w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="T/uews4x"; 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 j18si2810641edw.165.2022.01.07.01.02.10; Fri, 07 Jan 2022 01:02:14 -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="T/uews4x"; 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 4F44F68AB1B; Fri, 7 Jan 2022 11:02:01 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qk1-f182.google.com (mail-qk1-f182.google.com [209.85.222.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0BDC068AA6A for ; Fri, 7 Jan 2022 11:01:55 +0200 (EET) Received: by mail-qk1-f182.google.com with SMTP id m2so5321031qkd.8 for ; Fri, 07 Jan 2022 01:01:54 -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:in-reply-to:references :mime-version:content-transfer-encoding; bh=XI2jKw+xBNNeW/WLPCe6+y2XInocUAnScpKYCRuPiS4=; b=T/uews4xoah31OT6QV94CEV9x6Zos4VfKIsN6Y0MObHycbLe0XFiUwhCbZvLK2TNLO XJsnt7HmGcS3ouS7BFlXO9pRq0+ftQ2tHF0sN/b9+F2NpPcr0PbBhJLKavAeEF3c26c0 nPZI25eK1PNICRwvGHCqo7T3EnYuczTptH2STk9Pz9X0ZaYHnLQS1CAyVh1EX7HTiJL/ JnAvr/mJGGgAfih75SeI7nTRLPK2SNa4Sguk7s1bPEV2R+Wob16YqR0kkXOpiuYmzqiv gxenUCHF8gTD30n2o9Gj1t+XdggdMnYQpioRE58xOuNJFUqjvefVrRdmBwDsIauoGh00 L3QQ== 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=XI2jKw+xBNNeW/WLPCe6+y2XInocUAnScpKYCRuPiS4=; b=m9OMxgSaJJDck5NmViUDvO/r9kr4eWs0Na2tVDSXtGApxFfXVQOMrOaQLel4d+Eil5 qWrcPVuplWo/WTQKM7egKVGZHBp0lZ7HNViZFyiuiJ5J7PzJlDZtKhiE8CujaQkFKkYr YKNNAZO+QmbA9Z/mG2sb857+XU7Z7+hVwKY3J4vJwT68JCCvycMs3+amzdHiXEQoY/u0 AF+biIWCz3HLvqL3X+7HIkhIL9EdYWLpztsakUbb1DoRBlxPoGF8Jpck8xgSNp+0pJel v4s4l9oiLSA4JAMC2HLCrT64ffC0ldOizpFp1gKXPpZaCt84R4NjSj0piXL9hes5XkfX LD3Q== X-Gm-Message-State: AOAM531Y/tgUhDV7CwrNGLcHE5mzd9Tmjjn/tbCjO+l+GYrbI7Hk3x5k wPtD57q5ZPflY/6AuACqxOapqt+1Y0s= X-Received: by 2002:a37:8006:: with SMTP id b6mr4243727qkd.433.1641546113586; Fri, 07 Jan 2022 01:01:53 -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 u17sm3077071qki.2.2022.01.07.01.01.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 01:01:53 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Jan 2022 04:01:08 -0500 Message-Id: <20220107090111.243853-3-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220107090111.243853-1-leo.izen@gmail.com> References: <20220107090111.243853-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: TT/uH/WjBGDV 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 1225faf15a..e5eb8c5ba5 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 @@ -3334,6 +3336,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" @@ -6548,6 +6551,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 423022e714..d538c3a4d1 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1055,6 +1055,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 Fri Jan 7 09:01:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33148 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp209476iog; Fri, 7 Jan 2022 01:02:22 -0800 (PST) X-Google-Smtp-Source: ABdhPJyPSYHSEERpBHpLKcWjmGqjZI//RrPyYGKra5/ah1eIBw0w3R0T2jtx4iIF0c2ZueGhDSXU X-Received: by 2002:a05:6402:424f:: with SMTP id g15mr62757508edb.316.1641546142638; Fri, 07 Jan 2022 01:02:22 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641546142; cv=none; d=google.com; s=arc-20160816; b=BnWJxynhn3n8i+bNg9VVpXH4VzdSGv8RTIZwq3p+8gBm9h0Y4zena49FhvcUsWjmya KkBQlEYlYSy9TARrXLyTNe2zdRku4vSZfQKLs400J/ONqjWDW6L8fEBLmaPrJ41+D9YJ D31k6viKlynnvIGvRx2YPdyDBrBF7TpkwT82NpWM4NuD3v/uB4kg9MaFTVUu2o2yHtzn 3xXDMFblK2rPoAFjPROi6k+1D8L7bPTZqI3CBMHZ0TdRF7h8N6DGsQwVC74CtfKkvj6D k4QSUTmgIeHs0HuEGTKPbP8LvlG51kjU7YCDtkEMuK1ZAeO0Vl2Mo19FfV3+M9CfqcYI bvDA== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=ba4C1KEn4K/ykYTWpqgUO1gE/vtXR0A4HGZqQMtioBE=; b=A3hStWS7V5XzkXzrc+8HwqcV7BRI0HKQS7a1LSBTsHiDonA94BpJRvBoqf0rKPBFnx Gv5K2lRBvlLcrTi+ZRWBazD10CZUDO0WdnFwPkauai2BJL/BRUVK+DyAenKUnKOQXvVn b57Q6Qhxbxda7fZcLNS1f6nBjZ5MDM+O7i48Rl4gs77GgsHqTzqXmAlltX5EFfgOVCMs Q710DGxXopeF5lIaWQnuDYgYSdba9DWMgVsguzS7eZJBjWq2BcXpc0HvFuyYuNxalFG6 jzzi9jqBvlLHUoS/IrmqBJkJs9xW/K/SAVCblFFk+5iUIEH6JuaS1ynCmnXuo5+cO3T8 hXXg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=eU7JsG8x; 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 jg23si2571865ejc.156.2022.01.07.01.02.22; Fri, 07 Jan 2022 01:02:22 -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=eU7JsG8x; 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 5368168AC14; Fri, 7 Jan 2022 11:02:02 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f170.google.com (mail-qt1-f170.google.com [209.85.160.170]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7E95768AA6A for ; Fri, 7 Jan 2022 11:01:55 +0200 (EET) Received: by mail-qt1-f170.google.com with SMTP id p19so4926533qtw.12 for ; Fri, 07 Jan 2022 01:01:55 -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:in-reply-to:references :mime-version:content-transfer-encoding; bh=RDZyL53bZlVU2T/yMFTEYi5iFIpT4CWQocTvm55Vso0=; b=eU7JsG8xWPVBLnOB4Ay9rqVRclRg5gKBYhcDz1SYEoTERk/T2cN1IJdpU1JADIz9lg 7uD9Wxz6qCoW5ImAofTchZfs3obS83XbI1Ha4RuYecOyOcaBZsijykrDo6pgfGdoaqFC TEiFQHTyXG+Azn4GktVz0ZQQlruWFcssn2AlINHRtfinRXr+DXXhKDgrBhn7/0HbMHM6 zFWxONaMN8EDAg74EqnSLAuq1+Dx6jHOQCW4HyEHyXSE4/S2DqnLBeh/Ple5Qs4VKzCC 6AJIuhlCZkyQIlwFWo6Z2iS5oU8kDsH0Z3bmg4y4JHZNFvIzYn8ZUt5lBYe/l8bZnFpu d33g== 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=RDZyL53bZlVU2T/yMFTEYi5iFIpT4CWQocTvm55Vso0=; b=V8tdEL75Q8w60LaBJTltgYRO0LsDIQ8yUY1hDlEe6WXQN0euRz0OnNlBTmIKXe7r4a inSg8dllUA9aGfkP0tuTeRvbVByzfnlulff70VOG3Yv11P82gzmWX55blxGgGQIOIXjB VMHI6MK0Ou85VkmXgns5OJLaEIgiQCT8AY27mElFMiUl1/qtY0myPrzhMcKdOdc/KnUR BKIedBpIRVapbVft0kLjD7FOrsQKaLYsToJHqU2/nmyR0QjeNANzAjUCfgqUqSB28xCb o4GbwVw+UuyjOWl8e8yA3gm2CJeIIugfimhOaTp/8Tj1vcGTsNFLoHjHlroALH2YLDVk qXAQ== X-Gm-Message-State: AOAM531lLMZKw7biHBAshjjWlmeRxl+2CMWdb2m/cUZWM16sln1tMKO6 8E3o9s+PHRTH+aYvw1VlgKLBYVlCxK8= X-Received: by 2002:ac8:5c54:: with SMTP id j20mr55788364qtj.438.1641546114149; Fri, 07 Jan 2022 01:01:54 -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 u17sm3077071qki.2.2022.01.07.01.01.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 01:01:53 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Jan 2022 04:01:09 -0500 Message-Id: <20220107090111.243853-4-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220107090111.243853-1-leo.izen@gmail.com> References: <20220107090111.243853-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: 3qXxBe5QTT94 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 e5eb8c5ba5..21ae5e9e1e 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] @@ -3337,6 +3337,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 d538c3a4d1..844d7c3ade 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1056,6 +1056,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 Fri Jan 7 09:01:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33150 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp209748iog; Fri, 7 Jan 2022 01:02:46 -0800 (PST) X-Google-Smtp-Source: ABdhPJzrEjDNp/PFuWGXr2NQJ+bs1UY/YSpt/R2y62uXyzfbJ/3RWRpQb5vtRIqr3VZYnOhYf4As X-Received: by 2002:a50:ef11:: with SMTP id m17mr8863130eds.77.1641546166110; Fri, 07 Jan 2022 01:02:46 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641546166; cv=none; d=google.com; s=arc-20160816; b=qCvw3xuiEGm38LuU7WtKdkKMzEeIK9359s3RH+EVkOvRAzmLoUiSk9qm1XDyM1RpY5 VxesMWwxEXbCjGKvj5zEYhxIjnx9QAfgoNqGXjlhVcVp0U5bmbYTvTuPlzBVUSFNs55W xEmNCpeBP/abVFZvbbT5Kh9/lTspf55wUrRij6oO5EojFtpKHK/No3MRbDN1laMx5h1f wtrLnoDQ0we9o3xMGhGxjkQS4gYB/aW50Q7LRVHXyTl7udovydJaaZ+gROUFmJITqmNO l9JaEnhyW05Z+NygoQGWSeMXrirKVLKe0QGjgrfNfBUkCWboOz1K/bb0V8c5loqMK0MZ OWoQ== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=CQSxiofB9UwuarmyxEcwi4KOZTscUvBcI/TIqoFSrjI=; b=DVNzmLZXzYeA2sEy9VCEMXSXeaa5IdB82voaDfKbHqriNxPUKlOkqBrCLm8fC7Jy4X MmMm9E5q+A796V/Ei/FJajJ6+tz6arMaxY6Nk9Pz1wSDFZb2EIHwQ3lN1TFnahwfwZvb F6tVRtaWJb8Pg9AKD8YyUmqE8ZhoDRSCuhZnlnacz3XNObuvD6MOMqo3s9Ljolj4XfW5 5AZkeCSmfvYKAFvqRrY1vs3KuYexpcS92Zc6YFjIkA7OMvN3z8eau0ChhKEXAxjNtGHv JvnC1daka0+iwjM3sK3lH/5/r1CN6vcVH0VZ0hT62C/C20lYH4fdRTSOD1EY7uhSYqUD Kd8w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=W9yaHOY2; 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 eg50si1019282edb.339.2022.01.07.01.02.45; Fri, 07 Jan 2022 01:02: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=W9yaHOY2; 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 5A22868AE72; Fri, 7 Jan 2022 11:02:04 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qk1-f176.google.com (mail-qk1-f176.google.com [209.85.222.176]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E11BD68A927 for ; Fri, 7 Jan 2022 11:01:55 +0200 (EET) Received: by mail-qk1-f176.google.com with SMTP id f138so5315603qke.10 for ; Fri, 07 Jan 2022 01:01:55 -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:in-reply-to:references :mime-version:content-transfer-encoding; bh=rEApeLgCxRFtfvakiFX4XMaMIgCBXx3pLDyrgNBQ/xw=; b=W9yaHOY2eO63xMIZaG4XX2twhQYKqZ/6DYUxQMvn0vcgLyZsmEZhrEeT0L9gbRdC1p xHNiDfYAca3yJHY80qNrOySMZDxAeM1OOXa6PKNu7ztAFPlkP9BQMCIZ8VYZCjwA+6Bs nIFHu4YOniOU271PDDRsqbgimSYsakRYM02dzFVsxCHitSV+tKPsh1Goc76z8jId44aA WWYopTrbcaJHQHop6B3FdsVntvwUtVhfpSL9EuVb+n4T/1yh4FwrkkNuJ0VfE7Z7k5/r BPCJGH0HdGUBJVsCyNxznjtKK9a5PJIoRWu+6I5Qe5VZXEgYmp0SHHPQrF9dEteKNuJ+ ZF8Q== 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=rEApeLgCxRFtfvakiFX4XMaMIgCBXx3pLDyrgNBQ/xw=; b=miXK5Urd3eVPydAV5+f+qmtuqgAIzJPYBesJWtzLsSkoyCq5Dakd3Z1TKjC7NitwBS 2gGkBzScohn5C2S3dixwYut534ATIsBr8NIlWF1z0jmp/i40OTW9b2z5IDS1P4udT9zA HPKjleLvOv4+Lv7cPRsaTjYC5XP+9LfusdcIXCjX2x0nsb30m1lG6BtmHvAibzEZMoYH keX9tggAoviVJ7jG3CMvqhQvkeSPBpcHYpxEQuGs5cUPR5AoH7tlmJBkXFZZpb//YUL+ Yt6KNT8StDKXoax3kF6s6aM60fn/7x2RSoGzxC/s5Uow+ahc2Y9TbjZoKs3lHfsckUXv Uh8Q== X-Gm-Message-State: AOAM5306gGMvDHYs5ZpE+0IXBcENqaTXNIiIL4bGei0nefT0rKIisHOt hMmdYD2ylNp+xz+D9TczgbvKSI7g4wk= X-Received: by 2002:a37:5545:: with SMTP id j66mr44200715qkb.705.1641546114765; Fri, 07 Jan 2022 01:01:54 -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 u17sm3077071qki.2.2022.01.07.01.01.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 01:01:54 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Jan 2022 04:01:10 -0500 Message-Id: <20220107090111.243853-5-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220107090111.243853-1-leo.izen@gmail.com> References: <20220107090111.243853-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: TOYCr23NzinK 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 d066a7745b..24e0dd3389 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -508,6 +508,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 e401cd39b5..226495cf1f 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -7431,6 +7431,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 77f4b46712..9083bb1a29 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 17 +#define LIBAVFORMAT_VERSION_MINOR 18 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ From patchwork Fri Jan 7 09:01:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 33151 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp209874iog; Fri, 7 Jan 2022 01:02:56 -0800 (PST) X-Google-Smtp-Source: ABdhPJzqIdVGmILqzskQvduq0byOafinvrCF0MflM03WAh4wxN1y3N7DVLETsyeKVeRpcO4jvbHL X-Received: by 2002:a17:906:c156:: with SMTP id dp22mr48011337ejc.283.1641546176644; Fri, 07 Jan 2022 01:02:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641546176; cv=none; d=google.com; s=arc-20160816; b=WGcTsV6oqb6cpCxRcworAo26ZJbStkBj8bpn1K6Sbf86QD+llfGhJRVuH26mWnvq66 wz/ffmLdpfrWZlFQWO7/0XidU4gLlccKJiGVgaCThyMIPXacHYpKOF5GMCHmkCjQAm4M cT6P5N4Dgj6B16fYPvAPwKCpMVNgMuhmZDSxlZzfXjoYyWAjpjQUu75n3fjf6903Acv+ FfBTSWgn/LHNnUarPiGnNctc7GONu3Vu1vNbr1uLWKBUoeHaxemxajVhS0Vuda8jgdCf AigWdyBuz6nJoexnu1bCAjq/tj/LU8WHVdmDjSPOKLiD3ExdZxRo2m9lIFnggbxqyFRR lCNw== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=PkCkw2VGhc5aSZXoijBD9fUVA9lhNdS/7ty7nc26T4w=; b=jsg3SieK2eJJhCqJGtRTTOelSaU7Rc5Ucm1ka3I1XUUeu0MzM3NupSMmv+pqki4jxZ mVxXsx7MlLy5AWPbFUzFYsp+TpicXHPb4RsB0Kac0OaEYwgOrzG2LQvG2EL/i2ltOl1w otCeYkb9MeVVIN10YRer3W1SiW4yaq9UiOOUTPIQVpgQaOGorJg1G/Dx0Ga3huSut55b pOp02TcdydVre+JRrhB8yxG3Z+nOW72VLOaG3R0KPdIeN80pIorW5rp3jjupD0ch5s45 yPXjZTx0vj6aJgTJadpTU8+ZO6/joySONKiGGz6r21KkyGVNvdY6Wxuj5dkvR/X/dERU d2Zw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=bffd8MtA; 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 q20si2602473edd.566.2022.01.07.01.02.56; Fri, 07 Jan 2022 01:02:56 -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=bffd8MtA; 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 5280168AEC1; Fri, 7 Jan 2022 11:02:05 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5D09E68AAC1 for ; Fri, 7 Jan 2022 11:01:56 +0200 (EET) Received: by mail-qv1-f50.google.com with SMTP id kc16so4943625qvb.3 for ; Fri, 07 Jan 2022 01:01:56 -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:in-reply-to:references :mime-version:content-transfer-encoding; bh=NlulufVn30kk4HsEIjKVJ923MgLx0RB+ZVM6qhEn0Zw=; b=bffd8MtAeqa26HNF2qzZmQsNsbXxn8yjV29UXnMdNbYRFR4UYFCd0kAJxD87L113yx 3RoNTPZrnOTDoKMVwcoV1tQjIksOzbX0TSYcaMn/Keg3Iw48BlyWxDqDvb2lAfUl8le1 Q5JFIOkfEHx5Z+uX91q8T9za5KHx7yi8GGuJ8jH9lLjMFXZLHgw0TKGo6nXWJxqPKmOV KtESRMzeG7Qx9ZIzMVU2onSoGQ5gbTxKilaplx5c/ZjrR9y1gkzysPT/eQ/O6Pcpc1R6 WIgzevUkHqC+w9SbMkc181kH/9c4zLqL8vgMQllF0vSwZnVTln31RtPKM7SH1HIxgUwb wCLg== 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=NlulufVn30kk4HsEIjKVJ923MgLx0RB+ZVM6qhEn0Zw=; b=nrsWp4TOR1fg9M1jaNTK5bPjhr0Ctcp0wp3qjU6gme3jkO4lhxbiFzqyPtuzzcpxcP c0GYYU5DijWHsNYoP0AGZZscHPfVWohuCRI8sZA3PDpbKkV32P1C7S2Zgs4tPWG25yoL xcHg2r/8qoV02X9auP0jiYE+q/afgr3VwXFZ8TmathFk5PRMUbDndwR4mIFO4uSl/ulc 0UPhn2kn9JB/adQmLxQusar2eLb7izQMMLw7YMqtbaaiTTSsFuERo0i6C9L/iQF0+azu V4CWfb1FUKIUkaSdJMY9YIKIDlzQMQlq6dJmQGJQkIN4qtmRnCSivuaPgGsXKu9DRQfO 8NAw== X-Gm-Message-State: AOAM531W/dJ1teQ87/Lbk77bO3UyPksjKHDLtiZA8zDyU8dHjCHEytXP QqPOA4Sqlgzc7SiVejvgmKJ4Lv7o+b4= X-Received: by 2002:a05:6214:1c4f:: with SMTP id if15mr56754656qvb.60.1641546115275; Fri, 07 Jan 2022 01:01:55 -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 u17sm3077071qki.2.2022.01.07.01.01.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Jan 2022 01:01:55 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Fri, 7 Jan 2022 04:01:11 -0500 Message-Id: <20220107090111.243853-6-leo.izen@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220107090111.243853-1-leo.izen@gmail.com> References: <20220107090111.243853-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: Szwa4F4s85tM 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