Shaka Packager SDK
Loading...
Searching...
No Matches
iamf_audio_util.cc
1// Copyright 2024 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/codecs/iamf_audio_util.h>
8
9#include <cstddef>
10#include <cstdint>
11#include <iomanip>
12#include <ios>
13#include <vector>
14
15#include <absl/log/log.h>
16
17#include <packager/media/base/bit_reader.h>
18#include <packager/media/base/fourccs.h>
19#include <packager/media/base/rcheck.h>
20#include <packager/media/base/stream_info.h>
21
22namespace shaka {
23namespace media {
24
25namespace {
26
27const uint8_t kMaxIamfProfile = 2;
28
29// 3.2. OBU type
30// Only the IA Sequence Header and Codec Configs are used in this
31// implementation.
32enum ObuType {
33 OBU_IA_Codec_Config = 0,
34 OBU_IA_Sequence_Header = 31,
35};
36
37// 8.1.1. leb128(). Unsigned integer represented by a variable number of
38// little-endian bytes.
39bool ReadLeb128(BitReader& reader, size_t* size, size_t* leb128_bytes) {
40 size_t value = 0;
41 size_t bytes_read = 0;
42 for (int i = 0; i < 8; i++) {
43 size_t leb128_byte = 0;
44 RCHECK(reader.ReadBits(8, &leb128_byte));
45 value |= (leb128_byte & 0x7f) << (i * 7);
46 bytes_read += 1;
47 if (!(leb128_byte & 0x80))
48 break;
49 }
50 // It is a requirement of bitstream conformance that the value returned from
51 // the leb128 parsing process is less than or equal to (1<<32) - 1.
52 RCHECK(value <= ((1ull << 32) - 1));
53 if (size != nullptr) {
54 *size = value;
55 }
56 if (leb128_bytes != nullptr) {
57 *leb128_bytes = bytes_read;
58 }
59 return true;
60}
61
62bool ParseObuHeader(BitReader& reader, int& obu_type, size_t& obu_size) {
63 size_t leb128_bytes;
64 bool obu_trimming_status_flag;
65 bool obu_extension_flag;
66
67 RCHECK(reader.ReadBits(5, &obu_type));
68 RCHECK(reader.SkipBits(1)); // Skip obu_redundant_copy
69 RCHECK(reader.ReadBits(1, &obu_trimming_status_flag));
70 RCHECK(reader.ReadBits(1, &obu_extension_flag));
71
72 RCHECK(ReadLeb128(reader, &obu_size, &leb128_bytes));
73
74 if (obu_trimming_status_flag) {
75 // Skip num_samples_to_trim_at_end
76 RCHECK(ReadLeb128(reader, nullptr, &leb128_bytes));
77 obu_size -= leb128_bytes;
78 // Skip num_samples_to_trim_at_start
79 RCHECK(ReadLeb128(reader, nullptr, &leb128_bytes));
80 obu_size -= leb128_bytes;
81 }
82
83 if (obu_extension_flag) {
84 size_t extension_header_size;
85 RCHECK(ReadLeb128(reader, &extension_header_size, &leb128_bytes));
86 obu_size -= leb128_bytes;
87 RCHECK(reader.SkipBits(extension_header_size * 8));
88 obu_size -= extension_header_size * 8;
89 }
90
91 return true;
92}
93
94bool ParseSequenceHeaderObu(BitReader& reader,
95 uint8_t& primary_profile,
96 uint8_t& additional_profile) {
97 uint32_t ia_code;
98
99 RCHECK(reader.ReadBits(32, &ia_code));
100 if (ia_code != FOURCC_iamf) {
101 LOG(WARNING) << "Unknown ia_code= " << std::setfill('0') << std::setw(8)
102 << std::hex << ia_code;
103 return false;
104 }
105
106 RCHECK(reader.ReadBits(8, &primary_profile));
107 if (primary_profile > kMaxIamfProfile) {
108 LOG(WARNING) << "Unknown primary_profile= " << primary_profile;
109 return false;
110 }
111
112 RCHECK(reader.ReadBits(8, &additional_profile));
113 if (additional_profile > kMaxIamfProfile) {
114 LOG(WARNING) << "Unknown additional_profile= " << additional_profile;
115 return false;
116 }
117
118 return true;
119}
120
121bool ParseCodecConfigObu(BitReader& reader, size_t obu_size, Codec& codec) {
122 uint32_t codec_id;
123 size_t leb128_bytes;
124
125 // Skip codec_config_id
126 RCHECK(ReadLeb128(reader, nullptr, &leb128_bytes));
127 obu_size -= leb128_bytes;
128
129 RCHECK(reader.ReadBits(32, &codec_id));
130 obu_size -= 4;
131
132 // Skip the remainder of the OBU.
133 RCHECK(reader.SkipBits(obu_size * 8));
134
135 switch (codec_id) {
136 case FOURCC_Opus:
137 codec = kCodecOpus;
138 break;
139 case FOURCC_mp4a:
140 codec = kCodecAAC;
141 break;
142 case FOURCC_fLaC:
143 codec = kCodecFlac;
144 break;
145 case FOURCC_ipcm:
146 codec = kCodecPcm;
147 break;
148 default:
149 LOG(WARNING) << "Unknown codec_id= " << std::setfill('0') << std::setw(8)
150 << std::hex << codec_id;
151 return false;
152 }
153
154 return true;
155}
156} // namespace
157
158bool GetIamfCodecStringInfo(const std::vector<uint8_t>& iacb,
159 uint8_t& codec_string_info) {
160 uint8_t primary_profile = 0;
161 uint8_t additional_profile = 0;
162 // codec used to encode IAMF audio substreams
163 Codec iamf_codec = Codec::kUnknownCodec;
164
165 int obu_type;
166 size_t obu_size;
167
168 BitReader reader(iacb.data(), iacb.size());
169
170 // configurationVersion
171 RCHECK(reader.SkipBits(8));
172
173 // configOBUs_size
174 RCHECK(ReadLeb128(reader, &obu_size, nullptr));
175
176 while (reader.bits_available() > 0) {
177 RCHECK(ParseObuHeader(reader, obu_type, obu_size));
178
179 switch (obu_type) {
180 case OBU_IA_Sequence_Header:
181 RCHECK(ParseSequenceHeaderObu(reader, primary_profile,
182 additional_profile));
183 break;
184 case OBU_IA_Codec_Config:
185 RCHECK(ParseCodecConfigObu(reader, obu_size, iamf_codec));
186 break;
187 default:
188 // Skip other irrelevant OBUs.
189 RCHECK(reader.SkipBits(obu_size * 8));
190 break;
191 }
192 }
193
194 // In IAMF v1.1 (https://aomediacodec.github.io/iamf),
195 // the valid values of primary_profile and additional_profile are {0, 1, 2}.
196 // The valid codec_ids are {Opus, mp4a, fLaC, ipcm}.
197 //
198 // This can be represented in uint8_t as:
199 // primary_profile (2bits) + additional_profile (2bits) + iamf_codec (4bits),
200 // where iamf_codec is represented using the Codec enum.
201 //
202 // Since iamf_codec is limited to 16 values, subtract the value of kCodecAudio
203 // to ensure it fits. If future audio codecs are added to the Codec enum,
204 // it may break the assumption that IAMF supported codecs are present within
205 // the first 16 audio codec entries.
206 // Further, if these values change in future version of IAMF, this format may
207 // need to be changed, and AudioStreamInfo::GetCodecString needs to be updated
208 // accordingly.
209 codec_string_info =
210 ((primary_profile << 6) | ((additional_profile << 4) & 0x3F) |
211 ((iamf_codec - kCodecAudio) & 0xF));
212
213 return true;
214}
215
216} // namespace media
217} // namespace shaka
All the methods that are virtual are virtual for mocking.