Coverage Report

Created: 2025-10-12 06:18

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
30
31
Error Box_avcC::parse(BitstreamRange& range, const heif_security_limits* limits)
32
1.84k
{
33
1.84k
  m_configuration.configuration_version = range.read8();
34
1.84k
  m_configuration.AVCProfileIndication = range.read8();
35
1.84k
  m_configuration.profile_compatibility = range.read8();
36
1.84k
  m_configuration.AVCLevelIndication = range.read8();
37
1.84k
  uint8_t lengthSizeMinusOneWithReserved = range.read8();
38
1.84k
  m_configuration.lengthSize =
39
1.84k
      (lengthSizeMinusOneWithReserved & 0b00000011) + 1;
40
41
1.84k
  uint8_t numOfSequenceParameterSets = (range.read8() & 0b00011111);
42
4.56k
  for (int i = 0; i < numOfSequenceParameterSets; i++) {
43
2.71k
    uint16_t sequenceParameterSetLength = range.read16();
44
2.71k
    std::vector<uint8_t> sps(sequenceParameterSetLength);
45
2.71k
    range.read(sps.data(), sps.size());
46
2.71k
    m_sps.push_back(sps);
47
2.71k
  }
48
49
1.84k
  uint8_t numOfPictureParameterSets = range.read8();
50
4.65k
  for (int i = 0; i < numOfPictureParameterSets; i++) {
51
2.81k
    uint16_t pictureParameterSetLength = range.read16();
52
2.81k
    std::vector<uint8_t> pps(pictureParameterSetLength);
53
2.81k
    range.read(pps.data(), pps.size());
54
2.81k
    m_pps.push_back(pps);
55
2.81k
  }
56
57
  // See ISO/IEC 14496-15 2017 Section 5.3.3.1.2
58
1.84k
  if ((m_configuration.AVCProfileIndication != 66) &&
59
1.82k
      (m_configuration.AVCProfileIndication != 77) &&
60
1.71k
      (m_configuration.AVCProfileIndication != 88)) {
61
1.29k
    m_configuration.chroma_format = (heif_chroma) (range.read8() & 0b00000011);
62
1.29k
    m_configuration.bit_depth_luma = 8 + (range.read8() & 0b00000111);
63
1.29k
    m_configuration.bit_depth_chroma = 8 + (range.read8() & 0b00000111);
64
1.29k
    uint8_t numOfSequenceParameterSetExt = range.read8();
65
4.00k
    for (int i = 0; i < numOfSequenceParameterSetExt; i++) {
66
2.71k
      uint16_t sequenceParameterSetExtLength = range.read16();
67
2.71k
      std::vector<uint8_t> sps_ext(sequenceParameterSetExtLength);
68
2.71k
      range.read(sps_ext.data(), sps_ext.size());
69
2.71k
      m_sps_ext.push_back(sps_ext);
70
2.71k
    }
71
1.29k
  }
72
73
1.84k
  return range.get_error();
74
1.84k
}
75
76
Error Box_avcC::write(StreamWriter& writer) const
77
0
{
78
0
  size_t box_start = reserve_box_header_space(writer);
79
80
0
  writer.write8(m_configuration.configuration_version);
81
0
  writer.write8(m_configuration.AVCProfileIndication);
82
0
  writer.write8(m_configuration.profile_compatibility);
83
0
  writer.write8(m_configuration.AVCLevelIndication);
84
0
  uint8_t lengthSizeMinusOneWithReserved = 0b11111100 | ((m_configuration.lengthSize - 1) & 0b11);
85
0
  writer.write8(lengthSizeMinusOneWithReserved);
86
87
0
  if (m_sps.size() > 0b00011111) {
88
0
    return {heif_error_Encoding_error,
89
0
            heif_suberror_Unspecified,
90
0
            "Cannot write more than 31 PPS into avcC box."};
91
0
  }
92
93
0
  uint8_t numSpsWithReserved = 0b11100000 | (m_sps.size() & 0b00011111);
94
0
  writer.write8(numSpsWithReserved);
95
0
  for (const auto& sps : m_sps) {
96
0
    if (sps.size() > 0xFFFF) {
97
0
      return {heif_error_Encoding_error,
98
0
              heif_suberror_Unspecified,
99
0
              "Cannot write SPS larger than 65535 bytes into avcC box."};
100
0
    }
101
0
    writer.write16((uint16_t) sps.size());
102
0
    writer.write(sps);
103
0
  }
104
105
0
  if (m_pps.size() > 0xFF) {
106
0
    return {heif_error_Encoding_error,
107
0
            heif_suberror_Unspecified,
108
0
            "Cannot write more than 255 PPS into avcC box."};
109
0
  }
110
111
0
  writer.write8(m_pps.size() & 0xFF);
112
0
  for (const auto& pps : m_pps) {
113
0
    if (pps.size() > 0xFFFF) {
114
0
      return {heif_error_Encoding_error,
115
0
              heif_suberror_Unspecified,
116
0
              "Cannot write PPS larger than 65535 bytes into avcC box."};
117
0
    }
118
0
    writer.write16((uint16_t) pps.size());
119
0
    writer.write(pps);
120
0
  }
121
122
0
  if ((m_configuration.AVCProfileIndication != 66) &&
123
0
      (m_configuration.AVCProfileIndication != 77) &&
124
0
      (m_configuration.AVCProfileIndication != 88)) {
125
0
    writer.write8(m_configuration.chroma_format);
126
0
    writer.write8(m_configuration.bit_depth_luma - 8);
127
0
    writer.write8(m_configuration.bit_depth_chroma - 8);
128
129
0
    if (m_sps_ext.size() > 0xFF) {
130
0
      return {heif_error_Encoding_error,
131
0
              heif_suberror_Unspecified,
132
0
              "Cannot write more than 255 SPS-Ext into avcC box."};
133
0
    }
134
135
0
    writer.write8(m_sps_ext.size() & 0xFF);
136
0
    for (const auto& spsext : m_sps_ext) {
137
0
      if (spsext.size() > 0xFFFF) {
138
0
        return {heif_error_Encoding_error,
139
0
                heif_suberror_Unspecified,
140
0
                "Cannot write SPS-Ext larger than 65535 bytes into avcC box."};
141
0
      }
142
0
      writer.write16((uint16_t) spsext.size());
143
0
      writer.write(spsext);
144
0
    }
145
0
  }
146
147
0
  prepend_header(writer, box_start);
148
149
0
  return Error::Ok;
150
0
}
151
152
std::string Box_avcC::dump(Indent& indent) const
153
1.76k
{
154
1.76k
  std::ostringstream sstr;
155
1.76k
  sstr << Box::dump(indent);
156
1.76k
  sstr << indent << "configuration_version: " << ((int) m_configuration.configuration_version) << "\n"
157
1.76k
       << indent << "AVCProfileIndication: " << ((int) m_configuration.AVCProfileIndication) << " (" << profileIndicationAsText() << ")\n"
158
1.76k
       << indent << "profile_compatibility: " << ((int) m_configuration.profile_compatibility) << "\n"
159
1.76k
       << indent << "AVCLevelIndication: " << ((int) m_configuration.AVCLevelIndication) << "\n"
160
1.76k
       << indent << "Chroma format: ";
161
162
1.76k
  switch (m_configuration.chroma_format) {
163
1.03k
    case heif_chroma_monochrome:
164
1.03k
      sstr << "4:0:0\n";
165
1.03k
      break;
166
573
    case heif_chroma_420:
167
573
      sstr << "4:2:0\n";
168
573
      break;
169
33
    case heif_chroma_422:
170
33
      sstr << "4:2:2\n";
171
33
      break;
172
117
    case heif_chroma_444:
173
117
      sstr << "4:4:4\n";
174
117
      break;
175
0
    default:
176
0
      sstr << "unsupported\n";
177
0
      break;
178
1.76k
  }
179
180
1.76k
  sstr << indent << "Bit depth luma: " << ((int) m_configuration.bit_depth_luma) << "\n"
181
1.76k
       << indent << "Bit depth chroma: " << ((int) m_configuration.bit_depth_chroma) << "\n";
182
183
2.59k
  for (const auto& sps : m_sps) {
184
2.59k
    sstr << indent << "SPS: ";
185
1.00M
    for (uint8_t b : sps) {
186
1.00M
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
187
1.00M
    }
188
2.59k
    sstr << "\n";
189
2.59k
    sstr << std::dec;
190
2.59k
  }
191
192
1.94k
  for (const auto& spsext : m_sps_ext) {
193
1.94k
    sstr << indent << "SPS-EXT: ";
194
1.94k
    for (uint8_t b : spsext) {
195
391
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
196
391
    }
197
1.94k
    sstr << "\n";
198
1.94k
    sstr << std::dec;
199
1.94k
  }
200
201
2.30k
  for (const auto& pps : m_pps) {
202
2.30k
    sstr << indent << "PPS: ";
203
133k
    for (uint8_t b : pps) {
204
133k
      sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
205
133k
    }
206
2.30k
    sstr << "\n";
207
2.30k
    sstr << std::dec;
208
2.30k
  }
209
210
1.76k
  return sstr.str();
211
1.76k
}
212
213
std::string Box_avcC::profileIndicationAsText() const
214
1.76k
{
215
  // See ISO/IEC 14496-10:2022 Annex A
216
1.76k
  switch (m_configuration.AVCProfileIndication) {
217
394
    case 44:
218
394
      return "CALVC 4:4:4";
219
17
    case 66:
220
17
      return "Constrained Baseline";
221
107
    case 77:
222
107
      return "Main";
223
419
    case 88:
224
419
      return "Extended";
225
24
    case 100:
226
24
      return "High variant";
227
48
    case 110:
228
48
      return "High 10";
229
16
    case 122:
230
16
      return "High 4:2:2";
231
204
    case 244:
232
204
      return "High 4:4:4";
233
532
    default:
234
532
      return "Unknown";
235
1.76k
  }
236
1.76k
}
237
238
239
void Box_avcC::get_header_nals(std::vector<uint8_t>& data) const
240
0
{
241
0
  for (const auto& sps : m_sps) {
242
0
    data.push_back((sps.size() >> 24) & 0xFF);
243
0
    data.push_back((sps.size() >> 16) & 0xFF);
244
0
    data.push_back((sps.size() >> 8) & 0xFF);
245
0
    data.push_back((sps.size() >> 0) & 0xFF);
246
247
0
    data.insert(data.end(), sps.begin(), sps.end());
248
0
  }
249
250
0
  for (const auto& spsext : m_sps_ext) {
251
0
    data.push_back((spsext.size() >> 24) & 0xFF);
252
0
    data.push_back((spsext.size() >> 16) & 0xFF);
253
0
    data.push_back((spsext.size() >> 8) & 0xFF);
254
0
    data.push_back((spsext.size() >> 0) & 0xFF);
255
256
0
    data.insert(data.end(), spsext.begin(), spsext.end());
257
0
  }
258
259
0
  for (const auto& pps : m_pps) {
260
0
    data.push_back((pps.size() >> 24) & 0xFF);
261
0
    data.push_back((pps.size() >> 16) & 0xFF);
262
0
    data.push_back((pps.size() >> 8) & 0xFF);
263
0
    data.push_back((pps.size() >> 0) & 0xFF);
264
265
0
    data.insert(data.end(), pps.begin(), pps.end());
266
0
  }
267
0
}