[FFmpeg-devel] [RFC PATCH] RFC -> JPEG-XL: Image format header reader

Varun Gupta varun.gupta140 at gmail.com
Sun Apr 12 14:31:00 EEST 2020


Hi everyone,

I made some improvements to the header reader for JPEG-XL, the patch for
which I had submitted earlier.
The improvements include writing separate functions for all the
datatypes to minimize code redundancy.
All this information regarding headers will have to be read in the
parser itself because I won't be able to find the frame end without it.
There is a particular thing that is bothering me and I would like to get
some suggestion from the community regarding that.
How should i handle the case of the reader reading bits beyond the
boundary ?
I have returned an error code for that in the reader implementation but
should I check it everytime I read some bits ?
This would make the code really messy. Please suggest something
regarding this and I'll be happy to include it in my patch.
Thanks
Varun

---
 libavcodec/jpegxl.h             | 391 ++++++++++++++++++++++++++++++++
 libavcodec/jpegxl_parser.h      |  47 ++++
 libavcodec/jpegxl_read_header.c | 340 +++++++++++++++++++++++++++
 3 files changed, 778 insertions(+)
 create mode 100644 libavcodec/jpegxl.h
 create mode 100644 libavcodec/jpegxl_parser.h
 create mode 100644 libavcodec/jpegxl_read_header.c

diff --git a/libavcodec/jpegxl.h b/libavcodec/jpegxl.h
new file mode 100644
index 0000000000..154aab8f61
--- /dev/null
+++ b/libavcodec/jpegxl.h
@@ -0,0 +1,391 @@
+/*
+ * JPEG-XL format definitions
+ * Copyright (c) 2020 Varun Gupta
+ *
+ * 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 format definitions.
+ */
+
+#ifndef AVCODEC_JPEGXL_H
+#define AVCODEC_JPEGXL_H
+
+#include "get_bits.h"
+#include <stdint.h>
+
+#define JPEG_XL_SIG_FF    0xff
+#define JPEG_XL_SIG_TYPE  0x0a
+
+typedef enum ColourSpace {
+    kRGB = 0,
+    kGrey,
+    kXYB,
+    kUnknown,
+    kXYZ
+} ColourSpace;
+
+typedef enum Primaries {
+    kSRGB = 1,
+    kCustomPrimary = 2,
+    k2100 = 9,
+    kP3 = 11
+} Primaries;
+
+typedef enum RenderingIntent {
+    kPerceptual = 0,
+    kRelative,
+    kSaturation,
+    kAbsolute
+} RenderingIntent;
+
+typedef enum WhitePoint {
+    kD65 = 1,
+    kCustom = 2,
+    kE = 10,
+    kDCI = 11
+} WhitePoint;
+
+typedef enum TransferFunction {
+    k709 = 1,
+    kUnknownTransferFunction = 2,
+    kLinear = 8,
+    kSRGBTransferFunction = 13,
+    kPQ = 16,
+    kDCITransferFunction = 17,
+    kHLG = 18
+} TransferFunction;
+
+typedef struct Customxy {
+    int x;
+    int y;
+} Customxy;
+
+typedef struct ExtraChannelInfo {
+    unsigned int meaning;
+    float red;
+    float green;
+    float blue;
+    float solidity;
+} ExtraChannelInfo;
+
+typedef enum JPEGXLHeaderStates {
+    JPEGXL_UNDEFINED = 0,
+    JPEGXL_SIG,
+    JPEGXL_SIZE_HEADER,
+    JPEGXL_IMAGE_METADATA,
+    JPEGXL_PREVIEW_HEADER,
+    JPEGXL_ANIMATION_HEADER,
+    JPEGXL_ICC_CODEC,
+    JPEGXL_PREVIEW_FRAME,
+    JPEGXL_FRAMES
+} jpegxl_header_states;
+
+typedef struct ColourEncoding {
+    unsigned int all_default;
+    unsigned int received_icc;
+    unsigned int opaque_icc;
+    ColourSpace colour_space;
+    WhitePoint white_point;
+    Customxy white;
+    Primaries primaries;
+    Customxy red;
+    Customxy green;
+    Customxy blue;
+    unsigned int have_gamma;
+    unsigned int gamma;
+    TransferFunction transfer_function;
+    RenderingIntent rendering_intent;
+} ColourEncoding;
+
+typedef struct Extensions {
+    unsigned long long extensions;
+    unsigned long long extension_bits;
+} Extensions;
+
+typedef struct ImageMetadata2 {
+    unsigned int all_default;
+    unsigned int have_preview;
+    unsigned int have_animation;
+    unsigned int orientation_minus_1;
+    unsigned int depth_bits;
+    unsigned int depth_shift;
+    unsigned int num_extra_channels;
+    unsigned int extra_channel_bits;
+    ExtraChannelInfo* extra_channel_info;
+    Extensions extension;
+} ImageMetadata2;
+
+typedef struct SizeHeader {
+    unsigned int ysize_div8_minus_1;
+    unsigned int ysize_minus_1;
+    unsigned int xsize_div8_minus_1;
+    unsigned int xsize_minus_1;
+    unsigned int xsize;
+    unsigned int ysize;
+} SizeHeader;
+
+typedef struct PreviewHeader {
+    unsigned int ysize_div8_minus_1;
+    unsigned int ysize_minus_1;
+    unsigned int xsize_div8_minus_1;
+    unsigned int xsize_minus_1;
+    unsigned int xsize;
+    unsigned int ysize;
+} PreviewHeader;
+
+typedef struct ImageMetadata {
+    unsigned int all_default;
+    unsigned int have_icc;
+    unsigned int bits_per_sample;
+    unsigned int alpha_bits;
+    unsigned int target_nits_div50;
+    ColourEncoding colour_encoding;
+    ImageMetadata2 m2;
+} ImageMetadata;
+
+typedef struct AnimationHeader {
+    unsigned int composite_still;
+    unsigned long int tps_numerator_minus_1;
+    unsigned int tps_denominator_minus_1;
+    unsigned long int num_loops;
+    unsigned int have_timecodes;
+} AnimationHeader;
+
+typedef struct u32Parameter {
+    unsigned int identifier;   // identifier = 1 for Val, 2 for Bits, 3 for BitsOffset
+    unsigned int param1;
+    unsigned int param2;   // only applicable in case of BitsOffset where it is the offset
+} u32Parameter;
+
+static int checkIfValueInColourSpace (unsigned int value) {
+    return value >=0 && value <= 4;
+}
+
+static int checkIfValueInPrimaries (unsigned int value) {
+    return value == 1 || value == 2 || value == 9 || value == 11;
+}
+
+static int checkIfValueInRenderingIntent (unsigned int value) {
+    return value >=0 && value <= 3;
+}
+
+static int checkIfValueInWhitePoint (unsigned int value) {
+    return value == 1 || value == 2 || value == 10 || value == 11;
+}
+
+static int checkIfValueInTransferFunction (unsigned int value) {
+    return value == 1 || value == 2 || value == 8 || value == 13 ||
+         value == 16 || value == 17 || value == 18;
+}
+
+static unsigned int u(int n, GetBitContext* gb) {
+    unsigned int read_val;
+    if (n == 0) {
+        return 0;
+    }
+    read_val =  get_bits_long(gb, n);
+    if (get_bits_left(gb) < 0) {
+        return AVERROR_BUFFER_TOO_SMALL;   // read beyond the buffer boundary
+    }
+    return read_val;
+}
+
+static unsigned int pu(GetBitContext* gb) {
+    int bits_read = get_bits_count(gb);
+    int no_of_bits_to_byte = 8 - (bits_read % 8);
+    return u(no_of_bits_to_byte, gb);
+}
+
+static unsigned int bitsOffset(unsigned int n, unsigned int offset, GetBitContext* gb) {
+    unsigned long long value = u(n, gb) + offset;
+    return (unsigned int) (value % (1LL << 32));
+}
+
+static unsigned int resolveU32Parameter(u32Parameter parameter, GetBitContext* gb) {
+    if (parameter.identifier == 1) {  // parameter = Val(u)
+        return parameter.param1;
+    }
+    if (parameter.identifier == 2) {    // parameter = Bits(n)
+        return u(parameter.param1, gb);
+    }
+
+    // parameter = BitsOffset(n, offset)
+    return bitsOffset(parameter.param1, parameter.param2, gb);
+}
+
+static unsigned int u32(u32Parameter d0, u32Parameter d1, u32Parameter d2, u32Parameter d3, GetBitContext* gb) {
+    int selector = u(2, gb);
+    if (selector == 0) {
+        return resolveU32Parameter(d0, gb);
+    }
+    if(selector == 1) {
+        return resolveU32Parameter(d1, gb);
+    }
+    if(selector == 2) {
+        return resolveU32Parameter(d2, gb);
+    }
+    return resolveU32Parameter(d3, gb);
+}
+
+static unsigned long long u64(GetBitContext* gb) {
+    unsigned int selector = u(2, gb), shift;
+    unsigned long long value = 0;
+    if (selector == 0) {  // Val(0)
+        return 0;
+    }
+    if (selector == 1) {  // BitsOffset(4, 1)
+        return bitsOffset(4, 1, gb);
+    }
+    if(selector == 2) {   // BitsOffset(8, 17)
+        return bitsOffset(8, 17, gb);
+    }
+
+    // read value from multiple components
+    value = u(12, gb);
+    shift = 12;
+    // Read 1-bit flag, stop if zero, read and add 4 or 8 bit components
+    while (u(1, gb) == 1) {
+        // Last part only has 4 bits because we already have read 60 bits
+        if (shift == 60) {
+            value += u(4, gb) << shift;
+            break;
+        }
+        value += u(8, gb) << shift;
+        shift += 8;
+    }
+    return value;
+}
+
+static unsigned long long varint(GetBitContext* gb) {
+    unsigned int b, shift = 0;
+    unsigned long long value = 0;
+
+    // Read a byte, add its 7 lower bits, stop if upper bit 0
+    while (1) {
+        b = u(8, gb);
+        value += (b & 127) << shift;
+        if (b <= 127) {
+            break;
+        }
+        shift += 7;
+        if (shift >= 63) {
+            return AVERROR_INVALIDDATA;   // Code stream is ill-formed
+        }
+    }
+    return value;
+}
+
+static unsigned int u8(GetBitContext* gb) {
+    unsigned int n;
+    if (u(1, gb) == 0) {
+          return 0;
+    }
+    n = u(3, gb);
+    return u(n, gb) + (1 << n);
+}
+
+static double f16(GetBitContext* gb) {
+    unsigned int bits16, sign, biased_exp, mantissa, biased_exp32, mantissa32, bits32;
+    float value = 0;
+    bits16 = u(16, gb);
+    sign = bits16 >> 15;
+    biased_exp = (bits16 >> 10) & 0x1f;
+    if (biased_exp == 31) {
+        return AVERROR_INVALIDDATA;   // Code stream is ill-formed
+    }
+    mantissa = bits16 & 0x3ff;
+    if (biased_exp == 0) {
+        value = (1.0f / 16384) * (mantissa * (1.0f / 1024));
+    } else {
+        biased_exp32 = biased_exp + (127 - 15);
+        mantissa32 = mantissa << (23 - 10);
+        bits32 = (sign << 31) | (biased_exp32 << 23) | mantissa32;
+        memcpy(&value, &bits32, sizeof(bits32));
+    }
+    return value;
+}
+
+static unsigned int pu0(GetBitContext* gb) {
+    unsigned int skippedBits = pu(gb);
+    if (skippedBits != 0) {
+        return AVERROR_INVALIDDATA;   // Code stream is ill-formed
+    }
+    return 0;
+}
+
+static unsigned int bool(GetBitContext* gb) {
+    return u(1, gb);
+}
+
+static int s32(u32Parameter d0, u32Parameter d1, u32Parameter d2, u32Parameter d3, GetBitContext* gb) {
+    unsigned int value = u32(d0, d1, d2, d3, gb);
+    if (value & 1) {
+        return -((value >> 1) + 1);
+    }
+    return value >> 1;
+}
+
+static unsigned int enumType(GetBitContext* gb) {
+    u32Parameter d0 = {1, 0};
+    u32Parameter d1 = {1, 1};
+    u32Parameter d2 = {3, 4, 2};
+    u32Parameter d3 = {3, 6, 18};
+    return u32(d0, d1, d2, d3, gb);
+}
+
+static unsigned int getXSize(unsigned int ratio, unsigned int ysize) {
+    unsigned int xsize;
+    switch (ratio) {
+        case 1:
+            xsize = ysize;
+            break;
+        case 2:
+            xsize = (ysize * 12) / 10;
+            break;
+        case 3:
+            xsize = (ysize * 4) / 3;
+            break;
+        case 4:
+            xsize = (ysize * 3) / 2;
+            break;
+        case 5:
+            xsize = (ysize * 16) / 9;
+            break;
+        case 6:
+            xsize = (ysize * 5) / 4;
+            break;
+        case 7:
+            xsize = ysize * 2;
+            break;
+    }
+    return xsize;
+}
+
+static void customXY(int* x, int* y, GetBitContext* gb) {
+    u32Parameter d0 = {2, 19};
+    u32Parameter d1 = {3, 19, 524288};
+    u32Parameter d2 = {3, 20, 1048576};
+    u32Parameter d3 = {3, 21, 2097152};
+
+    *x = s32(d0, d1, d2, d3, gb);
+    *y = s32(d0, d1, d2, d3, gb);
+}
+
+#endif //AVCODEC_JPEGXL_H
diff --git a/libavcodec/jpegxl_parser.h b/libavcodec/jpegxl_parser.h
new file mode 100644
index 0000000000..d051488455
--- /dev/null
+++ b/libavcodec/jpegxl_parser.h
@@ -0,0 +1,47 @@
+/*
+ * JPEG-XL parser
+ * Copyright (c) 2020 Varun Gupta
+ *
+ * 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 format definitions
+ */
+
+#ifndef AVCODEC_JPEGXL_PARSER_H
+#define AVCODEC_JPEGXL_PARSER_H
+
+#include "jpegxl.h"
+#include "parser.h"
+
+typedef struct HeaderReader {
+    jpegxl_header_states state;
+    SizeHeader size;
+    PreviewHeader preview;
+    ImageMetadata metadata;
+    AnimationHeader animation;
+} HeaderReader;
+
+typedef struct JPEGXLParseContext {
+    ParseContext pc;
+    GetBitContext gb;
+    HeaderReader reader;
+} JPEGXLParseContext;
+
+#endif // AVCODEC_JPEGXL_PARSER_H
diff --git a/libavcodec/jpegxl_read_header.c b/libavcodec/jpegxl_read_header.c
new file mode 100644
index 0000000000..288a987a92
--- /dev/null
+++ b/libavcodec/jpegxl_read_header.c
@@ -0,0 +1,340 @@
+/*
+ * JPEG-XL parser
+ * Copyright (c) 2020 Varun Gupta
+ *
+ * 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 reader
+ */
+
+#include "jpegxl_parser.h"
+
+static unsigned int read_image_headers(JPEGXLParseContext *context) {
+    unsigned int small, ratio, enum_value, div8;
+    u32Parameter d0, d1, d2, d3;
+    memset(&context->reader, 0, sizeof(context->reader));   // Initializing all fields to 0
+    while (1) {
+        if (context->reader.state == JPEGXL_UNDEFINED) {
+            if (u(8, &context->gb) == JPEG_XL_SIG_FF) {
+                context->reader.state = JPEGXL_SIG;
+            } else {
+                return AVERROR_INVALIDDATA; // Code stream is ill-formed
+            }
+        } else if (context->reader.state == JPEGXL_SIG) {
+            if (u(8, &context->gb) == JPEG_XL_SIG_TYPE) {
+                context->reader.state = JPEGXL_SIZE_HEADER;
+            } else {
+                return AVERROR_INVALIDDATA; // Code stream is ill-formed
+            }
+        } else if (context->reader.state == JPEGXL_SIZE_HEADER) {
+            small = u(1, &context->gb);
+            if (small) {
+                context->reader.size.ysize_div8_minus_1 = u(5, &context->gb);
+            } else {
+                d0 = (u32Parameter){2, 9};
+                d1 = (u32Parameter){2, 13};
+                d2 = (u32Parameter){2, 18};
+                d3 = (u32Parameter){2, 30};
+                context->reader.size.ysize_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+            }
+            context->reader.size.ysize = small ? (context->reader.size.ysize_div8_minus_1 + 1) * 8 : context->reader.size.ysize_minus_1 + 1;
+            ratio = u(3, &context->gb);
+            if (ratio == 0) {
+                if (small) {
+                    context->reader.size.xsize_div8_minus_1 = u(5, &context->gb);
+                } else {
+                    d0 = (u32Parameter){2, 9};
+                    d1 = (u32Parameter){2, 13};
+                    d2 = (u32Parameter){2, 18};
+                    d3 = (u32Parameter){2, 30};
+                    context->reader.size.xsize_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+                }
+            }
+            context->reader.size.xsize = ratio == 0 ? (small ? (context->reader.size.xsize_div8_minus_1 + 1) * 8 : context->reader.size.xsize_minus_1 + 1) : getXSize(ratio, context->reader.size.ysize);
+            context->reader.state = JPEGXL_IMAGE_METADATA;
+        } else if (context->reader.state == JPEGXL_IMAGE_METADATA) {
+
+            // setting up default values
+            context->reader.metadata.bits_per_sample = 8;
+            context->reader.metadata.target_nits_div50 = 5;
+            context->reader.metadata.colour_encoding.colour_space = kRGB;
+            context->reader.metadata.colour_encoding.white_point = kD65;
+            context->reader.metadata.colour_encoding.primaries = kSRGB;
+            context->reader.metadata.colour_encoding.transfer_function = kSRGBTransferFunction;
+            context->reader.metadata.colour_encoding.rendering_intent = kRelative;
+
+            context->reader.metadata.all_default = bool(&context->gb);
+            if (!context->reader.metadata.all_default) {
+                unsigned int use_desc, not_xy;
+                // have_icc
+                context->reader.metadata.have_icc = bool(&context->gb);
+
+                // bits_per_sample
+                d0 = (u32Parameter){1, 8};
+                d1 = (u32Parameter){1, 16};
+                d2 = (u32Parameter){1, 32};
+                d3 = (u32Parameter){2, 5};
+                context->reader.metadata.bits_per_sample = u32(d0, d1, d2, d3, &context->gb);
+
+                // colour_encoding
+                context->reader.metadata.colour_encoding.all_default = bool(&context->gb);
+                if (!context->reader.metadata.colour_encoding.all_default) {
+                    context->reader.metadata.colour_encoding.received_icc = bool(&context->gb);
+                }
+
+                if (context->reader.metadata.colour_encoding.received_icc) {
+                    context->reader.metadata.colour_encoding.opaque_icc = bool(&context->gb);
+                }
+
+                use_desc = !context->reader.metadata.colour_encoding.all_default && !context->reader.metadata.colour_encoding.opaque_icc;
+
+                if (use_desc) { // colour_space enum
+                    enum_value = enumType(&context->gb);
+                    if (checkIfValueInColourSpace(enum_value)) {
+                      context->reader.metadata.colour_encoding.colour_space = enum_value;
+                    } else {
+                      return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                not_xy = context->reader.metadata.colour_encoding.colour_space != kXYZ && context->reader.metadata.colour_encoding.colour_space != kXYB;
+
+                if (use_desc && not_xy) { // white_point enum
+                    enum_value = enumType(&context->gb);
+                    if (checkIfValueInWhitePoint(enum_value)) {
+                        context->reader.metadata.colour_encoding.white_point = enum_value;
+                    } else {
+                        return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                if (use_desc && context->reader.metadata.colour_encoding.white_point == kCustom) {
+                    customXY(&context->reader.metadata.colour_encoding.white.x, &context->reader.metadata.colour_encoding.white.y, &context->gb);
+                }
+
+                if (use_desc && not_xy && context->reader.metadata.colour_encoding.colour_space != kGrey) { // primaries enum
+                    enum_value = enumType(&context->gb);
+                    if (checkIfValueInPrimaries(enum_value)) {
+                        context->reader.metadata.colour_encoding.primaries = enum_value;
+                    } else {
+                        return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                if (use_desc && context->reader.metadata.colour_encoding.primaries == kCustomPrimary) {
+                    customXY(&context->reader.metadata.colour_encoding.red.x,
+                             &context->reader.metadata.colour_encoding.red.y,
+                             &context->gb);
+                    customXY(&context->reader.metadata.colour_encoding.green.x,
+                             &context->reader.metadata.colour_encoding.green.y,
+                             &context->gb);
+                    customXY(&context->reader.metadata.colour_encoding.blue.x,
+                             &context->reader.metadata.colour_encoding.blue.y,
+                             &context->gb);
+                }
+
+                if (use_desc && not_xy) {
+                    context->reader.metadata.colour_encoding.have_gamma = bool(&context->gb);
+                }
+
+                if (use_desc && context->reader.metadata.colour_encoding.have_gamma) {
+                    context->reader.metadata.colour_encoding.gamma = u(24, &context->gb);
+                }
+
+                // if have_gamma == false, transfer function is defined by transfer_function
+                // else, transfer function is defined by OETF exponent = gamma/10^7
+                // and this OETF should be in (0,1)
+                if (context->reader.metadata.colour_encoding.have_gamma) {
+                    if ((context->reader.metadata.colour_encoding.gamma * 1.0f) / 10000000 >= 1 ||
+                        (context->reader.metadata.colour_encoding.gamma * 1.0f) / 10000000 == 0) {
+                        return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                if (use_desc && !context->reader.metadata.colour_encoding.have_gamma && not_xy) { // transfer_function enum
+                    enum_value = enumType(&context->gb);
+                    if (checkIfValueInTransferFunction(enum_value)) {
+                        context->reader.metadata.colour_encoding.transfer_function = enum_value;
+                    } else {
+                        return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                if (use_desc && context->reader.metadata.colour_encoding.colour_space != kGrey && not_xy) { // rendering_intent enum
+                    enum_value = enumType(&context->gb);
+                    if (checkIfValueInRenderingIntent(enum_value)) {
+                        context->reader.metadata.colour_encoding.rendering_intent = enum_value;
+                    } else {
+                        return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                    }
+                }
+
+                if (context->reader.metadata.colour_encoding.opaque_icc && !context->reader.metadata.have_icc) {
+                    return AVERROR_INVALIDDATA; // Code stream is ill-formed
+                }
+
+                // alpha_bits
+                d0 = (u32Parameter){1, 0};
+                d1 = (u32Parameter){1, 8};
+                d2 = (u32Parameter){1, 16};
+                d3 = (u32Parameter){2, 4};
+                context->reader.metadata.alpha_bits = u32(d0, d1, d2, d3, &context->gb);
+
+                // target_nits_div50
+                d0 = (u32Parameter){1, 5};
+                d1 = (u32Parameter){1, 20};
+                d2 = (u32Parameter){1, 80};
+                d3 = (u32Parameter){3, 10, 1};
+                context->reader.metadata.target_nits_div50 = u32(d0, d1, d2, d3, &context->gb);
+
+                // m2
+                context->reader.metadata.m2.all_default = bool(&context->gb);
+                if (!context->reader.metadata.m2.all_default) {
+                    context->reader.metadata.m2.have_preview = bool(&context->gb);
+                    context->reader.metadata.m2.have_animation = bool(&context->gb);
+                    context->reader.metadata.m2.orientation_minus_1 = u(3, &context->gb);
+
+                    d0 = (u32Parameter){1, 0};
+                    d1 = (u32Parameter){1, 8};
+                    d2 = (u32Parameter){1, 16};
+                    d3 = (u32Parameter){2, 4};
+                    context->reader.metadata.m2.depth_bits = u32(d0, d1, d2, d3, &context->gb);
+
+                    d0 = (u32Parameter){1, 0};
+                    d1 = (u32Parameter){1, 3};
+                    d2 = (u32Parameter){1, 4};
+                    d3 = (u32Parameter){3, 3, 1};
+                    context->reader.metadata.m2.depth_shift = u32(d0, d1, d2, d3, &context->gb);
+
+                    d0 = (u32Parameter){1, 0};
+                    d1 = (u32Parameter){2, 4};
+                    d2 = (u32Parameter){3, 8, 16};
+                    d3 = (u32Parameter){3, 12, 1};
+                    context->reader.metadata.m2.num_extra_channels = u32(d0, d1, d2, d3, &context->gb);
+                }
+
+                if (context->reader.metadata.m2.num_extra_channels > 0) {
+                    d0 = (u32Parameter){1, 0};
+                    d1 = (u32Parameter){1, 8};
+                    d2 = (u32Parameter){1, 16};
+                    d3 = (u32Parameter){2, 4};
+                    context->reader.metadata.m2.extra_channel_bits = u32(d0, d1, d2, d3, &context->gb);
+                }
+
+                context->reader.metadata.m2.extra_channel_info = (ExtraChannelInfo *)malloc(context->reader.metadata.m2.num_extra_channels * sizeof(ExtraChannelInfo));
+                for (int channel = 0; channel < context->reader.metadata.m2.num_extra_channels; channel++) {
+                    d0 = (u32Parameter){1, 0};
+                    d1 = (u32Parameter){1, 1};
+                    d2 = (u32Parameter){1, 2};
+                    d3 = (u32Parameter){2, 6};
+                    context->reader.metadata.m2.extra_channel_info[channel].meaning = u32(d0, d1, d2, d3, &context->gb);
+
+                    if (context->reader.metadata.m2.extra_channel_info[channel].meaning == 1) {
+                        context->reader.metadata.m2.extra_channel_info[channel].red = f16(&context->gb);
+                        context->reader.metadata.m2.extra_channel_info[channel].green = f16(&context->gb);
+                        context->reader.metadata.m2.extra_channel_info[channel].blue = f16(&context->gb);
+                        context->reader.metadata.m2.extra_channel_info[channel].solidity = f16(&context->gb);
+                    }
+                }
+
+                if (!context->reader.metadata.m2.all_default) {
+                        context->reader.metadata.m2.extension.extensions = u64(&context->gb);
+                        if (context->reader.metadata.m2.extension.extensions != 0) {
+                            context->reader.metadata.m2.extension.extension_bits = u64(&context->gb);
+                            // TODO -> skip the extension_bits
+                        }
+                }
+            }
+            context->reader.state = JPEGXL_PREVIEW_HEADER;
+        } else if (context->reader.state == JPEGXL_PREVIEW_HEADER) {
+            if (!context->reader.metadata.m2.have_preview) {
+                context->reader.state = JPEGXL_ANIMATION_HEADER;
+                continue;
+            }
+
+            div8 = bool(&context->gb);
+            if (div8) {
+                d0 = (u32Parameter){1, 15};
+                d1 = (u32Parameter){1, 31};
+                d2 = (u32Parameter){2, 5};
+                d3 = (u32Parameter){3, 9, 32};
+                context->reader.preview.ysize_div8_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+            } else {
+                d0 = (u32Parameter){2, 6};
+                d1 = (u32Parameter){3, 8, 64};
+                d2 = (u32Parameter){3, 10, 320};
+                d3 = (u32Parameter){3, 12, 1344};
+                context->reader.preview.ysize_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+            }
+            context->reader.preview.ysize = div8 ? (context->reader.preview.ysize_div8_minus_1 + 1) * 8 : context->reader.preview.ysize_minus_1 + 1;
+
+            ratio = u(3, &context->gb);
+            if (ratio == 0) {
+                if (div8) {
+                    d0 = (u32Parameter){1, 15};
+                    d1 = (u32Parameter){1, 31};
+                    d2 = (u32Parameter){2, 5};
+                    d3 = (u32Parameter){3, 9, 32};
+                    context->reader.preview.xsize_div8_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+                } else {
+                    d0 = (u32Parameter){2, 6};
+                    d1 = (u32Parameter){3, 8, 64};
+                    d2 = (u32Parameter){3, 10, 320};
+                    d3 = (u32Parameter){3, 12, 1344};
+                    context->reader.preview.xsize_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+                }
+            }
+            context->reader.preview.xsize = ratio == 0 ? (div8 ? (context->reader.preview.xsize_div8_minus_1 + 1) * 8 : context->reader.preview.xsize_minus_1 + 1) : getXSize(ratio, context->reader.preview.ysize);
+
+            if (context->reader.preview.xsize > 4096 || context->reader.preview.ysize > 4096) {
+                return AVERROR_INVALIDDATA; // Code stream is ill-formed
+            }
+            context->reader.state = JPEGXL_ANIMATION_HEADER;
+        } else if (context->reader.state == JPEGXL_ANIMATION_HEADER) {
+            if (!context->reader.metadata.m2.have_animation) {
+                break;
+            }
+            context->reader.animation.composite_still = bool(&context->gb);
+            if (!context->reader.animation.composite_still) {
+                d0 = (u32Parameter){1, 99};
+                d1 = (u32Parameter){1, 999};
+                d2 = (u32Parameter){2, 6};
+                d3 = (u32Parameter){2, 18};
+                context->reader.animation.tps_numerator_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+
+                d0 = (u32Parameter){1, 0};
+                d1 = (u32Parameter){1, 1000};
+                d2 = (u32Parameter){2, 8};
+                d3 = (u32Parameter){2, 10};
+                context->reader.animation.tps_denominator_minus_1 = u32(d0, d1, d2, d3, &context->gb);
+
+                d0 = (u32Parameter){1, 0};
+                d1 = (u32Parameter){2, 3};
+                d2 = (u32Parameter){2, 16};
+                d3 = (u32Parameter){2, 32};
+                context->reader.animation.num_loops = u32(d0, d1, d2, d3, &context->gb);
+
+                context->reader.animation.have_timecodes = bool(&context->gb);
+            }
+            break;
+        }
+    }
+    return 0;
+}
\ No newline at end of file
-- 
2.25.0



More information about the ffmpeg-devel mailing list