/src/libheif/libheif/codecs/avif_boxes.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com> |
4 | | * |
5 | | * This file is part of libheif. |
6 | | * |
7 | | * libheif is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation, either version 3 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * libheif 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 |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libheif. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "pixelimage.h" |
22 | | #include "avif_boxes.h" |
23 | | #include "avif_dec.h" |
24 | | #include "bitstream.h" |
25 | | #include "common_utils.h" |
26 | | #include "libheif/api_structs.h" |
27 | | #include "file.h" |
28 | | #include <iomanip> |
29 | | #include <limits> |
30 | | #include <string> |
31 | | #include <cstring> |
32 | | |
33 | | // https://aomediacodec.github.io/av1-spec/av1-spec.pdf |
34 | | |
35 | | |
36 | | Error Box_av1C::parse(BitstreamRange& range, const heif_security_limits* limits) |
37 | 1.30k | { |
38 | | //parse_full_box_header(range); |
39 | | |
40 | 1.30k | if (!has_fixed_box_size()) { |
41 | | // Note: in theory, it is allowed to have an av1C box with unspecified size (until the end of the file), |
42 | | // but that would be very uncommon and give us problems in the calculation of `configOBUs_bytes` below. |
43 | | // It's better to error on this case than to open a DoS vulnerability. |
44 | 2 | return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "av1C with unspecified box size"}; |
45 | 2 | } |
46 | | |
47 | 1.29k | uint8_t byte; |
48 | | |
49 | 1.29k | auto& c = m_configuration; // abbreviation |
50 | | |
51 | 1.29k | byte = range.read8(); |
52 | 1.29k | if ((byte & 0x80) == 0) { |
53 | | // error: marker bit not set |
54 | 52 | } |
55 | | |
56 | 1.29k | c.version = byte & 0x7F; |
57 | | |
58 | 1.29k | byte = range.read8(); |
59 | 1.29k | c.seq_profile = (byte >> 5) & 0x7; |
60 | 1.29k | c.seq_level_idx_0 = byte & 0x1f; |
61 | | |
62 | 1.29k | byte = range.read8(); |
63 | 1.29k | c.seq_tier_0 = (byte >> 7) & 1; |
64 | 1.29k | c.high_bitdepth = (byte >> 6) & 1; |
65 | 1.29k | c.twelve_bit = (byte >> 5) & 1; |
66 | 1.29k | c.monochrome = (byte >> 4) & 1; |
67 | 1.29k | c.chroma_subsampling_x = (byte >> 3) & 1; |
68 | 1.29k | c.chroma_subsampling_y = (byte >> 2) & 1; |
69 | 1.29k | c.chroma_sample_position = byte & 3; |
70 | | |
71 | 1.29k | byte = range.read8(); |
72 | 1.29k | c.initial_presentation_delay_present = (byte >> 4) & 1; |
73 | 1.29k | if (c.initial_presentation_delay_present) { |
74 | 41 | c.initial_presentation_delay_minus_one = byte & 0x0F; |
75 | 41 | } |
76 | | |
77 | 1.29k | const size_t configOBUs_bytes = range.get_remaining_bytes(); |
78 | 1.29k | m_config_OBUs.resize(configOBUs_bytes); |
79 | | |
80 | 1.29k | if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { |
81 | | // error |
82 | 0 | } |
83 | | |
84 | 1.29k | return range.get_error(); |
85 | 1.30k | } |
86 | | |
87 | | |
88 | | Error Box_av1C::write(StreamWriter& writer) const |
89 | 0 | { |
90 | 0 | size_t box_start = reserve_box_header_space(writer); |
91 | |
|
92 | 0 | const auto& c = m_configuration; // abbreviation |
93 | |
|
94 | 0 | writer.write8(c.version | 0x80); |
95 | |
|
96 | 0 | writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | |
97 | 0 | (c.seq_level_idx_0 & 0x1f))); |
98 | |
|
99 | 0 | writer.write8((uint8_t) ((c.seq_tier_0 ? 0x80 : 0) | |
100 | 0 | (c.high_bitdepth ? 0x40 : 0) | |
101 | 0 | (c.twelve_bit ? 0x20 : 0) | |
102 | 0 | (c.monochrome ? 0x10 : 0) | |
103 | 0 | (c.chroma_subsampling_x ? 0x08 : 0) | |
104 | 0 | (c.chroma_subsampling_y ? 0x04 : 0) | |
105 | 0 | (c.chroma_sample_position & 0x03))); |
106 | |
|
107 | 0 | writer.write8(0); // TODO initial_presentation_delay |
108 | |
|
109 | 0 | prepend_header(writer, box_start); |
110 | |
|
111 | 0 | return Error::Ok; |
112 | 0 | } |
113 | | |
114 | | |
115 | | std::string Box_av1C::dump(Indent& indent) const |
116 | 0 | { |
117 | 0 | std::ostringstream sstr; |
118 | 0 | sstr << Box::dump(indent); |
119 | |
|
120 | 0 | const auto& c = m_configuration; // abbreviation |
121 | |
|
122 | 0 | sstr << indent << "version: " << ((int) c.version) << "\n" |
123 | 0 | << indent << "seq_profile: " << ((int) c.seq_profile) << "\n" |
124 | 0 | << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n" |
125 | 0 | << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n" |
126 | 0 | << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n" |
127 | 0 | << indent << "monochrome: " << ((int) c.monochrome) << "\n" |
128 | 0 | << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n" |
129 | 0 | << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n" |
130 | 0 | << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n" |
131 | 0 | << indent << "initial_presentation_delay: "; |
132 | |
|
133 | 0 | if (c.initial_presentation_delay_present) { |
134 | 0 | sstr << c.initial_presentation_delay_minus_one + 1 << "\n"; |
135 | 0 | } |
136 | 0 | else { |
137 | 0 | sstr << "not present\n"; |
138 | 0 | } |
139 | |
|
140 | 0 | sstr << indent << "config OBUs:"; |
141 | 0 | for (size_t i = 0; i < m_config_OBUs.size(); i++) { |
142 | 0 | sstr << " " << std::hex << std::setfill('0') << std::setw(2) |
143 | 0 | << ((int) m_config_OBUs[i]); |
144 | 0 | } |
145 | 0 | sstr << std::dec << "\n"; |
146 | |
|
147 | 0 | return sstr.str(); |
148 | 0 | } |
149 | | |
150 | | |
151 | | Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std::shared_ptr<HeifPixelImage>& image) |
152 | 0 | { |
153 | 0 | int bpp = image->get_bits_per_pixel(heif_channel_Y); |
154 | 0 | heif_chroma chroma = image->get_chroma_format(); |
155 | |
|
156 | 0 | uint8_t profile = compute_avif_profile(bpp, chroma); |
157 | |
|
158 | 0 | int width = image->get_width(heif_channel_Y); |
159 | 0 | int height = image->get_height(heif_channel_Y); |
160 | |
|
161 | 0 | uint8_t level; |
162 | |
|
163 | 0 | if (width <= 8192 && height <= 4352 && (width * height) <= 8912896) { |
164 | 0 | level = 13; // 5.1 |
165 | 0 | } |
166 | 0 | else if (width <= 16384 && height <= 8704 && (width * height) <= 35651584) { |
167 | 0 | level = 17; // 6.1 |
168 | 0 | } |
169 | 0 | else { |
170 | 0 | level = 31; // maximum |
171 | 0 | } |
172 | |
|
173 | 0 | inout_config->seq_profile = profile; |
174 | 0 | inout_config->seq_level_idx_0 = level; |
175 | 0 | inout_config->high_bitdepth = (bpp > 8) ? 1 : 0; |
176 | 0 | inout_config->twelve_bit = (bpp >= 12) ? 1 : 0; |
177 | 0 | inout_config->monochrome = (chroma == heif_chroma_monochrome) ? 1 : 0; |
178 | 0 | inout_config->chroma_subsampling_x = uint8_t(chroma_h_subsampling(chroma) >> 1); |
179 | 0 | inout_config->chroma_subsampling_y = uint8_t(chroma_v_subsampling(chroma) >> 1); |
180 | | |
181 | | // 0 - CSP_UNKNOWN |
182 | | // 1 - CSP_VERTICAL |
183 | | // 2 - CSP_COLOCATED |
184 | | // 3 - CSP_RESERVED |
185 | |
|
186 | 0 | inout_config->chroma_sample_position = (chroma == heif_chroma_420 ? 0 : 2); |
187 | | |
188 | |
|
189 | 0 | return Error::Ok; |
190 | 0 | } |
191 | | |
192 | | |
193 | | Error Box_a1op::parse(BitstreamRange& range, const heif_security_limits* limits) |
194 | 8 | { |
195 | 8 | op_index = range.read8(); |
196 | | |
197 | 8 | return range.get_error(); |
198 | 8 | } |
199 | | |
200 | | |
201 | | std::string Box_a1op::dump(Indent& indent) const |
202 | 0 | { |
203 | 0 | std::ostringstream sstr; |
204 | 0 | sstr << Box::dump(indent); |
205 | |
|
206 | 0 | sstr << indent << "op-index: " << ((int) op_index) << "\n"; |
207 | |
|
208 | 0 | return sstr.str(); |
209 | 0 | } |
210 | | |
211 | | |
212 | | Error Box_a1op::write(StreamWriter& writer) const |
213 | 0 | { |
214 | 0 | size_t box_start = reserve_box_header_space(writer); |
215 | |
|
216 | 0 | writer.write8(op_index); |
217 | |
|
218 | 0 | prepend_header(writer, box_start); |
219 | |
|
220 | 0 | return Error::Ok; |
221 | 0 | } |
222 | | |
223 | | |
224 | | Error Box_a1lx::parse(BitstreamRange& range, const heif_security_limits* limits) |
225 | 38 | { |
226 | 38 | uint8_t flags = range.read8(); |
227 | | |
228 | 152 | for (int i = 0; i < 3; i++) { |
229 | 114 | if (flags & 1) { |
230 | 33 | layer_size[i] = range.read32(); |
231 | 33 | } |
232 | 81 | else { |
233 | 81 | layer_size[i] = range.read16(); |
234 | 81 | } |
235 | 114 | } |
236 | | |
237 | 38 | return range.get_error(); |
238 | 38 | } |
239 | | |
240 | | |
241 | | std::string Box_a1lx::dump(Indent& indent) const |
242 | 0 | { |
243 | 0 | std::ostringstream sstr; |
244 | 0 | sstr << Box::dump(indent); |
245 | |
|
246 | 0 | sstr << indent << "layer-sizes: [" << layer_size[0] << "," << layer_size[1] << "," << layer_size[2] << "]\n"; |
247 | |
|
248 | 0 | return sstr.str(); |
249 | 0 | } |
250 | | |
251 | | |
252 | | Error Box_a1lx::write(StreamWriter& writer) const |
253 | 0 | { |
254 | 0 | size_t box_start = reserve_box_header_space(writer); |
255 | |
|
256 | 0 | bool large = (layer_size[0] > 0xFFFF || layer_size[1] > 0xFFFF || layer_size[2] > 0xFFFF); |
257 | 0 | writer.write8(large ? 1 : 0); |
258 | |
|
259 | 0 | for (int i = 0; i < 3; i++) { |
260 | 0 | if (large) { |
261 | 0 | writer.write32(layer_size[i]); |
262 | 0 | } |
263 | 0 | else { |
264 | 0 | writer.write16((uint16_t) layer_size[i]); |
265 | 0 | } |
266 | 0 | } |
267 | |
|
268 | 0 | prepend_header(writer, box_start); |
269 | |
|
270 | 0 | return Error::Ok; |
271 | 0 | } |
272 | | |
273 | | |
274 | | static uint64_t leb128(BitReader& reader) |
275 | 0 | { |
276 | 0 | uint64_t val = 0; |
277 | 0 | for (int i = 0; i < 8; i++) { |
278 | 0 | int64_t v = reader.get_bits(8); |
279 | 0 | val |= (v & 0x7F) << (i * 7); |
280 | 0 | if (!(v & 0x80)) { |
281 | 0 | break; |
282 | 0 | } |
283 | 0 | } |
284 | |
|
285 | 0 | return val; |
286 | 0 | } |
287 | | |
288 | | |
289 | | struct obu_header_info |
290 | | { |
291 | | int type; |
292 | | bool has_size; |
293 | | uint64_t size = 0; |
294 | | }; |
295 | | |
296 | | static obu_header_info read_obu_header_type(BitReader& reader) |
297 | 0 | { |
298 | 0 | obu_header_info info; |
299 | |
|
300 | 0 | reader.skip_bits(1); |
301 | 0 | info.type = reader.get_bits(4); |
302 | 0 | bool has_extension = reader.get_bits(1); |
303 | 0 | info.has_size = reader.get_bits(1); |
304 | 0 | reader.skip_bits(1); |
305 | |
|
306 | 0 | if (has_extension) { |
307 | 0 | reader.skip_bits(8); |
308 | 0 | } |
309 | |
|
310 | 0 | if (info.has_size) { |
311 | 0 | info.size = leb128(reader); |
312 | 0 | } |
313 | |
|
314 | 0 | return info; |
315 | 0 | } |
316 | | |
317 | | |
318 | | const static int HEIF_OBU_SEQUENCE_HEADER = 1; |
319 | | const static int CP_UNSPECIFIED = 2; |
320 | | const static int TC_UNSPECIFIED = 2; |
321 | | const static int MC_UNSPECIFIED = 2; |
322 | | const static int CP_BT_709 = 1; |
323 | | const static int TC_SRGB = 13; |
324 | | const static int MC_IDENTITY = 0; |
325 | | |
326 | | const static int HEIF_CSP_UNKNOWN = 0; |
327 | | // 1 - CSP_VERTICAL |
328 | | // 2 - CSP_COLOCATED |
329 | | // 3 - CSP_RESERVED |
330 | | |
331 | | bool fill_av1C_configuration_from_stream(Box_av1C::configuration* out_config, const uint8_t* data, int dataSize) |
332 | 0 | { |
333 | 0 | BitReader reader(data, dataSize); |
334 | | |
335 | | // --- find OBU_SEQUENCE_HEADER |
336 | |
|
337 | 0 | bool seq_header_found = false; |
338 | |
|
339 | 0 | while (reader.get_bits_remaining() > 0) { |
340 | 0 | obu_header_info header_info = read_obu_header_type(reader); |
341 | 0 | if (header_info.type == HEIF_OBU_SEQUENCE_HEADER) { |
342 | 0 | seq_header_found = true; |
343 | 0 | break; |
344 | 0 | } |
345 | 0 | else if (header_info.has_size) { |
346 | 0 | if (header_info.size > (uint64_t)std::numeric_limits<int>::max()) { |
347 | 0 | return false; |
348 | 0 | } |
349 | | |
350 | 0 | reader.skip_bytes((int)header_info.size); |
351 | 0 | } |
352 | 0 | else { |
353 | 0 | return false; |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | 0 | if (!seq_header_found) { |
358 | 0 | return false; |
359 | 0 | } |
360 | | |
361 | | |
362 | | // --- read sequence header |
363 | | |
364 | 0 | int dummy; // throw away value |
365 | |
|
366 | 0 | bool decoder_model_info_present = false; |
367 | 0 | int buffer_delay_length_minus1 = 0; |
368 | |
|
369 | 0 | out_config->seq_profile = (uint8_t)reader.get_bits(3); |
370 | 0 | bool still_picture = reader.get_bits(1); |
371 | 0 | (void) still_picture; |
372 | |
|
373 | 0 | bool reduced_still_picture = reader.get_bits(1); |
374 | 0 | if (reduced_still_picture) { |
375 | 0 | out_config->seq_level_idx_0 = (uint8_t)reader.get_bits(5); |
376 | 0 | out_config->seq_tier_0 = 0; |
377 | 0 | } |
378 | 0 | else { |
379 | 0 | bool timing_info_present_flag = reader.get_bits(1); |
380 | 0 | if (timing_info_present_flag) { |
381 | | // --- skip timing info |
382 | 0 | reader.skip_bytes(2 * 4); |
383 | 0 | bool equal_picture_interval = reader.get_bits(1); |
384 | 0 | if (equal_picture_interval) { |
385 | 0 | reader.get_uvlc(&dummy); |
386 | 0 | } |
387 | | |
388 | | // --- skip decoder_model_info |
389 | 0 | decoder_model_info_present = reader.get_bits(1); |
390 | 0 | if (decoder_model_info_present) { |
391 | 0 | buffer_delay_length_minus1 = reader.get_bits(5); |
392 | 0 | reader.skip_bits(32); |
393 | 0 | reader.skip_bits(10); |
394 | 0 | } |
395 | 0 | } |
396 | |
|
397 | 0 | bool initial_display_delay_present_flag = reader.get_bits(1); |
398 | 0 | int operating_points_cnt_minus1 = reader.get_bits(5); |
399 | 0 | for (int i = 0; i <= operating_points_cnt_minus1; i++) { |
400 | 0 | reader.skip_bits(12); |
401 | 0 | auto level = (uint8_t) reader.get_bits(5); |
402 | 0 | if (i == 0) { |
403 | 0 | out_config->seq_level_idx_0 = level; |
404 | 0 | } |
405 | 0 | if (level > 7) { |
406 | 0 | auto tier = (uint8_t) reader.get_bits(1); |
407 | 0 | if (i == 0) { |
408 | 0 | out_config->seq_tier_0 = tier; |
409 | 0 | } |
410 | 0 | } |
411 | |
|
412 | 0 | if (decoder_model_info_present) { |
413 | 0 | bool decoder_model_present_for_this = reader.get_bits(1); |
414 | 0 | if (decoder_model_present_for_this) { |
415 | 0 | int n = buffer_delay_length_minus1 + 1; |
416 | 0 | reader.skip_bits(n); |
417 | 0 | reader.skip_bits(n); |
418 | 0 | reader.skip_bits(1); |
419 | 0 | } |
420 | 0 | } |
421 | |
|
422 | 0 | if (initial_display_delay_present_flag) { |
423 | 0 | bool initial_display_delay_present_for_this = reader.get_bits(1); |
424 | 0 | if (i==0) { |
425 | 0 | out_config->initial_presentation_delay_present = initial_display_delay_present_for_this; |
426 | 0 | } |
427 | |
|
428 | 0 | if (initial_display_delay_present_for_this) { |
429 | 0 | auto delay = (uint8_t)reader.get_bits(4); |
430 | 0 | if (i==0) { |
431 | 0 | out_config->initial_presentation_delay_minus_one = delay; |
432 | 0 | } |
433 | 0 | } |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | |
|
438 | 0 | int frame_width_bits_minus1 = reader.get_bits(4); |
439 | 0 | int frame_height_bits_minus1 = reader.get_bits(4); |
440 | 0 | int max_frame_width_minus1 = reader.get_bits(frame_width_bits_minus1 + 1); |
441 | 0 | int max_frame_height_minus1 = reader.get_bits(frame_height_bits_minus1 + 1); |
442 | 0 | (void)max_frame_width_minus1; |
443 | 0 | (void)max_frame_height_minus1; |
444 | | |
445 | | // printf("max size: %d x %d\n", max_frame_width_minus1+1, max_frame_height_minus1+1); |
446 | |
|
447 | 0 | int frame_id_numbers_present_flag = 0; |
448 | 0 | if (!reduced_still_picture) { |
449 | 0 | frame_id_numbers_present_flag = reader.get_bits(1); |
450 | 0 | } |
451 | 0 | if (frame_id_numbers_present_flag) { |
452 | 0 | reader.skip_bits(7); |
453 | 0 | } |
454 | |
|
455 | 0 | reader.skip_bits(3); |
456 | 0 | if (!reduced_still_picture) { |
457 | 0 | reader.skip_bits(4); |
458 | | |
459 | | // order hint |
460 | 0 | bool enable_order_hint = reader.get_bits(1); |
461 | 0 | if (enable_order_hint) { |
462 | 0 | reader.skip_bits(2); |
463 | 0 | } |
464 | | |
465 | | // screen content |
466 | 0 | int force_screen_content_tools = 2; |
467 | 0 | if (reader.get_bits(1) == 0) { |
468 | 0 | force_screen_content_tools = reader.get_bits(1); |
469 | 0 | } |
470 | |
|
471 | 0 | if (force_screen_content_tools > 0) { |
472 | | // integer mv |
473 | 0 | if (reader.get_bits(1) == 0) { |
474 | 0 | reader.skip_bits(1); |
475 | 0 | } |
476 | 0 | } |
477 | |
|
478 | 0 | if (enable_order_hint) { |
479 | 0 | reader.skip_bits(3); |
480 | 0 | } |
481 | 0 | } |
482 | |
|
483 | 0 | reader.skip_bits(3); |
484 | | |
485 | | // --- color config |
486 | |
|
487 | 0 | out_config->high_bitdepth = (uint8_t)reader.get_bits(1); |
488 | 0 | if (out_config->seq_profile == 2 && out_config->high_bitdepth) { |
489 | 0 | out_config->twelve_bit = (uint8_t)reader.get_bits(1); |
490 | 0 | } |
491 | 0 | else { |
492 | 0 | out_config->twelve_bit = 0; |
493 | 0 | } |
494 | |
|
495 | 0 | if (out_config->seq_profile == 1) { |
496 | 0 | out_config->monochrome = 0; |
497 | 0 | } |
498 | 0 | else { |
499 | 0 | out_config->monochrome = (uint8_t)reader.get_bits(1); |
500 | 0 | } |
501 | |
|
502 | 0 | int color_primaries = CP_UNSPECIFIED; |
503 | 0 | int transfer_characteristics = TC_UNSPECIFIED; |
504 | 0 | int matrix_coefficients = MC_UNSPECIFIED; |
505 | |
|
506 | 0 | bool color_description_preset_flag = reader.get_bits(1); |
507 | 0 | if (color_description_preset_flag) { |
508 | 0 | color_primaries = reader.get_bits(8); |
509 | 0 | transfer_characteristics = reader.get_bits(8); |
510 | 0 | matrix_coefficients = reader.get_bits(8); |
511 | 0 | } |
512 | 0 | else { |
513 | | // color description unspecified |
514 | 0 | } |
515 | |
|
516 | 0 | if (out_config->monochrome) { |
517 | 0 | reader.skip_bits(1); |
518 | 0 | out_config->chroma_subsampling_x = 1; |
519 | 0 | out_config->chroma_subsampling_y = 1; |
520 | 0 | out_config->chroma_sample_position = HEIF_CSP_UNKNOWN; |
521 | 0 | } |
522 | 0 | else if (color_primaries == CP_BT_709 && |
523 | 0 | transfer_characteristics == TC_SRGB && |
524 | 0 | matrix_coefficients == MC_IDENTITY) { |
525 | 0 | out_config->chroma_subsampling_x = 0; |
526 | 0 | out_config->chroma_subsampling_y = 0; |
527 | 0 | } |
528 | 0 | else { |
529 | 0 | reader.skip_bits(1); |
530 | 0 | if (out_config->seq_profile == 0) { |
531 | 0 | out_config->chroma_subsampling_x = 1; |
532 | 0 | out_config->chroma_subsampling_y = 1; |
533 | 0 | } |
534 | 0 | else if (out_config->seq_profile == 1) { |
535 | 0 | out_config->chroma_subsampling_x = 0; |
536 | 0 | out_config->chroma_subsampling_y = 0; |
537 | 0 | } |
538 | 0 | else { |
539 | 0 | if (out_config->twelve_bit) { |
540 | 0 | out_config->chroma_subsampling_x = (uint8_t)reader.get_bits(1); |
541 | 0 | if (out_config->chroma_subsampling_x) { |
542 | 0 | out_config->chroma_subsampling_y = (uint8_t)reader.get_bits(1); |
543 | 0 | } |
544 | 0 | else { |
545 | 0 | out_config->chroma_subsampling_y = 0; |
546 | 0 | } |
547 | 0 | } |
548 | 0 | else { |
549 | 0 | out_config->chroma_subsampling_x = 1; |
550 | 0 | out_config->chroma_subsampling_y = 0; |
551 | 0 | } |
552 | 0 | } |
553 | |
|
554 | 0 | if (out_config->chroma_subsampling_x && |
555 | 0 | out_config->chroma_subsampling_y) { |
556 | 0 | out_config->chroma_sample_position = (uint8_t)reader.get_bits(2); |
557 | 0 | } |
558 | 0 | } |
559 | |
|
560 | 0 | reader.skip_bits(1); // separate_uv_delta |
561 | |
|
562 | 0 | return true; |
563 | 0 | } |