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  */
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)
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 /* verify that a codestream header is valid */
246 int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen)
247 {
248  GetBitContext gbi, *gb = &gbi;
249  int all_default, extra_fields = 0;
250  int xyb_encoded = 1, have_icc_profile = 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)
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  jxl_u32(100, 1000, 1, 1, 0, 0, 10, 30);
289  jxl_u32(1, 1001, 1, 1, 0, 0, 8, 10);
290  jxl_u32(0, 0, 0, 0, 0, 3, 16, 32);
291  jxl_bits_skip(1);
292  }
293  }
294 
295  if (!all_default) {
297 
298  /* modular_16bit_buffers must equal 1 */
299  if (!jxl_bits(1))
300  return -1;
301 
302  num_extra_channels = jxl_u32(0, 1, 2, 1, 0, 0, 4, 12);
303  if (num_extra_channels > 4)
304  return -1;
305  for (uint32_t i = 0; i < num_extra_channels; i++) {
306  if (jpegxl_read_extra_channel_info(gb) < 0)
307  return -1;
308  }
309 
310  xyb_encoded = jxl_bits(1);
311 
312  /* color encoding bundle */
313  if (!jxl_bits(1)) {
314  uint32_t color_space;
315  have_icc_profile = jxl_bits(1);
316  color_space = jxl_enum();
317  if (color_space > 63)
318  return -1;
319 
320  if (!have_icc_profile) {
321  if (color_space != FF_JPEGXL_CS_XYB) {
322  uint32_t white_point = jxl_enum();
323  if (white_point > 63)
324  return -1;
325  if (white_point == FF_JPEGXL_WP_CUSTOM) {
326  /* ux and uy values */
327  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
328  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
329  }
330  if (color_space != FF_JPEGXL_CS_GRAY) {
331  /* primaries */
332  uint32_t primaries = jxl_enum();
333  if (primaries > 63)
334  return -1;
335  if (primaries == FF_JPEGXL_PR_CUSTOM) {
336  /* ux/uy values for r,g,b */
337  for (int i = 0; i < 6; i++)
338  jxl_u32(0, 524288, 1048576, 2097152, 19, 19, 20, 21);
339  }
340  }
341  }
342 
343  /* transfer characteristics */
344  if (jxl_bits(1)) {
345  /* gamma */
346  jxl_bits_skip(24);
347  } else {
348  /* transfer function */
349  if (jxl_enum() > 63)
350  return -1;
351  }
352 
353  /* rendering intent */
354  if (jxl_enum() > 63)
355  return -1;
356  }
357  }
358 
359  /* tone mapping bundle */
360  if (extra_fields && !jxl_bits(1))
361  jxl_bits_skip(16 + 16 + 1 + 16);
362 
363  extensions = jxl_u64();
364  if (extensions) {
365  for (int i = 0; i < 64; i++) {
366  if (extensions & (UINT64_C(1) << i))
367  jxl_u64();
368  }
369  }
370  }
371 
372  /* default transform */
373  if (!jxl_bits(1)) {
374  /* opsin inverse matrix */
375  if (xyb_encoded && !jxl_bits(1))
376  jxl_bits_skip(16 * 16);
377  /* cw_mask and default weights */
378  if (jxl_bits(1))
379  jxl_bits_skip(16 * 15);
380  if (jxl_bits(1))
381  jxl_bits_skip(16 * 55);
382  if (jxl_bits(1))
383  jxl_bits_skip(16 * 210);
384  }
385 
386  if (!have_icc_profile) {
387  int bits_remaining = 7 - (get_bits_count(gb) - 1) % 8;
388  if (bits_remaining && jxl_bits(bits_remaining))
389  return -1;
390  }
391 
392  if (get_bits_left(gb) < 0)
393  return -1;
394 
395  return 0;
396 }
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:839
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:219
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
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:61
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:667
width
#define width
FF_JPEGXL_CT_ALPHA
@ FF_JPEGXL_CT_ALPHA
Definition: jpegxl_probe.c:28
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:253
FF_JPEGXL_CT_SELECTION_MASK
@ FF_JPEGXL_CT_SELECTION_MASK
Definition: jpegxl_probe.c:31
height
#define height
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
jxl_enum
#define jxl_enum()
Definition: jpegxl_probe.c:65
ff_jpegxl_verify_codestream_header
int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen)
Definition: jpegxl_probe.c:246
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
constants
static const struct @315 constants[]
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_read_extra_channel_info
static int jpegxl_read_extra_channel_info(GetBitContext *gb)
validate a Jpeg XL Extra Channel Info bundle
Definition: jpegxl_probe.c:211
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