Coverage Report

Created: 2026-05-30 06:06

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