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
