123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- * Jpeg XL header verification
- * Copyright (c) 2022 Leo Izen <leo.izen@gmail.com>
- *
- * 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;
- }
|