FFmpeg
jpegxl_probe.c
Go to the documentation of this file.
1 /*
2  * Jpeg XL header verification
3  * Copyright (c) 2022 Leo Izen <leo.izen@gmail.com>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "jpegxl_probe.h"
23 
24 #define BITSTREAM_READER_LE
25 #include "libavcodec/get_bits.h"
26 
37 };
38 
44 };
45 
51 };
52 
58 };
59 
60 #define jxl_bits(n) get_bits_long(gb, (n))
61 #define jxl_bits_skip(n) skip_bits_long(gb, (n))
62 #define jxl_u32(c0, c1, c2, c3, u0, u1, u2, u3) jpegxl_u32(gb, \
63  (const uint32_t[]){c0, c1, c2, c3}, (const uint32_t[]){u0, u1, u2, u3})
64 #define jxl_u64() jpegxl_u64(gb)
65 #define jxl_enum() jxl_u32(0, 1, 2, 18, 0, 0, 4, 6)
66 
67 /* read a U32(c_i + u(u_i)) */
68 static uint32_t jpegxl_u32(GetBitContext *gb,
69  const uint32_t constants[4], const uint32_t ubits[4])
70 {
71  uint32_t ret, choice = jxl_bits(2);
72 
73  ret = constants[choice];
74  if (ubits[choice])
75  ret += jxl_bits(ubits[choice]);
76 
77  return ret;
78 }
79 
80 /* read a U64() */
81 static uint64_t jpegxl_u64(GetBitContext *gb)
82 {
83  uint64_t shift = 12, ret;
84 
85  switch (jxl_bits(2)) {
86  case 0:
87  ret = 0;
88  break;
89  case 1:
90  ret = 1 + jxl_bits(4);
91  break;
92  case 2:
93  ret = 17 + jxl_bits(8);
94  break;
95  case 3:
96  ret = jxl_bits(12);
97  while (jxl_bits(1)) {
98  if (shift < 60) {
99  ret |= (uint64_t)jxl_bits(8) << shift;
100  shift += 8;
101  } else {
102  ret |= (uint64_t)jxl_bits(4) << shift;
103  break;
104  }
105  }
106  break;
107  }
108 
109  return ret;
110 }
111 
112 static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio)
113 {
114  uint64_t height64 = height; /* avoid integer overflow */
115  switch (ratio) {
116  case 1:
117  return height;
118  case 2:
119  return (uint32_t)((height64 * 12) / 10);
120  case 3:
121  return (uint32_t)((height64 * 4) / 3);
122  case 4:
123  return (uint32_t)((height64 * 3) / 2);
124  case 5:
125  return (uint32_t)((height64 * 16) / 9);
126  case 6:
127  return (uint32_t)((height64 * 5) / 4);
128  case 7:
129  return (uint32_t)(height64 * 2);
130  default:
131  break;
132  }
133 
134  return 0; /* manual width */
135 }
136 
137 /**
138  * validate a Jpeg XL Size Header
139  * @return >= 0 upon valid size, < 0 upon invalid size found
140  */
142 {
143  uint32_t width, height;
144 
145  if (jxl_bits(1)) {
146  /* small size header */
147  height = (jxl_bits(5) + 1) << 3;
149  if (!width)
150  width = (jxl_bits(5) + 1) << 3;
151  } else {
152  /* large size header */
153  height = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30);
155  if (!width)
156  width = 1 + jxl_u32(0, 0, 0, 0, 9, 13, 18, 30);
157  }
158  if (width > (1 << 18) || height > (1 << 18)
159  || (width >> 4) * (height >> 4) > (1 << 20))
160  return -1;
161 
162  return 0;
163 }
164 
165 /**
166  * validate a Jpeg XL Preview Header
167  * @return >= 0 upon valid size, < 0 upon invalid size found
168  */
170 {
171  uint32_t width, height;
172 
173  if (jxl_bits(1)) {
174  /* coded height and width divided by eight */
175  height = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3;
177  if (!width)
178  width = jxl_u32(16, 32, 1, 33, 0, 0, 5, 9) << 3;
179  } else {
180  /* full height and width coded */
181  height = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12);
183  if (!width)
184  width = jxl_u32(1, 65, 321, 1345, 6, 8, 10, 12);
185  }
186  if (width > 4096 || height > 4096)
187  return -1;
188 
189  return 0;
190 }
191 
192 /**
193  * skip a Jpeg XL BitDepth Header. These cannot be invalid.
194  */
196 {
197  if (jxl_bits(1)) {
198  /* float samples */
199  jxl_u32(32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */
200  jxl_bits_skip(4); /* exponent */
201  } else {
202  /* integer samples */
203  jxl_u32(8, 10, 12, 1, 0, 0, 0, 6);
204  }
205 }
206 
207 /**
208  * validate a Jpeg XL Extra Channel Info bundle
209  * @return >= 0 upon valid, < 0 upon invalid
210  */
211 static int jpegxl_read_extra_channel_info(GetBitContext *gb, int validate_level)
212 {
213  int all_default = jxl_bits(1);
214  uint32_t type, name_len = 0;
215 
216  if (!all_default) {
217  type = jxl_enum();
218  if (type > 63)
219  return -1; /* enum types cannot be 64+ */
220  if (type == FF_JPEGXL_CT_BLACK && validate_level)
221  return -1;
223  jxl_u32(0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */
224  /* max of name_len is 1071 = 48 + 2^10 - 1 */
225  name_len = jxl_u32(0, 0, 16, 48, 0, 4, 5, 10);
226  } else {
228  }
229 
230  /* skip over the name */
231  jxl_bits_skip(8 * name_len);
232 
233  if (!all_default && type == FF_JPEGXL_CT_ALPHA)
234  jxl_bits_skip(1);
235 
237  jxl_bits_skip(16 * 4);
238 
239  if (type == FF_JPEGXL_CT_CFA)
240  jxl_u32(1, 0, 3, 19, 0, 2, 4, 8);
241 
242  return 0;
243 }
244 
245 int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen, int validate_level)
246 {
247  GetBitContext gbi, *gb = &gbi;
248  int all_default, extra_fields = 0;
249  int xyb_encoded = 1, have_icc_profile = 0;
250  int animation_offset = 0;
251  uint32_t num_extra_channels;
252  uint64_t extensions;
253  int ret;
254 
255  ret = init_get_bits8(gb, buf, buflen);
256  if (ret < 0)
257  return ret;
258 
260  return -1;
261 
262  if (jpegxl_read_size_header(gb) < 0 && validate_level)
263  return -1;
264 
265  all_default = jxl_bits(1);
266  if (!all_default)
267  extra_fields = jxl_bits(1);
268 
269  if (extra_fields) {
270  jxl_bits_skip(3); /* orientation */
271 
272  /*
273  * intrinstic size
274  * any size header here is valid, but as it
275  * is variable length we have to read it
276  */
277  if (jxl_bits(1))
279 
280  /* preview header */
281  if (jxl_bits(1)) {
282  if (jpegxl_read_preview_header(gb) < 0)
283  return -1;
284  }
285 
286  /* animation header */
287  if (jxl_bits(1)) {
288  animation_offset = get_bits_count(gb);
289  jxl_u32(100, 1000, 1, 1, 0, 0, 10, 30);
290  jxl_u32(1, 1001, 1, 1, 0, 0, 8, 10);
291  jxl_u32(0, 0, 0, 0, 0, 3, 16, 32);
292  jxl_bits_skip(1);
293  }
294  }
295 
296  if (!all_default) {
298 
299  /* modular_16bit_buffers must equal 1 */
300  if (!jxl_bits(1) && validate_level)
301  return -1;
302 
303  num_extra_channels = jxl_u32(0, 1, 2, 1, 0, 0, 4, 12);
304  if (num_extra_channels > 4 && validate_level)
305  return -1;
306  for (uint32_t i = 0; i < num_extra_channels; i++) {
307  if (jpegxl_read_extra_channel_info(gb, validate_level) < 0)
308  return -1;
309  }
310 
311  xyb_encoded = jxl_bits(1);
312 
313  /* color encoding bundle */
314  if (!jxl_bits(1)) {
315  uint32_t color_space;
316  have_icc_profile = jxl_bits(1);
317  color_space = jxl_enum();
318  if (color_space > 63)
319  return -1;
320 
321  if (!have_icc_profile) {
322  if (color_space != FF_JPEGXL_CS_XYB) {
323  uint32_t white_point = jxl_enum();
324  if (white_point > 63)
325  return -1;
326  if (white_point == FF_JPEGXL_WP_CUSTOM) {
327  /* ux and uy values */
328  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
329  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
330  }
331  if (color_space != FF_JPEGXL_CS_GRAY) {
332  /* primaries */
333  uint32_t primaries = jxl_enum();
334  if (primaries > 63)
335  return -1;
337  /* ux/uy values for r,g,b */
338  for (int i = 0; i < 6; i++)
339  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
340  }
341  }
342  }
343 
344  /* transfer characteristics */
345  if (jxl_bits(1)) {
346  /* gamma */
347  jxl_bits_skip(24);
348  } else {
349  /* transfer function */
350  if (jxl_enum() > 63)
351  return -1;
352  }
353 
354  /* rendering intent */
355  if (jxl_enum() > 63)
356  return -1;
357  }
358  }
359 
360  /* tone mapping bundle */
361  if (extra_fields && !jxl_bits(1))
362  jxl_bits_skip(16 + 16 + 1 + 16);
363 
364  extensions = jxl_u64();
365  if (extensions) {
366  for (int i = 0; i < 64; i++) {
367  if (extensions & (UINT64_C(1) << i))
368  jxl_u64();
369  }
370  }
371  }
372 
373  /* default transform */
374  if (!jxl_bits(1)) {
375  /* opsin inverse matrix */
376  if (xyb_encoded && !jxl_bits(1))
377  jxl_bits_skip(16 * 16);
378  /* cw_mask and default weights */
379  if (jxl_bits(1))
380  jxl_bits_skip(16 * 15);
381  if (jxl_bits(1))
382  jxl_bits_skip(16 * 55);
383  if (jxl_bits(1))
384  jxl_bits_skip(16 * 210);
385  }
386 
387  if (!have_icc_profile) {
388  int bits_remaining = 7 - (get_bits_count(gb) - 1) % 8;
389  if (bits_remaining && jxl_bits(bits_remaining))
390  return -1;
391  }
392 
393  if (get_bits_left(gb) < 0)
394  return -1;
395 
396  return animation_offset;
397 }
JpegXLColorSpace
JpegXLColorSpace
Definition: jpegxl_probe.c:39
FF_JPEGXL_PR_SRGB
@ FF_JPEGXL_PR_SRGB
Definition: jpegxl_probe.c:54
get_bits_left
static int get_bits_left(GetBitContext *gb)
Definition: get_bits.h:664
FF_JPEGXL_CS_XYB
@ FF_JPEGXL_CS_XYB
Definition: jpegxl_probe.c:42
jpegxl_u64
static uint64_t jpegxl_u64(GetBitContext *gb)
Definition: jpegxl_probe.c:81
FF_JPEGXL_PR_P3
@ FF_JPEGXL_PR_P3
Definition: jpegxl_probe.c:57
get_bits_count
static int get_bits_count(const GetBitContext *s)
Definition: get_bits.h:256
JpegXLExtraChannelType
JpegXLExtraChannelType
Definition: jpegxl_probe.c:27
FF_JPEGXL_CS_UNKNOWN
@ FF_JPEGXL_CS_UNKNOWN
Definition: jpegxl_probe.c:43
JpegXLWhitePoint
JpegXLWhitePoint
Definition: jpegxl_probe.c:46
FF_JPEGXL_CT_THERMAL
@ FF_JPEGXL_CT_THERMAL
Definition: jpegxl_probe.c:34
JpegXLPrimaries
JpegXLPrimaries
Definition: jpegxl_probe.c:53
jxl_u64
#define jxl_u64()
Definition: jpegxl_probe.c:64
jpegxl_u32
static uint32_t jpegxl_u32(GetBitContext *gb, const uint32_t constants[4], const uint32_t ubits[4])
Definition: jpegxl_probe.c:68
jpegxl_probe.h
primaries
enum AVColorPrimaries primaries
Definition: mediacodec_wrapper.c:2664
jpegxl_skip_bit_depth
static void jpegxl_skip_bit_depth(GetBitContext *gb)
skip a Jpeg XL BitDepth Header.
Definition: jpegxl_probe.c:195
FF_JPEGXL_CT_DEPTH
@ FF_JPEGXL_CT_DEPTH
Definition: jpegxl_probe.c:29
FF_JPEGXL_CT_BLACK
@ FF_JPEGXL_CT_BLACK
Definition: jpegxl_probe.c:32
GetBitContext
Definition: get_bits.h:107
FF_JPEGXL_CS_RGB
@ FF_JPEGXL_CS_RGB
Definition: jpegxl_probe.c:40
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
FF_JPEGXL_CT_SPOT_COLOR
@ FF_JPEGXL_CT_SPOT_COLOR
Definition: jpegxl_probe.c:30
init_get_bits8
static int init_get_bits8(GetBitContext *s, const uint8_t *buffer, int byte_size)
Initialize GetBitContext.
Definition: get_bits.h:524
width
#define width
FF_JPEGXL_CT_ALPHA
@ FF_JPEGXL_CT_ALPHA
Definition: jpegxl_probe.c:28
ff_jpegxl_verify_codestream_header
int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen, int validate_level)
verify that a codestream header is valid
Definition: jpegxl_probe.c:245
get_bits.h
jxl_bits
#define jxl_bits(n)
Definition: jpegxl_probe.c:60
FF_JPEGXL_CT_CFA
@ FF_JPEGXL_CT_CFA
Definition: jpegxl_probe.c:33
FF_JPEGXL_WP_CUSTOM
@ FF_JPEGXL_WP_CUSTOM
Definition: jpegxl_probe.c:48
FF_JPEGXL_WP_D65
@ FF_JPEGXL_WP_D65
Definition: jpegxl_probe.c:47
shift
static int shift(int a, int b)
Definition: bonk.c:262
FF_JPEGXL_CT_SELECTION_MASK
@ FF_JPEGXL_CT_SELECTION_MASK
Definition: jpegxl_probe.c:31
height
#define height
constants
static const struct @323 constants[]
FF_JPEGXL_CT_OPTIONAL
@ FF_JPEGXL_CT_OPTIONAL
Definition: jpegxl_probe.c:36
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
FF_JPEGXL_WP_E
@ FF_JPEGXL_WP_E
Definition: jpegxl_probe.c:49
jpegxl_read_extra_channel_info
static int jpegxl_read_extra_channel_info(GetBitContext *gb, int validate_level)
validate a Jpeg XL Extra Channel Info bundle
Definition: jpegxl_probe.c:211
jxl_enum
#define jxl_enum()
Definition: jpegxl_probe.c:65
jpegxl_read_size_header
static int jpegxl_read_size_header(GetBitContext *gb)
validate a Jpeg XL Size Header
Definition: jpegxl_probe.c:141
FF_JPEGXL_PR_CUSTOM
@ FF_JPEGXL_PR_CUSTOM
Definition: jpegxl_probe.c:55
FF_JPEGXL_PR_2100
@ FF_JPEGXL_PR_2100
Definition: jpegxl_probe.c:56
ret
ret
Definition: filter_design.txt:187
FF_JPEGXL_CODESTREAM_SIGNATURE_LE
#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE
Definition: jpegxl_probe.h:27
jpegxl_read_preview_header
static int jpegxl_read_preview_header(GetBitContext *gb)
validate a Jpeg XL Preview Header
Definition: jpegxl_probe.c:169
jxl_u32
#define jxl_u32(c0, c1, c2, c3, u0, u1, u2, u3)
Definition: jpegxl_probe.c:62
jpegxl_width_from_ratio
static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio)
Definition: jpegxl_probe.c:112
FF_JPEGXL_CS_GRAY
@ FF_JPEGXL_CS_GRAY
Definition: jpegxl_probe.c:41
FF_JPEGXL_CT_NON_OPTIONAL
@ FF_JPEGXL_CT_NON_OPTIONAL
Definition: jpegxl_probe.c:35
FF_JPEGXL_WP_DCI
@ FF_JPEGXL_WP_DCI
Definition: jpegxl_probe.c:50
jxl_bits_skip
#define jxl_bits_skip(n)
Definition: jpegxl_probe.c:61