Coverage Report

Created: 2025-06-16 07:00

/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