From patchwork Tue Apr 12 05:53:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 35277 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:671c:b0:7c:62c8:b2d1 with SMTP id q28csp1410134pzh; Mon, 11 Apr 2022 22:55:13 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxhg4n2YqgUT5zR301j+Tl4v3rSJDuI0e3bEYuroUq0xN7VH0bhnnAlgzmwNCpbqyAZdG3K X-Received: by 2002:a17:907:8a17:b0:6e8:67d0:b6ca with SMTP id sc23-20020a1709078a1700b006e867d0b6camr15209550ejc.23.1649742913546; Mon, 11 Apr 2022 22:55:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1649742913; cv=none; d=google.com; s=arc-20160816; b=GIocmqGg7WKGoBZkIdcpKulsqPtwXalXg5r5WRuylbijeajkATlVtBPqboskyMsQoc X0eXFSu9ZHCoLb0lykMC3kKI9eaD33QmysXDmJWuMg+Fkv9eX8+S+ElpSRqSwFSJ77jp 7cxPgNfnNHPDCJeYOGRnMeI0uq84iMsQEOXk2X9jUhc7Si8JymQ+x58rQYtPQNwb4tk4 i1+/B6eHEPKVfvKu4GNgIxPBtrUkauDP/1H+fl2RSBclzyWGSM7e9/le9p2TVy/9RMMp SMnkhd5IUY+Prpuhkssl6lakoMZ1pHrzyva4OfesrwH3v82yks5uMK3bBOmI0izGkiLm mtbg== 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=4B4RqSlap20Wp/ePgxmQ7e1sMG3aRLDBW+jk5HQ6Q2s=; b=JwcXh/s2VXdq2kiLGMagY4KVDoH9R0/zQWAOXWfbT2NuyQBofJUc6tyAxaeOPNSqMq +3XPL7dj0KIFvVyl4bcbMIBEZeCJMLxRLU7hCAoae1MmOiMLefkWB79yi6tlqkYe00Cw NAQE+AnNte2OW6KSCjLRVSJ4Qkw/SGqfy1GacHRlHgOw6HexhmdDWADxB7UWsef/uuvg EN6v9sFLbm6HIDwN3ifTia9TBl6nUc12DFZEV3bTCH6OLFkgbdvMxrIkxJKVwZFXb+7p eYqwPgH0talPjNS2gEwz/Nh0yGblu0DCjniWeqePG86LPnevI9VIZ1YDmYLOeCF+uijp 26RA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=KZF4ThKM; 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 hd16-20020a170907969000b006e0a811b828si12102972ejc.313.2022.04.11.22.55.13; Mon, 11 Apr 2022 22:55:13 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=KZF4ThKM; 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 7FB3A68B38D; Tue, 12 Apr 2022 08:54:33 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f47.google.com (mail-qv1-f47.google.com [209.85.219.47]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 33FEA68B36F for ; Tue, 12 Apr 2022 08:54:25 +0300 (EEST) Received: by mail-qv1-f47.google.com with SMTP id y19so3447688qvk.5 for ; Mon, 11 Apr 2022 22:54:25 -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=eu+vndcqQ9psmYLI85idQx8sMfORuk+/7IDcVGYAPlc=; b=KZF4ThKMgDV//sRMYkQQQ1PWBWu8cK1zkFNpMa/IQp9rGUixw+rklwgLBlUsXKfxYc mDDrtzyBwT7dQFpvuHZCAiDEB9zf4CCWl3Q/8+XOd5R6hD/UGjZH/Kmjsubi6huKqFfU f4c6y3pkpEy0EVSY5kQmNUGCFAwPhrc6lOhlwv9i+B564GM5Kwar9qdfV8FCquyF3olJ h0hHNRIaqN5iqwH2mF09efpHDPJdAm26lKo2ASYjARTKLWmIfWkD7Uy8pHnPP6X0PdWJ Yjc3ffJs05zsJFgsrU4ZHCPf1qbEXezIZ5fPWIOBT967K1XwOBtpcRE4mP4PD9Coe0w5 ieoA== 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=eu+vndcqQ9psmYLI85idQx8sMfORuk+/7IDcVGYAPlc=; b=M2gzhOoMDmRl6WTDandG6BSP/Yiuur1GZJwDQ5uGo7OiDe14kOPO6Ysdbj1dDUCFtg yy5MRoctMFX9e3rQIj3oAzpw6BRqIqSBe6soafuxiu0bgUdpCxm92emeRW3M06bQd+tZ vlk+CMXVp8VW44fRaNkAQvDkrKenYJmDuPG+fqeTza4PF4IUgppQk/mGPAAEFOtUMcGY Yb1pOIzoPZq8MLlR39QX0iIKPFJdPG4ymg5IzMFBimGgLEQP5GTxv74gjTDze7AGovFp 51rZvozHsoz28QaBSWOYVhiMCefcsyic+uc6JI7UsoFVmAgkYCwn7HvXOPnsvyJR9Qhz ABJQ== X-Gm-Message-State: AOAM530wWYVvUyuL1P7uH+ld51nlOCdzLHMvKm8IAGpF1w8t7kVW6eG1 xLmFK8Zl4OzWLlp8qwBno+zfIl/ED18= X-Received: by 2002:a0c:f8ca:0:b0:444:41e8:89b1 with SMTP id h10-20020a0cf8ca000000b0044441e889b1mr7735028qvo.22.1649742863959; Mon, 11 Apr 2022 22:54:23 -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 k2-20020a37ba02000000b0067dc1b0104asm19801988qkf.124.2022.04.11.22.54.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 Apr 2022 22:54:23 -0700 (PDT) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 12 Apr 2022 01:53:33 -0400 Message-Id: <20220412055333.62424-5-leo.izen@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220412055333.62424-1-leo.izen@gmail.com> References: <20220412055333.62424-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v14 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: YEirt+PExvqX 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 | 18 ++ libavformat/img2enc.c | 6 +- libavformat/jpegxl_probe.c | 393 +++++++++++++++++++++++++++++++++++++ libavformat/jpegxl_probe.h | 32 +++ libavformat/mov.c | 1 + 9 files changed, 451 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 d7182d6bd8..beecdf5a66 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..627bb67212 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,22 @@ 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 (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size) >= 0) + return AVPROBE_SCORE_MAX - 2; + return 0; +} + static int pcx_probe(const AVProbeData *p) { const uint8_t *b = p->buf; @@ -1176,6 +1193,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..d3d3822fee --- /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; + 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 Preview Header + * @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 - (gb->index - 1) % 8; + if (bits_remaining && jxl_bits(bits_remaining)) + return -1; + } + + if (gb->index > gb->size_in_bits) + 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 {