/src/libheif/libheif/codecs/vvc_boxes.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * HEIF VVC codec. |
3 | | * Copyright (c) 2023 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 "vvc_boxes.h" |
22 | | #include "file.h" |
23 | | #include <cstring> |
24 | | #include <string> |
25 | | #include <cassert> |
26 | | #include <iomanip> |
27 | | #include <utility> |
28 | | #include <libheif/api_structs.h> |
29 | | |
30 | | |
31 | | Error Box_vvcC::parse(BitstreamRange& range, const heif_security_limits* limits) |
32 | 161 | { |
33 | 161 | parse_full_box_header(range); |
34 | | |
35 | 161 | uint8_t byte; |
36 | | |
37 | 161 | auto& c = m_configuration; // abbreviation |
38 | | |
39 | 161 | byte = range.read8(); |
40 | | |
41 | 161 | c.LengthSizeMinusOne = (byte >> 1) & 3; |
42 | 161 | c.ptl_present_flag = !!(byte & 1); |
43 | | |
44 | 161 | if (c.ptl_present_flag) { |
45 | 94 | uint16_t word = range.read16(); |
46 | 94 | c.ols_idx = (word >> 7) & 0x1FF; |
47 | 94 | c.num_sublayers = (word >> 4) & 0x07; |
48 | 94 | c.constant_frame_rate = (word >> 2) & 0x03; |
49 | 94 | c.chroma_format_idc = word & 0x03; |
50 | | |
51 | 94 | byte = range.read8(); |
52 | 94 | c.bit_depth_minus8 = (byte >> 5) & 0x07; |
53 | | |
54 | | // VvcPTLRecord |
55 | | |
56 | 94 | auto& ptl = c.native_ptl; // abbreviation |
57 | | |
58 | 94 | byte = range.read8(); |
59 | 94 | ptl.num_bytes_constraint_info = byte & 0x3f; |
60 | | |
61 | 94 | if (ptl.num_bytes_constraint_info == 0) { |
62 | 3 | return {heif_error_Invalid_input, |
63 | 3 | heif_suberror_Invalid_parameter_value, |
64 | 3 | "vvcC with num_bytes_constraint_info==0 is not allowed."}; |
65 | 3 | } |
66 | | |
67 | 91 | byte = range.read8(); |
68 | 91 | ptl.general_profile_idc = (byte >> 1) & 0x7f; |
69 | 91 | ptl.general_tier_flag = (byte & 1); |
70 | | |
71 | 91 | ptl.general_level_idc = range.read8(); |
72 | | |
73 | 2.46k | for (int i = 0; i < ptl.num_bytes_constraint_info; i++) { |
74 | 2.37k | byte = range.read8(); |
75 | 2.37k | if (i == 0) { |
76 | 91 | ptl.ptl_frame_only_constraint_flag = (byte >> 7) & 1; |
77 | 91 | ptl.ptl_multi_layer_enabled_flag = (byte >> 6) & 1; |
78 | 91 | byte &= 0x3f; |
79 | 91 | } |
80 | | |
81 | 2.37k | ptl.general_constraint_info.push_back(byte); |
82 | 2.37k | } |
83 | | |
84 | 91 | if (c.num_sublayers > 1) { |
85 | 74 | ptl.ptl_sublayer_level_present_flag.resize(c.num_sublayers - 1); |
86 | | |
87 | 74 | byte = range.read8(); |
88 | 74 | uint8_t mask = 0x80; |
89 | | |
90 | 400 | for (int i = c.num_sublayers - 2; i >= 0; i--) { |
91 | 326 | ptl.ptl_sublayer_level_present_flag[i] = !!(byte & mask); |
92 | 326 | mask >>= 1; |
93 | 326 | } |
94 | 74 | } |
95 | | |
96 | 91 | ptl.sublayer_level_idc.resize(c.num_sublayers); |
97 | 91 | if (c.num_sublayers > 0) { |
98 | 78 | ptl.sublayer_level_idc[c.num_sublayers - 1] = ptl.general_level_idc; |
99 | | |
100 | 404 | for (int i = c.num_sublayers - 2; i >= 0; i--) { |
101 | 326 | if (ptl.ptl_sublayer_level_present_flag[i]) { |
102 | 160 | ptl.sublayer_level_idc[i] = range.read8(); |
103 | 160 | } |
104 | 166 | else { |
105 | 166 | ptl.sublayer_level_idc[i] = ptl.sublayer_level_idc[i + 1]; |
106 | 166 | } |
107 | 326 | } |
108 | 78 | } |
109 | | |
110 | 91 | uint8_t ptl_num_sub_profiles = range.read8(); |
111 | 2.60k | for (int j=0; j < ptl_num_sub_profiles; j++) { |
112 | 2.51k | ptl.general_sub_profile_idc.push_back(range.read32()); |
113 | 2.51k | } |
114 | | |
115 | | |
116 | | // remaining fields |
117 | | |
118 | 91 | c.max_picture_width = range.read16(); |
119 | 91 | c.max_picture_height = range.read16(); |
120 | 91 | c.avg_frame_rate = range.read16(); |
121 | 91 | } |
122 | | |
123 | | |
124 | | // read NAL arrays |
125 | | |
126 | 158 | int nArrays = range.read8(); |
127 | | |
128 | 522 | for (int i = 0; i < nArrays && !range.error(); i++) { |
129 | 364 | byte = range.read8(); |
130 | | |
131 | 364 | NalArray array; |
132 | | |
133 | 364 | array.m_array_completeness = (byte >> 7) & 1; |
134 | 364 | array.m_NAL_unit_type = (byte & 0x3F); |
135 | | |
136 | 364 | int nUnits = range.read16(); |
137 | 884 | for (int u = 0; u < nUnits && !range.error(); u++) { |
138 | | |
139 | 520 | std::vector<uint8_t> nal_unit; |
140 | 520 | int size = range.read16(); |
141 | 520 | if (!size) { |
142 | | // Ignore empty NAL units. |
143 | 385 | continue; |
144 | 385 | } |
145 | | |
146 | 135 | if (range.prepare_read(size)) { |
147 | 92 | nal_unit.resize(size); |
148 | 92 | bool success = range.get_istream()->read((char*) nal_unit.data(), size); |
149 | 92 | if (!success) { |
150 | 0 | return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while reading hvcC box"}; |
151 | 0 | } |
152 | 92 | } |
153 | | |
154 | 135 | array.m_nal_units.push_back(std::move(nal_unit)); |
155 | 135 | } |
156 | | |
157 | 364 | m_nal_array.push_back(std::move(array)); |
158 | 364 | } |
159 | | |
160 | 158 | return range.get_error(); |
161 | 158 | } |
162 | | |
163 | | |
164 | | bool Box_vvcC::get_headers(std::vector<uint8_t>* dest) const |
165 | 0 | { |
166 | 0 | for (const auto& nal_array : m_nal_array) { |
167 | 0 | for (const auto& nal : nal_array.m_nal_units) { |
168 | 0 | assert(nal.size() <= 0xFFFF); |
169 | 0 | auto size = static_cast<uint16_t>(nal.size()); |
170 | |
|
171 | 0 | dest->push_back(0); |
172 | 0 | dest->push_back(0); |
173 | 0 | dest->push_back(static_cast<uint8_t>(size >> 8)); |
174 | 0 | dest->push_back(static_cast<uint8_t>(size & 0xFF)); |
175 | |
|
176 | 0 | dest->insert(dest->end(), nal.begin(), nal.end()); |
177 | 0 | } |
178 | 0 | } |
179 | |
|
180 | 0 | return true; |
181 | 0 | } |
182 | | |
183 | | |
184 | | void Box_vvcC::append_nal_data(const std::vector<uint8_t>& nal) |
185 | 0 | { |
186 | 0 | assert(nal.size()>=2); |
187 | 0 | uint8_t nal_type = (nal[1] >> 3) & 0x1F; |
188 | | |
189 | | // insert into existing array if it exists |
190 | |
|
191 | 0 | for (auto& nalarray : m_nal_array) { |
192 | 0 | if (nalarray.m_NAL_unit_type == nal_type) { |
193 | 0 | nalarray.m_nal_units.push_back(nal); |
194 | 0 | return; |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | // generate new NAL array |
199 | | |
200 | 0 | NalArray array; |
201 | 0 | array.m_array_completeness = true; |
202 | 0 | array.m_NAL_unit_type = uint8_t((nal[1] >> 3) & 0x1F); |
203 | 0 | array.m_nal_units.push_back(nal); |
204 | |
|
205 | 0 | m_nal_array.push_back(array); |
206 | 0 | } |
207 | | |
208 | | |
209 | | void Box_vvcC::append_nal_data(const uint8_t* data, size_t size) |
210 | 0 | { |
211 | 0 | std::vector<uint8_t> nal; |
212 | 0 | nal.resize(size); |
213 | 0 | memcpy(nal.data(), data, size); |
214 | |
|
215 | 0 | append_nal_data(nal); |
216 | 0 | } |
217 | | |
218 | | |
219 | | Error Box_vvcC::write(StreamWriter& writer) const |
220 | 0 | { |
221 | 0 | size_t box_start = reserve_box_header_space(writer); |
222 | |
|
223 | 0 | const auto& c = m_configuration; |
224 | |
|
225 | 0 | uint8_t byte; |
226 | |
|
227 | 0 | byte = uint8_t(0xF8 | (c.LengthSizeMinusOne<<1) | (c.ptl_present_flag ? 1 : 0)); |
228 | 0 | writer.write8(byte); |
229 | |
|
230 | 0 | if (c.ptl_present_flag) { |
231 | 0 | assert(c.ols_idx <= 0x1FF); |
232 | 0 | assert(c.num_sublayers <= 7); |
233 | 0 | assert(c.constant_frame_rate <= 3); |
234 | 0 | assert(c.chroma_format_idc <= 3); |
235 | 0 | assert(c.bit_depth_minus8 <= 7); |
236 | |
|
237 | 0 | auto word = uint16_t((c.ols_idx << 7) | (c.num_sublayers << 4) | (c.constant_frame_rate << 2) | (c.chroma_format_idc)); |
238 | 0 | writer.write16(word); |
239 | |
|
240 | 0 | writer.write8(uint8_t((c.bit_depth_minus8<<5) | 0x1F)); |
241 | |
|
242 | 0 | const auto& ptl = c.native_ptl; |
243 | |
|
244 | 0 | assert(ptl.general_profile_idc <= 0x7F); |
245 | |
|
246 | 0 | writer.write8(ptl.num_bytes_constraint_info & 0x3f); |
247 | 0 | writer.write8(static_cast<uint8_t>((ptl.general_profile_idc<<1) | ptl.general_tier_flag)); |
248 | 0 | writer.write8(ptl.general_level_idc); |
249 | |
|
250 | 0 | for (int i=0;i<ptl.num_bytes_constraint_info;i++) { |
251 | 0 | if (i==0) { |
252 | 0 | assert(ptl.ptl_frame_only_constraint_flag <= 1); |
253 | 0 | assert(ptl.ptl_multi_layer_enabled_flag <= 1); |
254 | 0 | assert(ptl.general_constraint_info[0] <= 0x3F); |
255 | 0 | byte = static_cast<uint8_t>((ptl.ptl_frame_only_constraint_flag << 7) | (ptl.ptl_multi_layer_enabled_flag << 6) | ptl.general_constraint_info[0]); |
256 | 0 | } |
257 | 0 | else { |
258 | 0 | byte = ptl.general_constraint_info[i]; |
259 | 0 | } |
260 | |
|
261 | 0 | writer.write8(byte); |
262 | 0 | } |
263 | |
|
264 | 0 | byte = 0; |
265 | 0 | if (c.num_sublayers > 1) { |
266 | 0 | uint8_t mask=0x80; |
267 | |
|
268 | 0 | for (int i = c.num_sublayers - 2; i >= 0; i--) { |
269 | 0 | if (ptl.ptl_sublayer_level_present_flag[i]) { |
270 | 0 | byte |= mask; |
271 | 0 | } |
272 | 0 | mask >>= 1; |
273 | 0 | } |
274 | 0 | } |
275 | 0 | writer.write8(byte); |
276 | |
|
277 | 0 | for (int i=c.num_sublayers-2; i >= 0; i--) { |
278 | 0 | if (ptl.ptl_sublayer_level_present_flag[i]) { |
279 | 0 | writer.write8(ptl.sublayer_level_idc[i]); |
280 | 0 | } |
281 | 0 | } |
282 | |
|
283 | 0 | assert(ptl.general_sub_profile_idc.size() <= 0xFF); |
284 | 0 | byte = static_cast<uint8_t>(ptl.general_sub_profile_idc.size()); |
285 | 0 | writer.write8(byte); |
286 | |
|
287 | 0 | for (int j=0; j < byte; j++) { |
288 | 0 | writer.write32(ptl.general_sub_profile_idc[j]); |
289 | 0 | } |
290 | |
|
291 | 0 | writer.write16(c.max_picture_width); |
292 | 0 | writer.write16(c.max_picture_height); |
293 | 0 | writer.write16(c.avg_frame_rate); |
294 | 0 | } |
295 | | |
296 | | // --- write configuration NALs |
297 | |
|
298 | 0 | if (m_nal_array.size() > 255) { |
299 | 0 | return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL arrays."}; |
300 | 0 | } |
301 | | |
302 | 0 | writer.write8((uint8_t)m_nal_array.size()); |
303 | 0 | for (const NalArray& nal_array : m_nal_array) { |
304 | 0 | uint8_t v2 = (nal_array.m_array_completeness ? 0x80 : 0); |
305 | 0 | v2 |= nal_array.m_NAL_unit_type; |
306 | 0 | writer.write8(v2); |
307 | |
|
308 | 0 | if (nal_array.m_nal_units.size() > 0xFFFF) { |
309 | 0 | return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL units."}; |
310 | 0 | } |
311 | | |
312 | 0 | writer.write16((uint16_t)nal_array.m_nal_units.size()); |
313 | 0 | for (const auto& nal : nal_array.m_nal_units) { |
314 | |
|
315 | 0 | if (nal.size() > 0xFFFF) { |
316 | 0 | return {heif_error_Encoding_error, heif_suberror_Unspecified, "VVC NAL too large."}; |
317 | 0 | } |
318 | | |
319 | 0 | writer.write16((uint16_t)nal.size()); |
320 | 0 | writer.write(nal); |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | 0 | prepend_header(writer, box_start); |
325 | |
|
326 | 0 | return Error::Ok; |
327 | 0 | } |
328 | | |
329 | | |
330 | | static const char* vvc_chroma_names[4] = {"mono", "4:2:0", "4:2:2", "4:4:4"}; |
331 | | |
332 | | const char* NAL_name(uint8_t nal_type) |
333 | 0 | { |
334 | 0 | switch (nal_type) { |
335 | 0 | case 12: return "OPI"; |
336 | 0 | case 13: return "DCI"; |
337 | 0 | case 14: return "VPS"; |
338 | 0 | case 15: return "SPS"; |
339 | 0 | case 16: return "PPS"; |
340 | 0 | case 17: return "PREFIX_APS"; |
341 | 0 | case 18: return "SUFFIX_APS"; |
342 | 0 | case 19: return "PH"; |
343 | 0 | default: return "?"; |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | |
348 | | std::string Box_vvcC::dump(Indent& indent) const |
349 | 0 | { |
350 | 0 | std::ostringstream sstr; |
351 | 0 | sstr << FullBox::dump(indent); |
352 | |
|
353 | 0 | const auto& c = m_configuration; // abbreviation |
354 | |
|
355 | 0 | sstr << indent << "NAL length size: " << ((int) c.LengthSizeMinusOne + 1) << "\n"; |
356 | 0 | if (c.ptl_present_flag) { |
357 | 0 | const auto& ptl = c.native_ptl; |
358 | 0 | sstr << indent << "ols-index: " << c.ols_idx << "\n" |
359 | 0 | << indent << "num sublayers: " << ((int) c.num_sublayers) << "\n" |
360 | 0 | << indent << "constant frame rate: " << (c.constant_frame_rate == 1 ? "constant" : (c.constant_frame_rate == 2 ? "multi-layer" : "unknown")) << "\n" |
361 | 0 | << indent << "chroma-format: " << vvc_chroma_names[c.chroma_format_idc] << "\n" |
362 | 0 | << indent << "bit-depth: " << ((int) c.bit_depth_minus8 + 8) << "\n" |
363 | 0 | << indent << "max picture width: " << c.max_picture_width << "\n" |
364 | 0 | << indent << "max picture height: " << c.max_picture_height << "\n"; |
365 | |
|
366 | 0 | sstr << indent << "general profile: " << ((int)ptl.general_profile_idc) << "\n" |
367 | 0 | << indent << "tier flag: " << ((int)ptl.general_tier_flag) << "\n" |
368 | 0 | << indent << "general level:" << ((int)ptl.general_level_idc) << "\n" |
369 | 0 | << indent << "ptl frame only constraint flag: " << ((int)ptl.ptl_frame_only_constraint_flag) << "\n" |
370 | 0 | << indent << "ptl multi layer enabled flag: " << ((int)ptl.ptl_multi_layer_enabled_flag) << "\n"; |
371 | 0 | } |
372 | | |
373 | |
|
374 | 0 | sstr << indent << "num of arrays: " << m_nal_array.size() << "\n"; |
375 | |
|
376 | 0 | sstr << indent << "config NALs:\n"; |
377 | 0 | for (const auto& nal_array : m_nal_array) { |
378 | 0 | indent++; |
379 | 0 | sstr << indent << "NAL type: " << ((int)nal_array.m_NAL_unit_type) << " (" << NAL_name(nal_array.m_NAL_unit_type) << ")\n"; |
380 | 0 | sstr << indent << "array completeness: " << ((int)nal_array.m_array_completeness) << "\n"; |
381 | |
|
382 | 0 | for (const auto& nal : nal_array.m_nal_units) { |
383 | 0 | indent++; |
384 | 0 | std::string ind = indent.get_string(); |
385 | 0 | sstr << write_raw_data_as_hex(nal.data(), nal.size(), ind, ind); |
386 | 0 | indent--; |
387 | 0 | } |
388 | 0 | indent--; |
389 | 0 | } |
390 | |
|
391 | 0 | return sstr.str(); |
392 | 0 | } |
393 | | |
394 | | static std::vector<uint8_t> remove_start_code_emulation(const uint8_t* sps, size_t size) |
395 | 0 | { |
396 | 0 | std::vector<uint8_t> out_data; |
397 | |
|
398 | 0 | for (size_t i = 0; i < size; i++) { |
399 | 0 | if (i + 2 < size && |
400 | 0 | sps[i] == 0 && |
401 | 0 | sps[i + 1] == 0 && |
402 | 0 | sps[i + 2] == 3) { |
403 | 0 | out_data.push_back(0); |
404 | 0 | out_data.push_back(0); |
405 | 0 | i += 2; |
406 | 0 | } |
407 | 0 | else { |
408 | 0 | out_data.push_back(sps[i]); |
409 | 0 | } |
410 | 0 | } |
411 | |
|
412 | 0 | return out_data; |
413 | 0 | } |
414 | | |
415 | | |
416 | | |
417 | | Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, |
418 | | Box_vvcC::configuration* config, |
419 | | int* width, int* height) |
420 | 0 | { |
421 | | // remove start-code emulation bytes from SPS header stream |
422 | |
|
423 | 0 | std::vector<uint8_t> sps_no_emul = remove_start_code_emulation(sps, size); |
424 | |
|
425 | 0 | sps = sps_no_emul.data(); |
426 | 0 | size = sps_no_emul.size(); |
427 | |
|
428 | 0 | BitReader reader(sps, (int) size); |
429 | | |
430 | | // skip NAL header |
431 | 0 | reader.skip_bits(2 * 8); |
432 | | |
433 | | // skip SPS ID |
434 | 0 | reader.skip_bits(4); |
435 | | |
436 | | // skip VPS ID |
437 | 0 | reader.skip_bits(4); |
438 | |
|
439 | 0 | config->ols_idx = 0; |
440 | 0 | config->num_sublayers = reader.get_bits8(3) + 1; |
441 | 0 | config->chroma_format_idc = reader.get_bits8(2); |
442 | 0 | reader.skip_bits(2); |
443 | |
|
444 | 0 | bool sps_ptl_dpb_hrd_params_present_flag = reader.get_bits(1); |
445 | 0 | if (sps_ptl_dpb_hrd_params_present_flag) { |
446 | | // profile_tier_level( 1, sps_max_sublayers_minus1 ) |
447 | |
|
448 | 0 | auto& ptl = config->native_ptl; |
449 | |
|
450 | 0 | if (true /*profileTierPresentFlag*/) { |
451 | 0 | ptl.general_profile_idc = reader.get_bits8(7); |
452 | 0 | ptl.general_tier_flag = reader.get_bits8(1); |
453 | 0 | } |
454 | 0 | ptl.general_level_idc = reader.get_bits8(8); |
455 | 0 | ptl.ptl_frame_only_constraint_flag = reader.get_bits8(1); |
456 | 0 | ptl.ptl_multi_layer_enabled_flag = reader.get_bits8(1); |
457 | |
|
458 | 0 | if (true /* profileTierPresentFlag*/ ) { |
459 | | // general_constraints_info() |
460 | |
|
461 | 0 | bool gci_present_flag = reader.get_bits(1); |
462 | 0 | if (gci_present_flag) { |
463 | 0 | assert(false); |
464 | 0 | } |
465 | 0 | else { |
466 | 0 | ptl.num_bytes_constraint_info = 1; |
467 | 0 | ptl.general_constraint_info.push_back(0); |
468 | 0 | } |
469 | |
|
470 | 0 | reader.skip_to_byte_boundary(); |
471 | 0 | } |
472 | |
|
473 | 0 | ptl.ptl_sublayer_level_present_flag.resize(config->num_sublayers); |
474 | 0 | for (int i = config->num_sublayers-2; i >= 0; i--) { |
475 | 0 | ptl.ptl_sublayer_level_present_flag[i] = reader.get_bits(1); |
476 | 0 | } |
477 | |
|
478 | 0 | reader.skip_to_byte_boundary(); |
479 | |
|
480 | 0 | ptl.sublayer_level_idc.resize(config->num_sublayers); |
481 | 0 | for (int i = config->num_sublayers-2; i >= 0; i--) { |
482 | 0 | if (ptl.ptl_sublayer_level_present_flag[i]) { |
483 | 0 | ptl.sublayer_level_idc[i] = reader.get_bits8(8); |
484 | 0 | } |
485 | 0 | } |
486 | |
|
487 | 0 | if (true /*profileTierPresentFlag*/) { |
488 | 0 | int ptl_num_sub_profiles = reader.get_bits(8); |
489 | 0 | ptl.general_sub_profile_idc.resize(ptl_num_sub_profiles); |
490 | |
|
491 | 0 | for (int i = 0; i < ptl_num_sub_profiles; i++) { |
492 | 0 | ptl.general_sub_profile_idc[i] = reader.get_bits(32); |
493 | 0 | } |
494 | 0 | } |
495 | 0 | } |
496 | |
|
497 | 0 | reader.skip_bits(1); // sps_gdr_enabled_flag |
498 | 0 | bool sps_ref_pic_resampling_enabled_flag = reader.get_bits(1); |
499 | 0 | if (sps_ref_pic_resampling_enabled_flag) { |
500 | 0 | reader.skip_bits(1); // sps_res_change_in_clvs_allowed_flag |
501 | 0 | } |
502 | |
|
503 | 0 | int sps_pic_width_max_in_luma_samples; |
504 | 0 | int sps_pic_height_max_in_luma_samples; |
505 | |
|
506 | 0 | bool success; |
507 | 0 | success = reader.get_uvlc(&sps_pic_width_max_in_luma_samples); |
508 | 0 | (void)success; |
509 | 0 | success = reader.get_uvlc(&sps_pic_height_max_in_luma_samples); |
510 | 0 | (void)success; |
511 | |
|
512 | 0 | *width = sps_pic_width_max_in_luma_samples; |
513 | 0 | *height = sps_pic_height_max_in_luma_samples; |
514 | |
|
515 | 0 | if (sps_pic_width_max_in_luma_samples > 0xFFFF || |
516 | 0 | sps_pic_height_max_in_luma_samples > 0xFFFF) { |
517 | 0 | return {heif_error_Encoding_error, |
518 | 0 | heif_suberror_Invalid_parameter_value, |
519 | 0 | "SPS max picture width or height exceeds maximum (65535)"}; |
520 | 0 | } |
521 | | |
522 | 0 | config->max_picture_width = static_cast<uint16_t>(sps_pic_width_max_in_luma_samples); |
523 | 0 | config->max_picture_height = static_cast<uint16_t>(sps_pic_height_max_in_luma_samples); |
524 | |
|
525 | 0 | int sps_conformance_window_flag = reader.get_bits(1); |
526 | 0 | if (sps_conformance_window_flag) { |
527 | 0 | int left,right,top,bottom; |
528 | 0 | reader.get_uvlc(&left); |
529 | 0 | reader.get_uvlc(&right); |
530 | 0 | reader.get_uvlc(&top); |
531 | 0 | reader.get_uvlc(&bottom); |
532 | 0 | } |
533 | |
|
534 | 0 | bool sps_subpic_info_present_flag = reader.get_bits(1); |
535 | 0 | if (sps_subpic_info_present_flag) { |
536 | 0 | assert(false); // TODO |
537 | 0 | } |
538 | |
|
539 | 0 | int bitDepth_minus8; |
540 | 0 | success = reader.get_uvlc(&bitDepth_minus8); |
541 | 0 | (void)success; |
542 | |
|
543 | 0 | if (bitDepth_minus8 > 0xFF - 8) { |
544 | 0 | return {heif_error_Encoding_error, heif_suberror_Unspecified, "VCC bit depth out of range."}; |
545 | 0 | } |
546 | | |
547 | 0 | config->bit_depth_minus8 = static_cast<uint8_t>(bitDepth_minus8); |
548 | |
|
549 | 0 | config->constant_frame_rate = 1; // is constant (TODO: where do we get this from) |
550 | |
|
551 | 0 | return Error::Ok; |
552 | 0 | } |
553 | | |