Coverage Report

Created: 2026-01-22 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/codecs/avc_boxes.cc
Line
Count
Source
1
/*
2
 * HEIF AVC codec.
3
 * Copyright (c) 2023 Brad Hards <bradh@frogmouth.net>
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 "avc_boxes.h"
22
#include <cstdint>
23
#include <iomanip>
24
#include <iostream>
25
#include <vector>
26
#include "file.h"
27
#include "context.h"
28
#include "avc_dec.h"
29
#include "hevc_boxes.h"
30
#include <utility>
31
#include <set>
32
33
34
Error Box_avcC::parse(BitstreamRange& range, const heif_security_limits* limits)
35
3.03k
{
36
3.03k
  m_configuration.configuration_version = range.read8();
37
3.03k
  m_configuration.AVCProfileIndication = range.read8();
38
3.03k
  m_configuration.profile_compatibility = range.read8();
39
3.03k
  m_configuration.AVCLevelIndication = range.read8();
40
3.03k
  uint8_t lengthSizeMinusOneWithReserved = range.read8();
41
3.03k
  m_configuration.lengthSize =
42
3.03k
      (lengthSizeMinusOneWithReserved & 0b00000011) + 1;
43
44
3.03k
  uint8_t numOfSequenceParameterSets = (range.read8() & 0b00011111);
45
6.09k
  for (int i = 0; i < numOfSequenceParameterSets; i++) {
46
3.05k
    uint16_t sequenceParameterSetLength = range.read16();
47
3.05k
    std::vector<uint8_t> sps(sequenceParameterSetLength);
48
3.05k
    range.read(sps.data(), sps.size());
49
3.05k
    m_sps.push_back(sps);
50
3.05k
  }
51
52
3.03k
  uint8_t numOfPictureParameterSets = range.read8();
53
6.73k
  for (int i = 0; i < numOfPictureParameterSets; i++) {
54
3.70k
    uint16_t pictureParameterSetLength = range.read16();
55
3.70k
    std::vector<uint8_t> pps(pictureParameterSetLength);
56
3.70k
    range.read(pps.data(), pps.size());
57
3.70k
    m_pps.push_back(pps);
58
3.70k
  }
59
60
  // See ISO/IEC 14496-15 2017 Section 5.3.3.1.2
61
3.03k
  if (range.get_remaining_bytes() > 0) {
62
1.10k
    if ((m_configuration.AVCProfileIndication != 66) &&
63
1.01k
        (m_configuration.AVCProfileIndication != 77) &&
64
828
        (m_configuration.AVCProfileIndication != 88)) {
65
777
      m_configuration.chroma_format = (heif_chroma) (range.read8() & 0b00000011);
66
777
      m_configuration.bit_depth_luma = 8 + (range.read8() & 0b00000111);
67
777
      m_configuration.bit_depth_chroma = 8 + (range.read8() & 0b00000111);
68
777
      uint8_t numOfSequenceParameterSetExt = range.read8();
69
4.78k
      for (int i = 0; i < numOfSequenceParameterSetExt; i++) {
70
4.00k
        uint16_t sequenceParameterSetExtLength = range.read16();
71
4.00k
        std::vector<uint8_t> sps_ext(sequenceParameterSetExtLength);
72
4.00k
        range.read(sps_ext.data(), sps_ext.size());
73
4.00k
        m_sps_ext.push_back(sps_ext);
74
4.00k
      }
75
777
    }
76
1.10k
  }
77
78
3.03k
  return range.get_error();
79
3.03k
}
80
81
Error Box_avcC::write(StreamWriter& writer) const
82
0
{
83
0
  size_t box_start = reserve_box_header_space(writer);
84
85
0
  writer.write8(m_configuration.configuration_version);
86
0
  writer.write8(m_configuration.AVCProfileIndication);
87
0
  writer.write8(m_configuration.profile_compatibility);
88
0
  writer.write8(m_configuration.AVCLevelIndication);
89
0
  uint8_t lengthSizeMinusOneWithReserved = 0b11111100 | ((m_configuration.lengthSize - 1) & 0b11);
90
0
  writer.write8(lengthSizeMinusOneWithReserved);
91
92
0
  if (m_sps.size() > 0b00011111) {
93
0
    return {
94
0
      heif_error_Encoding_error,
95
0
      heif_suberror_Unspecified,
96
0
      "Cannot write more than 31 PPS into avcC box."
97
0
    };
98
0
  }
99
100
0
  uint8_t numSpsWithReserved = 0b11100000 | (m_sps.size() & 0b00011111);
101
0
  writer.write8(numSpsWithReserved);
102
0
  for (const auto& sps : m_sps) {
103
0
    if (sps.size() > 0xFFFF) {
104
0
      return {
105
0
        heif_error_Encoding_error,
106
0
        heif_suberror_Unspecified,
107
0
        "Cannot write SPS larger than 65535 bytes into avcC box."
108
0
      };
109
0
    }
110
0
    writer.write16((uint16_t) sps.size());
111
0
    writer.write(sps);
112
0
  }
113
114
0
  if (m_pps.size() > 0xFF) {
115
0
    return {
116
0
      heif_error_Encoding_error,
117
0
      heif_suberror_Unspecified,
118
0
      "Cannot write more than 255 PPS into avcC box."
119
0
    };
120
0
  }
121
122
0
  writer.write8(m_pps.size() & 0xFF);
123
0
  for (const auto& pps : m_pps) {
124
0
    if (pps.size() > 0xFFFF) {
125
0
      return {
126
0
        heif_error_Encoding_error,
127
0
        heif_suberror_Unspecified,
128
0
        "Cannot write PPS larger than 65535 bytes into avcC box."
129
0
      };
130
0
    }
131
0
    writer.write16((uint16_t) pps.size());
132
0
    writer.write(pps);
133
0
  }
134
135
0
  if ((m_configuration.AVCProfileIndication != 66) &&
136
0
      (m_configuration.AVCProfileIndication != 77) &&
137
0
      (m_configuration.AVCProfileIndication != 88)) {
138
0
    writer.write8(m_configuration.chroma_format);
139
0
    writer.write8(m_configuration.bit_depth_luma - 8);
140
0
    writer.write8(m_configuration.bit_depth_chroma - 8);
141
142
0
    if (m_sps_ext.size() > 0xFF) {
143
0
      return {
144
0
        heif_error_Encoding_error,
145
0
        heif_suberror_Unspecified,
146
0
        "Cannot write more than 255 SPS-Ext into avcC box."
147
0
      };
148
0
    }
149
150
0
    writer.write8(m_sps_ext.size() & 0xFF);
151
0
    for (const auto& spsext : m_sps_ext) {
152
0
      if (spsext.size() > 0xFFFF) {
153
0
        return {
154
0
          heif_error_Encoding_error,
155
0
          heif_suberror_Unspecified,
156
0
          "Cannot write SPS-Ext larger than 65535 bytes into avcC box."
157
0
        };
158
0
      }
159
0
      writer.write16((uint16_t) spsext.size());
160
0
      writer.write(spsext);
161
0
    }
162
0
  }
163
164
0
  prepend_header(writer, box_start);
165
166
0
  return Error::Ok;
167
0
}
168
169
std::string Box_avcC::dump(Indent& indent) const
170
2.84k
{
171
2.84k
  std::ostringstream sstr;
172
2.84k
  sstr << Box::dump(indent);
173
2.84k
  sstr << indent << "configuration_version: " << ((int) m_configuration.configuration_version) << "\n"
174
2.84k
      << indent << "AVCProfileIndication: " << ((int) m_configuration.AVCProfileIndication) << " (" << profileIndicationAsText() << ")\n"
175
2.84k
      << indent << "profile_compatibility: " << ((int) m_configuration.profile_compatibility) << "\n"
176
2.84k
      << indent << "AVCLevelIndication: " << ((int) m_configuration.AVCLevelIndication) << "\n"
177
2.84k
      << indent << "Chroma format: ";
178
179
2.84k
  switch (m_configuration.chroma_format) {
180
567
    case heif_chroma_monochrome:
181
567
      sstr << "4:0:0\n";
182
567
      break;
183
2.13k
    case heif_chroma_420:
184
2.13k
      sstr << "4:2:0\n";
185
2.13k
      break;
186
22
    case heif_chroma_422:
187
22
      sstr << "4:2:2\n";
188
22
      break;
189
123
    case heif_chroma_444:
190
123
      sstr << "4:4:4\n";
191
123
      break;
192
0
    default:
193
0
      sstr << "unsupported\n";
194
0
      break;
195
2.84k
  }
196
197
2.84k
  sstr << indent << "Bit depth luma: " << ((int) m_configuration.bit_depth_luma) << "\n"
198
2.84k
      << indent << "Bit depth chroma: " << ((int) m_configuration.bit_depth_chroma) << "\n";
199
200
2.84k
  for (const auto& sps : m_sps) {
201
2.59k
    sstr << indent << "SPS: ";
202
393k
    for (uint8_t b : sps) {
203
393k
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
204
393k
    }
205
2.59k
    sstr << "\n";
206
2.59k
    sstr << std::dec;
207
2.59k
  }
208
209
2.84k
  for (const auto& spsext : m_sps_ext) {
210
1.77k
    sstr << indent << "SPS-EXT: ";
211
1.77k
    for (uint8_t b : spsext) {
212
486
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
213
486
    }
214
1.77k
    sstr << "\n";
215
1.77k
    sstr << std::dec;
216
1.77k
  }
217
218
2.84k
  for (const auto& pps : m_pps) {
219
1.52k
    sstr << indent << "PPS: ";
220
336k
    for (uint8_t b : pps) {
221
336k
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
222
336k
    }
223
1.52k
    sstr << "\n";
224
1.52k
    sstr << std::dec;
225
1.52k
  }
226
227
2.84k
  return sstr.str();
228
2.84k
}
229
230
std::string Box_avcC::profileIndicationAsText() const
231
2.84k
{
232
  // See ISO/IEC 14496-10:2022 Annex A
233
2.84k
  switch (m_configuration.AVCProfileIndication) {
234
270
    case 44:
235
270
      return "CALVC 4:4:4";
236
989
    case 66:
237
989
      return "Constrained Baseline";
238
541
    case 77:
239
541
      return "Main";
240
499
    case 88:
241
499
      return "Extended";
242
174
    case 100:
243
174
      return "High variant";
244
21
    case 110:
245
21
      return "High 10";
246
22
    case 122:
247
22
      return "High 4:2:2";
248
72
    case 244:
249
72
      return "High 4:4:4";
250
255
    default:
251
255
      return "Unknown";
252
2.84k
  }
253
2.84k
}
254
255
256
void Box_avcC::get_header_nals(std::vector<uint8_t>& data) const
257
0
{
258
0
  for (const auto& sps : m_sps) {
259
0
    data.push_back((sps.size() >> 24) & 0xFF);
260
0
    data.push_back((sps.size() >> 16) & 0xFF);
261
0
    data.push_back((sps.size() >> 8) & 0xFF);
262
0
    data.push_back((sps.size() >> 0) & 0xFF);
263
264
0
    data.insert(data.end(), sps.begin(), sps.end());
265
0
  }
266
267
0
  for (const auto& spsext : m_sps_ext) {
268
0
    data.push_back((spsext.size() >> 24) & 0xFF);
269
0
    data.push_back((spsext.size() >> 16) & 0xFF);
270
0
    data.push_back((spsext.size() >> 8) & 0xFF);
271
0
    data.push_back((spsext.size() >> 0) & 0xFF);
272
273
0
    data.insert(data.end(), spsext.begin(), spsext.end());
274
0
  }
275
276
0
  for (const auto& pps : m_pps) {
277
0
    data.push_back((pps.size() >> 24) & 0xFF);
278
0
    data.push_back((pps.size() >> 16) & 0xFF);
279
0
    data.push_back((pps.size() >> 8) & 0xFF);
280
0
    data.push_back((pps.size() >> 0) & 0xFF);
281
282
0
    data.insert(data.end(), pps.begin(), pps.end());
283
0
  }
284
0
}
285
286
287
void Box_avcC::append_sps_nal(const uint8_t* data, size_t size)
288
0
{
289
0
  std::vector<uint8_t> vec(data, data + size);
290
0
  m_sps.emplace_back(std::move(vec));
291
0
}
292
293
void Box_avcC::append_sps_ext_nal(const uint8_t* data, size_t size)
294
0
{
295
0
  std::vector<uint8_t> vec(data, data + size);
296
0
  m_sps_ext.emplace_back(std::move(vec));
297
0
}
298
299
void Box_avcC::append_pps_nal(const uint8_t* data, size_t size)
300
0
{
301
0
  std::vector<uint8_t> vec(data, data + size);
302
0
  m_pps.emplace_back(std::move(vec));
303
0
}
304
305
void skip_scaling_list(BitReader& reader, int sizeOfScalingList)
306
0
{
307
0
  int lastScale = 8;
308
0
  int nextScale = 8;
309
310
  // TODO: it seems that this can be simplified by exiting the loop
311
  //       as soon as nextScale==0.
312
313
#if 0
314
  // original version
315
  for (int j = 0; j < sizeOfScalingList; j++) {
316
    if (nextScale != 0) {
317
      int delta_scale;
318
      reader.get_svlc(&delta_scale);
319
      nextScale = (lastScale + delta_scale + 256) % 256;
320
    }
321
322
    lastScale = (nextScale == 0) ? lastScale : nextScale;
323
  }
324
#else
325
  // fast version
326
0
  for (int j = 0; j < sizeOfScalingList; j++) {
327
0
    int delta_scale;
328
0
    reader.get_svlc(&delta_scale);
329
0
    nextScale = (lastScale + delta_scale + 256) % 256;
330
331
0
    if (nextScale == 0) {
332
0
      break;
333
0
    }
334
335
0
    lastScale = nextScale;
336
0
  }
337
0
#endif
338
0
}
339
340
341
Error parse_sps_for_avcC_configuration(const uint8_t* sps, size_t size,
342
                                       Box_avcC::configuration* config,
343
                                       int* width, int* height)
344
0
{
345
  // remove start-code emulation bytes from SPS header stream
346
347
0
  std::vector<uint8_t> sps_no_emul = remove_start_code_emulation(sps, size);
348
349
0
  sps = sps_no_emul.data();
350
0
  size = sps_no_emul.size();
351
352
353
0
  BitReader reader(sps, (int) size);
354
355
  // skip NAL header
356
0
  reader.skip_bits(8);
357
358
0
  config->configuration_version = 1;
359
0
  config->AVCProfileIndication = reader.get_bits8(8);
360
0
  config->profile_compatibility = reader.get_bits8(8);
361
0
  config->AVCLevelIndication = reader.get_bits8(8);
362
0
  config->lengthSize = 4;
363
364
0
  int value;
365
0
  reader.get_uvlc(&value); // SPS ID
366
367
0
  Error invalidUVLC{
368
0
    heif_error_Invalid_input,
369
0
    heif_suberror_Unspecified,
370
0
    "Invalid variable length code in AVC SPS header"
371
0
  };
372
373
0
  if (std::set<int>{100, 110, 122, 244, 44, 83, 86}.contains(config->AVCProfileIndication)) {
374
0
    if (!reader.get_uvlc(&value)) {
375
0
      return invalidUVLC;
376
0
    }
377
378
0
    config->chroma_format = (heif_chroma) value;
379
0
    if (config->chroma_format == heif_chroma_444) {
380
0
      reader.skip_bits(1);
381
0
    }
382
383
0
    if (!reader.get_uvlc(&value)) {
384
0
      return invalidUVLC;
385
0
    }
386
0
    config->bit_depth_luma = static_cast<uint8_t>(8 + value);
387
388
0
    if (!reader.get_uvlc(&value)) {
389
0
      return invalidUVLC;
390
0
    }
391
0
    config->bit_depth_chroma = static_cast<uint8_t>(8 + value);
392
393
0
    reader.skip_bits(1);
394
0
    int seq_scaling_matrix_present_flag = reader.get_bits(1);
395
0
    if (seq_scaling_matrix_present_flag) {
396
0
      for (int i = 0; i < ((config->chroma_format != heif_chroma_444) ? 8 : 12); i++) {
397
0
        int scaling_list_present_flag = reader.get_bits(1);
398
0
        if (scaling_list_present_flag) {
399
0
          if (i < 6) {
400
0
            skip_scaling_list(reader, 16);
401
0
          }
402
0
          else {
403
0
            skip_scaling_list(reader, 64);
404
0
          }
405
0
        }
406
0
      }
407
0
    }
408
0
  }
409
0
  else {
410
0
    config->chroma_format = heif_chroma_420;
411
0
    config->bit_depth_luma = 8;
412
0
    config->bit_depth_chroma = 8;
413
0
  }
414
415
0
  reader.get_uvlc(&value); // log2_max_frame_num_minus4
416
0
  int pic_order_cnt_type;
417
0
  reader.get_uvlc(&pic_order_cnt_type);
418
0
  if (pic_order_cnt_type == 0) {
419
0
    reader.get_uvlc(&value);
420
0
  }
421
0
  else if (pic_order_cnt_type == 1) {
422
0
    reader.get_bits(1);
423
0
    reader.get_svlc(&value);
424
0
    reader.get_svlc(&value);
425
0
    int num_ref_franes_in_pic_order_cnt_cycle;
426
0
    reader.get_uvlc(&num_ref_franes_in_pic_order_cnt_cycle);
427
0
    for (int i = 0; i < num_ref_franes_in_pic_order_cnt_cycle; i++) {
428
0
      reader.get_uvlc(&value);
429
0
    }
430
0
  }
431
432
0
  reader.get_uvlc(&value); // num_ref_frames
433
0
  reader.skip_bits(1);
434
435
0
  int pic_width_in_mbs_minus1;
436
0
  int pic_height_in_mbs_minus1;
437
0
  reader.get_uvlc(&pic_width_in_mbs_minus1);
438
0
  reader.get_uvlc(&pic_height_in_mbs_minus1);
439
440
0
  *width = (pic_width_in_mbs_minus1 + 1) * 16;
441
0
  *height = (pic_height_in_mbs_minus1 + 1) * 16;
442
443
0
  uint32_t frame_mbs_only_flag = reader.get_bits(1);
444
0
  if (!frame_mbs_only_flag) {
445
0
    reader.skip_bits(1);
446
0
  }
447
0
  reader.skip_bits(1);
448
0
  uint32_t frame_cropping_flag = reader.get_bits(1);
449
0
  if (frame_cropping_flag) {
450
0
    int left, right, top, bottom;
451
0
    reader.get_uvlc(&left);
452
0
    reader.get_uvlc(&right);
453
0
    reader.get_uvlc(&top);
454
0
    reader.get_uvlc(&bottom);
455
456
0
    *width -= left + right;
457
0
    *height -= top + bottom;
458
0
  }
459
460
0
  return {};
461
0
}