From patchwork Sun Apr 17 13:22:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 35339 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:671c:b0:7c:62c8:b2d1 with SMTP id q28csp1136452pzh; Sun, 17 Apr 2022 06:23:38 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwumyzkxbVQSvL7ZFmqDWUGRDv6Tn+zxApY7Lll0JbuCTb0uGGUPl7DNV362eA+v6QQibrB X-Received: by 2002:a17:907:86a3:b0:6ec:aaa:7f82 with SMTP id qa35-20020a17090786a300b006ec0aaa7f82mr5651063ejc.651.1650201818293; Sun, 17 Apr 2022 06:23:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1650201818; cv=none; d=google.com; s=arc-20160816; b=Ln8N1x9htQqq1GiCUDYKEwGESmGAB3rq+cSm8BWIINhDlP4DpilMvCalyMYfw6x9la D0solYRLzMC1dloY5RjAO4kRePGZRISuFfUHmKC8jRH2r4UojuJWkdZz63Q5UrSTHGEV LwDVM3H1Jm0HdIQf3vbE6sr/cgOinGZKGEjZCbs/X2OzJmr04DNKgecWtTga6JQ8rZkJ 5Mh4/RlP29wS3h6bTmk2WMCcU7LedKgORrE6oA2ft+PUSyJvHJXBrjcKich1Nq/dE6OG kOxZdPWicmfhpcUrnwRrssg6ZbI0sFFGa0EEj3+tsmhBB9ZsWJfAzSsV4Fo1PoIY2EIM 29ZA== 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=coN//dLeFhUzxqVU6Rf6MkUEGnCMYE7EceDUp8CscwE=; b=fn9F7dKM4Z0t3e8k7HoK4Y2fTF0fgeJ/YlbbSip3Tj9lWUU4ILkZpJZ5YidwchGzNl dj8FmDkaX7WlAI0+A+WYhAa/MhYHUDr0FAUJZL7zO5e28UGBc82LiHd0w0eO4Si7f7GM Y3W18crIluqfG51WPNV6cBqhKHgPrZP0IWyP91MRnbugBNS2yWKImaFjpdSw08FACLWX GxeBzBZolcr7TIZ2Z6OmyzA6TqMgoQeDIQCnqMUUhsfQ/JRky8zw5MKNG1De6LFI8SBO v+UFHYbOkHGT6mTU235KQ+JNgGx24w/91N3icZQYkH54gMe8TlX7DMB7YUdYcnNHgFzF e12Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=kpu9l1Vb; 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 d18-20020a170906305200b006df76385ebasi3252437ejd.858.2022.04.17.06.23.37; Sun, 17 Apr 2022 06:23:38 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=kpu9l1Vb; 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 F0D8F68B300; Sun, 17 Apr 2022 16:22:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 609DA68B2C7 for ; Sun, 17 Apr 2022 16:22:50 +0300 (EEST) Received: by mail-qv1-f48.google.com with SMTP id a10so9386306qvm.8 for ; Sun, 17 Apr 2022 06:22:50 -0700 (PDT) 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=DyXZlKBLlWnKsgCqQx+CM+QRiFG149oIefMgq9kg20A=; b=kpu9l1Vb7EAnix1gIEDq69eVxwjAsWGCuILn2kNwn4FycVs3iJ3E0sXtXR64XGZ0Q2 08zFfqjOh/FPhTvQFGlrsGGzU8+qBL+Euex50ZeC8lGa7MFJZ/UKTe8XfPT7XZSOoMfp UlAl/TXDRHC4peQbDMNP6RCLxJcj7skaQanKPSnPTnrk7kstpEuftWRUb71HMI1jdETm q4Tc2mOHTANz8ZY90cht9BLiZYQE2jBAg2VFPdDiu7bQrNmv6w1DTwuzyIzUlb2xD71y g1QbIEyO8Y7CUkBBClUWENgFFisHF7HPh4GvqSua1WiWCUaD4CWEcvKwxRU4OfEgNPc8 Ce3Q== 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=DyXZlKBLlWnKsgCqQx+CM+QRiFG149oIefMgq9kg20A=; b=L4gYzosX4yjurTdbXA4TLahSVJdBLWAoPSZHO7B280f2nJs8rZ9j1duuRKwEWfdPAJ OIFrtoY/SmIYk9WOIw1Yw4+YBnQ06YSxLG+F800m3MsDEFP6g/EkiYHR3MdqJcjttgaA HtolKM4u/b7Wz3gdqwHqTY7pC2Qhnrj64jP5sFqOl8HqOAzrOWMsyC39yxo5RcY8USig ZMDA61CA85ZhgNlNACRIXAT6Z77573ZjShDaoj9/AsVNYQx26OilIHQ0vffHmLI0GOgR ncZxp4Wz3AmxCT35bjJ0K13zj/Ssf6CUlPMQK90MJfbh+n8oQ4YrYdcU4UoPxvRM7G+x zHEg== X-Gm-Message-State: AOAM532L1oqhb2qrmmlIQwJtSOeqAOwe4U5Ey8ss0sXNlL1UXEhEWUZA sng5CQB7XJ5vRMSvBBkJnPffCN7uRCs= X-Received: by 2002:a05:6214:500c:b0:435:6b7d:5bc3 with SMTP id jo12-20020a056214500c00b004356b7d5bc3mr5010791qvb.92.1650201768970; Sun, 17 Apr 2022 06:22:48 -0700 (PDT) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id s195-20020a37a9cc000000b0069ca29ab6f4sm3903381qke.26.2022.04.17.06.22.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 Apr 2022 06:22:48 -0700 (PDT) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Sun, 17 Apr 2022 09:22:36 -0400 Message-Id: <20220417132236.200239-5-leo.izen@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220417132236.200239-1-leo.izen@gmail.com> References: <20220417132236.200239-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v15 4/4] 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: krFdRiyXvLio This commit adds support to libavformat for muxing and demuxing Jpeg XL images as image2 streams. --- MAINTAINERS | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/img2.c | 1 + libavformat/img2dec.c | 20 ++ libavformat/img2enc.c | 6 +- libavformat/jpegxl_probe.c | 393 +++++++++++++++++++++++++++++++++++++ libavformat/jpegxl_probe.h | 32 +++ libavformat/mov.c | 1 + 9 files changed, 453 insertions(+), 3 deletions(-) create mode 100644 libavformat/jpegxl_probe.c create mode 100644 libavformat/jpegxl_probe.h diff --git a/MAINTAINERS b/MAINTAINERS index faea84ebf1..46723972dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -439,6 +439,7 @@ Muxers/Demuxers: ipmovie.c Mike Melanson ircam* Paul B Mahol iss.c Stefan Gehrer + jpegxl_probe.* Leo Izen jvdec.c Peter Ross kvag.c Zane van Iperen libmodplug.c Clément Bœsch diff --git a/libavformat/Makefile b/libavformat/Makefile index e3233fd7ac..f16634a418 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -272,6 +272,7 @@ OBJS-$(CONFIG_IMAGE_GIF_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_J2K_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_JPEG_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_JPEGLS_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += img2dec.o img2.o jpegxl_probe.o OBJS-$(CONFIG_IMAGE_PAM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_PBM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_PCX_PIPE_DEMUXER) += img2dec.o img2.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 7c1d0ac38f..63876c468f 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -510,6 +510,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 fe2ca7bfff..566ef873ca 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -88,6 +88,7 @@ const IdStrMap ff_img_tags[] = { { AV_CODEC_ID_GEM, "ximg" }, { AV_CODEC_ID_GEM, "timg" }, { AV_CODEC_ID_VBN, "vbn" }, + { AV_CODEC_ID_JPEGXL, "jxl" }, { AV_CODEC_ID_NONE, NULL } }; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 551b9d508e..5f9d1f094f 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -36,6 +36,7 @@ #include "avio_internal.h" #include "internal.h" #include "img2.h" +#include "jpegxl_probe.h" #include "libavcodec/mjpeg.h" #include "libavcodec/vbn.h" #include "libavcodec/xwd.h" @@ -837,6 +838,24 @@ 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 CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER + if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size) >= 0) + return AVPROBE_SCORE_MAX - 2; +#endif + return 0; +} + static int pcx_probe(const AVProbeData *p) { const uint8_t *b = p->buf; @@ -1176,6 +1195,7 @@ IMAGEAUTO_DEMUXER(gif, GIF) IMAGEAUTO_DEMUXER_EXT(j2k, JPEG2000, J2K) IMAGEAUTO_DEMUXER_EXT(jpeg, MJPEG, JPEG) IMAGEAUTO_DEMUXER(jpegls, JPEGLS) +IMAGEAUTO_DEMUXER(jpegxl, JPEGXL) IMAGEAUTO_DEMUXER(pam, PAM) IMAGEAUTO_DEMUXER(pbm, PBM) IMAGEAUTO_DEMUXER(pcx, PCX) diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index ae351963d9..5ed97bb833 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -263,9 +263,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,vbn,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,vbn,xbm,xface,pix,y", .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, diff --git a/libavformat/jpegxl_probe.c b/libavformat/jpegxl_probe.c new file mode 100644 index 0000000000..924b529ad5 --- /dev/null +++ b/libavformat/jpegxl_probe.c @@ -0,0 +1,393 @@ +/* + * Jpeg XL header verification + * Copyright (c) 2022 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 + */ + +#include "jpegxl_probe.h" + +#define BITSTREAM_READER_LE +#include "libavcodec/get_bits.h" + +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, +}; + +#define jxl_bits(n) get_bits_long(gb, (n)) +#define jxl_bits_skip(n) skip_bits_long(gb, (n)) +#define jxl_u32(c0, c1, c2, c3, u0, u1, u2, u3) jpegxl_u32(gb, \ + (const uint32_t[]){c0, c1, c2, c3}, (const uint32_t[]){u0, u1, u2, u3}) +#define jxl_u64() jpegxl_u64(gb) +#define jxl_enum() jxl_u32(0, 1, 2, 18, 0, 0, 4, 6) + +/* read a U32(c_i + u(u_i)) */ +static uint32_t jpegxl_u32(GetBitContext *gb, + const uint32_t constants[4], const uint32_t ubits[4]) +{ + uint32_t ret, choice = jxl_bits(2); + + ret = constants[choice]; + if (ubits[choice]) + ret += jxl_bits(ubits[choice]); + + return ret; +} + +/* read a U64() */ +static uint64_t jpegxl_u64(GetBitContext *gb) +{ + 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 uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio) +{ + uint64_t height64 = height; /* avoid integer overflow */ + switch (ratio) { + case 1: + return height; + case 2: + return (uint32_t)((height64 * 12) / 10); + case 3: + return (uint32_t)((height64 * 4) / 3); + case 4: + return (uint32_t)((height64 * 3) / 2); + case 5: + return (uint32_t)((height64 * 16) / 9); + case 6: + return (uint32_t)((height64 * 5) / 4); + case 7: + return (uint32_t)(height64 * 2); + default: + break; + } + + return 0; /* manual width */ +} + +/** + * validate a Jpeg XL Size Header + * @return >= 0 upon valid size, < 0 upon invalid size found + */ +static int jpegxl_read_size_header(GetBitContext *gb) +{ + uint32_t width, height; + + if (jxl_bits(1)) { + /* small size header */ + height = (jxl_bits(5) + 1) << 3; + width = jpegxl_width_from_ratio(height, jxl_bits(3)); + if (!width) + width = (jxl_bits(5) + 1) << 3; + } else { + /* large size header */ + height = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30); + width = jpegxl_width_from_ratio(height, jxl_bits(3)); + if (!width) + width = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30); + } + if (width > (1 << 18) || height > (1 << 18) + || (width >> 4) * (height >> 4) > (1 << 20)) + return -1; + + return 0; +} + +/** + * validate a Jpeg XL Preview Header + * @return >= 0 upon valid size, < 0 upon invalid size found + */ +static int jpegxl_read_preview_header(GetBitContext *gb) +{ + uint32_t width, height; + + if (jxl_bits(1)) { + /* coded height and width divided by eight */ + height = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3; + width = jpegxl_width_from_ratio(height, jxl_bits(3)); + if (!width) + width = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3; + } else { + /* full height and width coded */ + height = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12); + width = jpegxl_width_from_ratio(height, jxl_bits(3)); + if (!width) + width = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12); + } + if (width > 4096 || height > 4096) + return -1; + + return 0; +} + +/** + * skip a Jpeg XL BitDepth Header. These cannot be invalid. + */ +static void jpegxl_skip_bit_depth(GetBitContext *gb) +{ + if (jxl_bits(1)) { + /* float samples */ + jxl_u32(32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */ + jxl_bits_skip(4); /* exponent */ + } else { + /* integer samples */ + jxl_u32(8, 10, 12, 1, 0, 0, 0, 6); + } +} + +/** + * validate a Jpeg XL Extra Channel Info bundle + * @return >= 0 upon valid, < 0 upon invalid + */ +static int jpegxl_read_extra_channel_info(GetBitContext *gb) +{ + int all_default = jxl_bits(1); + uint32_t type, name_len = 0; + + if (!all_default) { + type = jxl_enum(); + if (type > 63) + return -1; /* enum types cannot be 64+ */ + if (type == FF_JPEGXL_CT_BLACK) + return -1; + jpegxl_skip_bit_depth(gb); + jxl_u32(0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */ + /* max of name_len is 1071 = 48 + 2^10 - 1 */ + name_len = jxl_u32(0, 0, 16, 48, 0, 4, 5, 10); + } else { + type = FF_JPEGXL_CT_ALPHA; + } + + /* skip over the name */ + jxl_bits_skip(8 * name_len); + + if (!all_default && type == FF_JPEGXL_CT_ALPHA) + jxl_bits_skip(1); + + if (type == FF_JPEGXL_CT_SPOT_COLOR) + jxl_bits_skip(16 * 4); + + if (type == FF_JPEGXL_CT_CFA) + jxl_u32(1, 0, 3, 19, 0, 2, 4, 8); + + return 0; +} + +/* verify that a codestream header is valid */ +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen) +{ + GetBitContext gbi, *gb = &gbi; + int all_default, extra_fields = 0; + int xyb_encoded = 1, have_icc_profile = 0; + uint32_t num_extra_channels; + uint64_t extensions; + + init_get_bits8(gb, buf, buflen); + + if (jxl_bits(16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) + return -1; + + if (jpegxl_read_size_header(gb) < 0) + return -1; + + all_default = jxl_bits(1); + if (!all_default) + extra_fields = jxl_bits(1); + + if (extra_fields) { + jxl_bits_skip(3); /* orientation */ + + /* + * intrinstic size + * any size header here is valid, but as it + * is variable length we have to read it + */ + if (jxl_bits(1)) + jpegxl_read_size_header(gb); + + /* preview header */ + if (jxl_bits(1)) { + if (jpegxl_read_preview_header(gb) < 0) + return -1; + } + + /* animation header */ + if (jxl_bits(1)) { + jxl_u32(100, 1000, 1, 1, 0, 0, 10, 30); + jxl_u32(1, 1001, 1, 1, 0, 0, 8, 10); + jxl_u32(0, 0, 0, 0, 0, 3, 16, 32); + jxl_bits_skip(1); + } + } + + if (!all_default) { + jpegxl_skip_bit_depth(gb); + + /* modular_16bit_buffers must equal 1 */ + if (!jxl_bits(1)) + return -1; + + num_extra_channels = jxl_u32(0, 1, 2, 1, 0, 0, 4, 12); + if (num_extra_channels > 4) + return -1; + for (uint32_t i = 0; i < num_extra_channels; i++) { + if (jpegxl_read_extra_channel_info(gb) < 0) + return -1; + } + + xyb_encoded = jxl_bits(1); + + /* color encoding bundle */ + if (!jxl_bits(1)) { + uint32_t color_space; + have_icc_profile = jxl_bits(1); + color_space = jxl_enum(); + if (color_space > 63) + return -1; + + if (!have_icc_profile) { + if (color_space != FF_JPEGXL_CS_XYB) { + uint32_t white_point = jxl_enum(); + if (white_point > 63) + return -1; + if (white_point == FF_JPEGXL_WP_CUSTOM) { + /* ux and uy values */ + jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); + jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); + } + if (color_space != FF_JPEGXL_CS_GRAY) { + /* primaries */ + uint32_t primaries = jxl_enum(); + if (primaries > 63) + return -1; + if (primaries == FF_JPEGXL_PR_CUSTOM) { + /* ux/uy values for r,g,b */ + for (int i = 0; i < 6; i++) + jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21); + } + } + } + + /* transfer characteristics */ + if (jxl_bits(1)) { + /* gamma */ + jxl_bits_skip(24); + } else { + /* transfer function */ + if (jxl_enum() > 63) + return -1; + } + + /* rendering intent */ + if (jxl_enum() > 63) + return -1; + } + } + + /* tone mapping bundle */ + if (extra_fields && !jxl_bits(1)) + jxl_bits_skip(16 + 16 + 1 + 16); + + extensions = jxl_u64(); + if (extensions) { + for (int i = 0; i < 64; i++) { + if (extensions & (UINT64_C(1) << i)) + jxl_u64(); + } + } + } + + /* default transform */ + if (!jxl_bits(1)) { + /* opsin inverse matrix */ + if (xyb_encoded && !jxl_bits(1)) + jxl_bits_skip(16 * 16); + /* cw_mask and default weights */ + if (jxl_bits(1)) + jxl_bits_skip(16 * 15); + if (jxl_bits(1)) + jxl_bits_skip(16 * 55); + if (jxl_bits(1)) + jxl_bits_skip(16 * 210); + } + + if (!have_icc_profile) { + int bits_remaining = 7 - (get_bits_count(gb) - 1) % 8; + if (bits_remaining && jxl_bits(bits_remaining)) + return -1; + } + + if (get_bits_left(gb) < 0) + return -1; + + return 0; +} diff --git a/libavformat/jpegxl_probe.h b/libavformat/jpegxl_probe.h new file mode 100644 index 0000000000..2960e81e11 --- /dev/null +++ b/libavformat/jpegxl_probe.h @@ -0,0 +1,32 @@ +/* + * Jpeg XL header verification + * Copyright (c) 2022 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 + */ + +#ifndef AVFORMAT_JPEGXL_PROBE_H +#define AVFORMAT_JPEGXL_PROBE_H + +#include + +#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff +#define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000 + +int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen); + +#endif /* AVFORMAT_JPEGXL_PROBE_H */ diff --git a/libavformat/mov.c b/libavformat/mov.c index 6c847de164..c4b8873b0a 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -7697,6 +7697,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 {