[FFmpeg-devel] [PATCH] JPEG-XL : Image Format Parser
Varun Gupta
varun.gupta140 at gmail.com
Sun Mar 15 20:41:54 EET 2020
From: Varun Gupta <gupvar at amazon.com>
---
libavcodec/avcodec.h | 1 +
libavcodec/jpeg-xl.h | 180 +++++++++
libavcodec/jpeg-xl_parser.c | 707 ++++++++++++++++++++++++++++++++++++
3 files changed, 888 insertions(+)
create mode 100644 libavcodec/jpeg-xl.h
create mode 100644 libavcodec/jpeg-xl_parser.c
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 5a0fc3405c..ecfa2e0009 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -463,6 +463,7 @@ enum AVCodecID {
AV_CODEC_ID_MVDV,
AV_CODEC_ID_MVHA,
AV_CODEC_ID_CDTOONS,
+ AV_CODEC_ID_JPEGXL,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/jpeg-xl.h b/libavcodec/jpeg-xl.h
new file mode 100644
index 0000000000..7a5b5a351d
--- /dev/null
+++ b/libavcodec/jpeg-xl.h
@@ -0,0 +1,180 @@
+/*
+ * 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_JPEG_XL_H
+#define AVCODEC_JPEG_XL_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 {
+ unsigned int x;
+ unsigned int y;
+} Customxy;
+
+typedef struct ExtraChannelInfo {
+ unsigned int meaning;
+ float red;
+ float green;
+ float blue;
+ float solidity;
+} ExtraChannelInfo;
+
+typedef enum JPEGXLParseStates {
+ JPEGXL_SIG = 1,
+ JPEGXL_SIZE_HEADER,
+ JPEGXL_IMAGE_METADATA,
+ JPEGXL_PREVIEW_HEADER,
+ JPEGXL_ANIMATION_HEADER,
+ JPEGXL_ICC_CODEC,
+ JPEGXL_PREVIEW_FRAME,
+ JPEGXL_FRAMES
+} jpegxl_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 long int gamma;
+ TransferFunction transfer_function;
+ RenderingIntent rendering_intent;
+} ColourEncoding;
+
+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;
+} ImageMetadata2;
+
+typedef struct SizeHeader {
+ unsigned long int ysize_div8_minus_1;
+ unsigned long int ysize_minus_1;
+ unsigned long int xsize_div8_minus_1;
+ unsigned long int xsize_minus_1;
+} SizeHeader;
+
+typedef struct PreviewHeader {
+ unsigned long int ysize_div8_minus_1;
+ unsigned long int ysize_minus_1;
+ unsigned long int xsize_div8_minus_1;
+ unsigned long int xsize_minus_1;
+} 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;
+
+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;
+}
+
+#endif //AVCODEC_JPEG_XL_H
diff --git a/libavcodec/jpeg-xl_parser.c b/libavcodec/jpeg-xl_parser.c
new file mode 100644
index 0000000000..e8d9007970
--- /dev/null
+++ b/libavcodec/jpeg-xl_parser.c
@@ -0,0 +1,707 @@
+/*
+ * 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
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/bswap.h"
+#include "libavutil/common.h"
+
+#include "jpeg-xl.h"
+#include "parser.h"
+#include "get_bits.h"
+
+typedef struct JPEGXLParseContext {
+ ParseContext pc;
+ int state;
+ int index; // keeps track of number of bits read from the media file
+ SizeHeader size;
+ PreviewHeader preview;
+ ImageMetadata metadata;
+ AnimationHeader animation;
+} JPEGXLParseContext;
+
+static unsigned int bits_offset_enum(GetBitContext gb, int n, int offset) {
+ unsigned int read_n_bits = get_bits(&gb, n);
+ return read_n_bits + offset;
+}
+
+// TODO -> add checks for buffer size overflow in between and ill formed checks
+static int jpegxl_find_frame_end(JPEGXLParseContext *context, const uint8_t *buf,
+ int buf_size) {
+ int index, next = END_NOT_FOUND;
+ GetBitContext gb;
+ init_get_bits(&gb, buf, buf_size*8);
+ for (index = 0; index < buf_size*8; ) {
+ if (!context->state) {
+ if (get_bits(&gb, 8) == JPEG_XL_SIG_FF) {
+ context->state = JPEGXL_SIG;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ index += 8;
+ context->index += 8;
+ } else if (context->state == JPEGXL_SIG) {
+ if (get_bits(&gb, 8) == JPEG_XL_SIG_TYPE) {
+ context->state = JPEGXL_SIZE_HEADER;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ index +=8;
+ context->index += 8;
+ } else if (context->state == JPEGXL_SIZE_HEADER) {
+ // default values
+ context->size.ysize_div8_minus_1 = 0;
+ context->size.ysize_minus_1 = 0;
+ context->size.xsize_div8_minus_1 = 0;
+ context->size.xsize_minus_1 = 0;
+
+ unsigned int small = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if (small) {
+ context->size.ysize_div8_minus_1 = get_bits(&gb, 5);
+ index += 5;
+ context->index += 5;
+ } else {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Bits(9)
+ context->size.ysize_minus_1 = get_bits(&gb, 9);
+ index += 9;
+ context->index += 9;
+ } else if (input == 1) { // d1 = Bits(13)
+ context->size.ysize_minus_1 = get_bits(&gb, 17);
+ index += 13;
+ context->index += 13;
+ } else if (input == 2) { // d2 = Bits(18)
+ context->size.ysize_minus_1 = get_bits_long(&gb, 18);
+ index += 18;
+ context->index += 18;
+ } else { // d3 = Bits(30)
+ context->size.ysize_minus_1 = get_bits_long(&gb, 30);
+ index += 30;
+ context->index += 30;
+ }
+ }
+ unsigned int ratio = get_bits(&gb, 3);
+ index += 3;
+ context->index += 3;
+ if (ratio == 0) {
+ if (small) {
+ context->size.xsize_div8_minus_1 = get_bits(&gb, 5);
+ index += 5;
+ context->index += 5;
+ } else {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Bits(9)
+ context->size.xsize_minus_1 = get_bits(&gb, 9);
+ index += 9;
+ context->index += 9;
+ } else if (input == 1) { // d1 = Bits(13)
+ context->size.xsize_minus_1 = get_bits(&gb, 17);
+ index += 13;
+ context->index += 13;
+ } else if (input == 2) { // d2 = Bits(18)
+ context->size.xsize_minus_1 = get_bits_long(&gb, 18);
+ index += 18;
+ context->index += 18;
+ } else { // d3 = Bits(30)
+ context->size.xsize_minus_1 = get_bits_long(&gb, 30);
+ index += 30;
+ context->index += 30;
+ }
+ }
+ }
+ context->state = JPEGXL_IMAGE_METADATA;
+ } else if (context->state == JPEGXL_IMAGE_METADATA) {
+ // setting up default values
+ context->metadata.have_icc = 0;
+ context->metadata.alpha_bits = 0;
+ context->metadata.bits_per_sample = 8;
+ context->metadata.target_nits_div50 = 5;
+ context->metadata.colour_encoding.received_icc = 0;
+ context->metadata.colour_encoding.opaque_icc = 0;
+ context->metadata.colour_encoding.colour_space = kRGB;
+ context->metadata.colour_encoding.white_point = kD65;
+ context->metadata.colour_encoding.primaries = kSRGB;
+ context->metadata.colour_encoding.have_gamma = 0;
+ context->metadata.colour_encoding.gamma = 0;
+ context->metadata.colour_encoding.transfer_function = kSRGBTransferFunction;
+ context->metadata.colour_encoding.rendering_intent = kRelative;
+ context->metadata.m2.have_preview = 0;
+ context->metadata.m2.have_animation = 0;
+ context->metadata.m2.orientation_minus_1 = 0;
+ context->metadata.m2.depth_bits = 0;
+ context->metadata.m2.depth_shift = 0;
+ context->metadata.m2.num_extra_channels = 0;
+ context->metadata.m2.extra_channel_bits = 0;
+
+ context->metadata.all_default = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if (!context->metadata.all_default) {
+ context->metadata.have_icc = get_bits(&gb, 1);
+ index++;
+ context->index++;
+
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(8)
+ context->metadata.bits_per_sample = 8;
+ } else if (input == 1) { // d1 = Val(16)
+ context->metadata.bits_per_sample = 16;
+ } else if (input == 2) { // d2 = Val(32)
+ context->metadata.bits_per_sample = 32;
+ } else { // d3 = Bits(5)
+ context->metadata.bits_per_sample = get_bits(&gb, 5);
+ index += 5;
+ context->index += 5;
+ }
+
+ context->metadata.colour_encoding.all_default = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if(!context->metadata.colour_encoding.all_default) {
+ context->metadata.colour_encoding.received_icc = get_bits(&gb ,1);
+ index++;
+ context->index++;
+ }
+ if(context->metadata.colour_encoding.received_icc) {
+ context->metadata.colour_encoding.opaque_icc = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ }
+ int use_desc = !context->metadata.colour_encoding.all_default &&
+ !context->metadata.colour_encoding.opaque_icc;
+ if(use_desc) { // colour_space enum
+ unsigned int input = get_bits(&gb, 2);
+ index += 2;
+ context->index += 2;
+ unsigned int enum_value;
+ if (input == 0) {
+ enum_value = 0;
+ } else if (input == 1) {
+ enum_value = 1;
+ } else if (input == 2) {
+ enum_value = bits_offset_enum(gb, 4, 2);
+ index += 4;
+ context->index += 4;
+ } else {
+ enum_value = bits_offset_enum(gb, 6, 18);
+ index += 6;
+ context->index += 6;
+ }
+
+ if (checkIfValueInColourSpace(enum_value)) {
+ context->metadata.colour_encoding.colour_space = enum_value;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ }
+ int not_xy = context->metadata.colour_encoding.colour_space != kXYZ &&
+ context->metadata.colour_encoding.colour_space != kXYB;
+ if(use_desc && not_xy) { // white_point enum
+ unsigned int input = get_bits(&gb, 2);
+ index += 2;
+ context->index += 2;
+ unsigned int enum_value;
+ if (input == 0) {
+ enum_value = 0;
+ } else if (input == 1) {
+ enum_value = 1;
+ } else if (input == 2) {
+ enum_value = bits_offset_enum(gb, 4, 2);
+ index += 4;
+ context->index += 4;
+ } else {
+ enum_value = bits_offset_enum(gb, 6, 18);
+ index += 6;
+ context->index += 6;
+ }
+
+ if (checkIfValueInWhitePoint(enum_value)) {
+ context->metadata.colour_encoding.white_point = enum_value;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ }
+ if (use_desc && context->metadata.colour_encoding.white_point == kCustom) {
+ // TODO -> Implement custom xy for white
+ }
+ if (use_desc && not_xy && context->metadata.colour_encoding.colour_space != kGrey) { // primaries enum
+ unsigned int input = get_bits(&gb, 2);
+ index += 2;
+ context->index += 2;
+ unsigned int enum_value;
+ if (input == 0) {
+ enum_value = 0;
+ } else if (input == 1) {
+ enum_value = 1;
+ } else if (input == 2) {
+ enum_value = bits_offset_enum(gb, 4, 2);
+ index += 4;
+ context->index += 4;
+ } else {
+ enum_value = bits_offset_enum(gb, 6, 18);
+ index += 6;
+ context->index += 6;
+ }
+
+ if (checkIfValueInPrimaries(enum_value)) {
+ context->metadata.colour_encoding.primaries = enum_value;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ }
+ if (use_desc && context->metadata.colour_encoding.primaries == kCustomPrimary) {
+ // TODO -> Implement custom xy for red
+ }
+ if (use_desc && context->metadata.colour_encoding.primaries == kCustomPrimary) {
+ // TODO -> Implement custom xy for green
+ }
+ if (use_desc && context->metadata.colour_encoding.primaries == kCustomPrimary) {
+ // TODO -> Implement custom xy for blue
+ }
+ if (use_desc && not_xy) {
+ context->metadata.colour_encoding.have_gamma = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ }
+ if (use_desc && context->metadata.colour_encoding.have_gamma) {
+ get_bits_long(&gb, 24);
+ index += 24;
+ context->index += 24;
+ }
+ if (use_desc && !context->metadata.colour_encoding.have_gamma && not_xy) { // transfer_function enum
+ unsigned int input = get_bits(&gb, 2);
+ index += 2;
+ context->index += 2;
+ unsigned int enum_value;
+ if (input == 0) {
+ enum_value = 0;
+ } else if (input == 1) {
+ enum_value = 1;
+ } else if (input == 2) {
+ enum_value = bits_offset_enum(gb, 4, 2);
+ index += 4;
+ context->index += 4;
+ } else {
+ enum_value = bits_offset_enum(gb, 6, 18);
+ index += 6;
+ context->index += 6;
+ }
+
+ if (checkIfValueInTransferFunction(enum_value)) {
+ context->metadata.colour_encoding.transfer_function = enum_value;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ }
+ if (use_desc && context->metadata.colour_encoding.colour_space != kGrey && not_xy) { // rendering_intent enum
+ unsigned int input = get_bits(&gb, 2);
+ index += 2;
+ context->index += 2;
+ unsigned int enum_value;
+ if (input == 0) {
+ enum_value = 0;
+ } else if (input == 1) {
+ enum_value = 1;
+ } else if (input == 2) {
+ enum_value = bits_offset_enum(gb, 4, 2);
+ index += 4;
+ context->index += 4;
+ } else {
+ enum_value = bits_offset_enum(gb, 6, 18);
+ index += 6;
+ context->index += 6;
+ }
+
+ if (checkIfValueInRenderingIntent(enum_value)) {
+ context->metadata.colour_encoding.rendering_intent = enum_value;
+ } else {
+ // TODO -> Bitstream is ill formed
+ }
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.alpha_bits = 0;
+ } else if (input == 1) { // d1 = Val(8)
+ context->metadata.alpha_bits = 8;
+ } else if (input == 2) { // d2 = Val(16)
+ context->metadata.alpha_bits = 16;
+ } else { // d3 = Bits(4)
+ context->metadata.alpha_bits = get_bits(&gb, 4);
+ index += 4;
+ context->index += 4;
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(5)
+ context->metadata.target_nits_div50 = 5;
+ } else if (input == 1) { // d1 = Val(20)
+ context->metadata.target_nits_div50 = 20;
+ } else if (input == 2) { // d2 = Val(80)
+ context->metadata.target_nits_div50 = 80;
+ } else { // d3 = BitsOffset(10,1)
+ context->metadata.target_nits_div50 = get_bits(&gb, 10) + 1;
+ index += 10;
+ context->index += 10;
+ }
+
+ context->metadata.m2.all_default = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if (!context->metadata.m2.all_default) {
+ context->metadata.m2.have_preview = get_bits(&gb, 1);
+ index++;
+ context->index++;
+
+ context->metadata.m2.have_animation = get_bits(&gb, 1);
+ index++;
+ context->index++;
+
+ context->metadata.m2.orientation_minus_1 = get_bits(&gb, 3);
+ index +=3;
+ context->index +=3;
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.m2.depth_bits = 0;
+ } else if (input == 1) { // d1 = Val(8)
+ context->metadata.m2.depth_bits = 8;
+ } else if (input == 2) { // d2 = Val(16)
+ context->metadata.m2.depth_bits = 16;
+ } else { // d3 = Bits(4)
+ context->metadata.m2.depth_bits = get_bits(&gb, 4);
+ index += 4;
+ context->index += 4;
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.m2.depth_shift = 0;
+ } else if (input == 1) { // d1 = Val(3)
+ context->metadata.m2.depth_shift = 3;
+ } else if (input == 2) { // d2 = Val(4)
+ context->metadata.m2.depth_shift = 4;
+ } else { // d3 = BitsOffset(3,1)
+ context->metadata.m2.depth_shift = get_bits(&gb, 3) + 1;
+ index += 3;
+ context->index += 3;
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.m2.num_extra_channels = 0;
+ } else if (input == 1) { // d1 = Bits(4)
+ context->metadata.m2.num_extra_channels = get_bits(&gb, 4);
+ index += 4;
+ context->index += 4;
+ } else if (input == 2) { // d2 = BitsOffset(8,16)
+ context->metadata.m2.num_extra_channels = get_bits(&gb, 8) + 16;
+ index += 8;
+ context->index += 8;
+ } else { // d3 = BitsOffset(12,1)
+ context->metadata.m2.num_extra_channels = get_bits(&gb, 12) + 1;
+ index += 12;
+ context->index += 12;
+ }
+ }
+ if (context->metadata.m2.num_extra_channels > 0) {
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.m2.extra_channel_bits = 0;
+ } else if (input == 1) { // d1 = Val(8)
+ context->metadata.m2.extra_channel_bits = 8;
+ } else if (input == 2) { // d2 = Val(16)
+ context->metadata.m2.extra_channel_bits = 16;
+ } else { // d3 = Bits(4)
+ context->metadata.m2.extra_channel_bits = get_bits(&gb, 4);
+ index += 4;
+ context->index += 4;
+ }
+ }
+ context->metadata.m2.extra_channel_info = (ExtraChannelInfo*) malloc (context->metadata.m2.num_extra_channels * sizeof(ExtraChannelInfo));
+ for (int channel = 0; channel < context->metadata.m2.num_extra_channels; channel++) {
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->metadata.m2.extra_channel_info[channel].meaning = 0;
+ } else if (input == 1) { // d1 = Val(1)
+ context->metadata.m2.extra_channel_info[channel].meaning = 1;
+ } else if (input == 2) { // d2 = Val(2)
+ context->metadata.m2.extra_channel_info[channel].meaning = 2;
+ } else { // d3 = Bits(6)
+ context->metadata.m2.extra_channel_info[channel].meaning = get_bits(&gb, 6);
+ index += 6;
+ context->index += 6;
+ }
+
+ // default values
+ context->metadata.m2.extra_channel_info[channel].red = 0;
+ context->metadata.m2.extra_channel_info[channel].green = 0;
+ context->metadata.m2.extra_channel_info[channel].blue = 0;
+ context->metadata.m2.extra_channel_info[channel].solidity = 0;
+
+ if (context->metadata.m2.extra_channel_info[channel].meaning == 1) {
+ // TODO -> Implement f16() for blue, red, green, solidity
+ }
+ }
+ if (!context->metadata.m2.all_default) {
+ // TODO -> Implement extensions
+ }
+ }
+ context->state = JPEGXL_PREVIEW_HEADER;
+ } else if (context->state == JPEGXL_PREVIEW_HEADER) {
+ // default values
+ context->preview.xsize_minus_1 = 0;
+ context->preview.xsize_div8_minus_1 = 0;
+ context->preview.ysize_minus_1 = 0;
+ context->preview.ysize_div8_minus_1 = 0;
+
+ if (!context->metadata.m2.have_preview) {
+ context->state = JPEGXL_ANIMATION_HEADER;
+ continue;
+ }
+
+ unsigned int div8 = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if (div8) {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(15)
+ context->preview.ysize_div8_minus_1 = 15;
+ } else if (input == 1) { // d1 = Val(31)
+ context->preview.ysize_div8_minus_1 = 31;
+ } else if (input == 2) { // d2 = Bits(5)
+ context->preview.ysize_div8_minus_1 = get_bits(&gb, 5);
+ index += 5;
+ context->index += 5;
+ } else { // d3 = BitsOffset(9,32)
+ context->preview.ysize_div8_minus_1 = get_bits(&gb, 9) + 32;
+ index += 9;
+ context->index += 9;
+ }
+ } else {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Bits(6)
+ context->preview.ysize_minus_1 = get_bits(&gb, 6);
+ index += 6;
+ context->index += 6;
+ } else if (input == 1) { // d1 = BitsOffset(8,64)
+ context->preview.ysize_minus_1 = get_bits(&gb, 8) + 64;
+ index += 8;
+ context->index += 8;
+ } else if (input == 2) { // d2 = BitsOffset(10,320)
+ context->preview.ysize_minus_1 = get_bits(&gb, 10) + 320;
+ index += 10;
+ context->index += 10;
+ } else { // d3 = BitsOffset(12,1344)
+ context->preview.ysize_minus_1 = get_bits(&gb, 12) + 1344;
+ index += 12;
+ context->index += 12;
+ }
+ }
+ unsigned int ratio = get_bits(&gb, 3);
+ index += 3;
+ context->index += 3;
+ if (ratio == 0) {
+ if (div8) {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(15)
+ context->preview.xsize_div8_minus_1 = 15;
+ } else if (input == 1) { // d1 = Val(31)
+ context->preview.xsize_div8_minus_1 = 31;
+ } else if (input == 2) { // d2 = Bits(5)
+ context->preview.xsize_div8_minus_1 = get_bits(&gb, 5);
+ index += 5;
+ context->index += 5;
+ } else { // d3 = BitsOffset(9,32)
+ context->preview.xsize_div8_minus_1 = get_bits(&gb, 9) + 32;
+ index += 9;
+ context->index += 9;
+ }
+ } else {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Bits(6)
+ context->preview.xsize_minus_1 = get_bits(&gb, 6);
+ index += 6;
+ context->index += 6;
+ } else if (input == 1) { // d1 = BitsOffset(8,64)
+ context->preview.xsize_minus_1 = get_bits(&gb, 8) + 64;
+ index += 8;
+ context->index += 8;
+ } else if (input == 2) { // d2 = BitsOffset(10,320)
+ context->preview.xsize_minus_1 = get_bits(&gb, 10) + 320;
+ index += 10;
+ context->index += 10;
+ } else { // d3 = BitsOffset(12,1344)
+ context->preview.xsize_minus_1 = get_bits(&gb, 12) + 1344;
+ index += 12;
+ context->index += 12;
+ }
+ }
+ }
+ context->state = JPEGXL_ANIMATION_HEADER;
+ } else if (context->state == JPEGXL_ANIMATION_HEADER) {
+ // default values
+ context->animation.num_loops = 0;
+ context->animation.tps_denominator_minus_1 = 0;
+ context->animation.tps_numerator_minus_1 = 0;
+ context->animation.have_timecodes = 0;
+
+ if (!context->metadata.m2.have_animation) {
+ context->state = JPEGXL_ICC_CODEC;
+ continue;
+ }
+
+ context->animation.composite_still = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ if (!context->animation.composite_still) {
+ unsigned int input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(99)
+ context->animation.tps_numerator_minus_1 = 99;
+ } else if (input == 1) { // d1 = Val(999)
+ context->animation.tps_numerator_minus_1 = 999;
+ } else if (input == 2) { // d2 = Bits(6)
+ context->animation.tps_numerator_minus_1 = get_bits(&gb, 6);
+ index += 6;
+ context->index += 6;
+ } else { // d3 = Bits(18)
+ context->animation.tps_numerator_minus_1 = get_bits_long(&gb, 18);
+ index += 18;
+ context->index += 18;
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->animation.tps_denominator_minus_1 = 0;
+ } else if (input == 1) { // d1 = Val(1000)
+ context->animation.tps_denominator_minus_1 = 1000;
+ } else if (input == 2) { // d2 = Bits(8)
+ context->animation.tps_denominator_minus_1 = get_bits(&gb, 8);
+ index += 8;
+ context->index += 8;
+ } else { // d3 = Bits(10)
+ context->animation.tps_denominator_minus_1 = get_bits_long(&gb, 10);
+ index += 10;
+ context->index += 10;
+ }
+
+ input = get_bits(&gb, 2); // U32() first reads 2 bits
+ index += 2;
+ context->index += 2;
+ if (input == 0) { // d0 = Val(0)
+ context->animation.num_loops = 0;
+ } else if (input == 1) { // d1 = Bits(3)
+ context->animation.num_loops = get_bits(&gb, 3);
+ index += 3;
+ context->index += 3;
+ } else if (input == 2) { // d2 = Bits(16)
+ context->animation.num_loops = get_bits(&gb, 16);
+ index += 16;
+ context->index += 16;
+ } else { // d3 = Bits(32)
+ context->animation.num_loops = get_bits_long(&gb, 32);
+ index += 32;
+ context->index += 32;
+ }
+
+ context->animation.have_timecodes = get_bits(&gb, 1);
+ index++;
+ context->index++;
+ }
+ context->state = JPEGXL_ICC_CODEC;
+ } else if (context->state == JPEGXL_ICC_CODEC) {
+ if (!context->metadata.have_icc) {
+ context->state = JPEGXL_PREVIEW_FRAME;
+ continue;
+ }
+ // TODO -> Handle ICC profile
+ } else if (context->state == JPEGXL_PREVIEW_FRAME) {
+ // TODO
+ } else if (context->state == JPEGXL_FRAMES) {
+ //TODO
+ }
+ }
+
+ return next;
+}
+
+static int jpegxl_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+ const uint8_t **poutbuf, int *poutbuf_size,
+ const uint8_t *buf, int buf_size) {
+ JPEGXLParseContext *context = s->priv_data;
+ int next;
+
+ next = jpegxl_find_frame_end(context, buf, buf_size);
+ if (ff_combine_frame(&context->pc, next, &buf, &buf_size) < 0) {
+ *poutbuf = NULL;
+ *poutbuf_size = 0;
+ return buf_size;
+ }
+
+ *poutbuf = buf;
+ *poutbuf_size = buf_size;
+ return next;
+}
+
+AVCodecParser ff_jpegxl_parser = {
+ .codec_ids = { AV_CODEC_ID_JPEGXL },
+ .priv_data_size = sizeof(JPEGXLParseContext),
+ .parser_parse = jpegxl_parse,
+ .parser_close = ff_parse_close,
+};
\ No newline at end of file
--
2.25.0
More information about the ffmpeg-devel
mailing list