1 /*
2 * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12 #include "iamf/cli/cli_util.h"
13
14 #include <array>
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstdio>
18 #include <list>
19 #include <string>
20 #include <utility>
21 #include <variant>
22 #include <vector>
23
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/container/flat_hash_set.h"
26 #include "absl/log/log.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/string_view.h"
30 #include "iamf/cli/audio_element_with_data.h"
31 #include "iamf/common/utils/macros.h"
32 #include "iamf/common/utils/sample_processing_utils.h"
33 #include "iamf/obu/audio_element.h"
34 #include "iamf/obu/codec_config.h"
35 #include "iamf/obu/demixing_param_definition.h"
36 #include "iamf/obu/mix_presentation.h"
37 #include "iamf/obu/param_definition_variant.h"
38 #include "iamf/obu/param_definitions.h"
39 #include "iamf/obu/types.h"
40
41 namespace iamf_tools {
42
43 namespace {
44
InsertParamDefinitionAndCheckEquivalence(const ParamDefinitionVariant & param_definition_variant_to_insert,absl::flat_hash_map<DecodedUleb128,ParamDefinitionVariant> & param_definition_variants)45 absl::Status InsertParamDefinitionAndCheckEquivalence(
46 const ParamDefinitionVariant& param_definition_variant_to_insert,
47 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>&
48 param_definition_variants) {
49 const auto parameter_id = std::visit(
50 [](const auto& param_definition) {
51 return param_definition.parameter_id_;
52 },
53 param_definition_variant_to_insert);
54 const auto [existing_param_definition_iter, inserted] =
55 param_definition_variants.insert(
56 {parameter_id, param_definition_variant_to_insert});
57
58 // Use double dispatch to check equivalence. Note this automatically returns
59 // false when the two variants do not hold the same type of objects.
60 const auto equivalent_to_param_definition_variant_to_insert =
61 [¶m_definition_variant_to_insert](const auto& rhs) {
62 return std::visit([&rhs](const auto& lhs) { return (lhs == rhs); },
63 param_definition_variant_to_insert);
64 };
65
66 if (!inserted && !std::visit(equivalent_to_param_definition_variant_to_insert,
67 existing_param_definition_iter->second)) {
68 return absl::InvalidArgumentError(absl::StrCat(
69 "Inequivalent `param_definition` for id = ", parameter_id));
70 }
71
72 return absl::OkStatus();
73 };
74
FillReconGainAuxiliaryData(const AudioElementWithData & audio_element,std::vector<ReconGainParamDefinition::ReconGainAuxiliaryData> & aux_data)75 void FillReconGainAuxiliaryData(
76 const AudioElementWithData& audio_element,
77 std::vector<ReconGainParamDefinition::ReconGainAuxiliaryData>& aux_data) {
78 const auto& channel_config =
79 std::get<ScalableChannelLayoutConfig>(audio_element.obu.config_);
80 aux_data.resize(channel_config.num_layers);
81 for (int l = 0; l < channel_config.num_layers; l++) {
82 aux_data[l].recon_gain_is_present_flag =
83 (channel_config.channel_audio_layer_configs[l]
84 .recon_gain_is_present_flag == 1);
85 aux_data[l].channel_numbers_for_layer =
86 audio_element.channel_numbers_for_layers[l];
87 }
88 }
89
90 } // namespace
91
IsStereoLayout(const Layout & layout)92 bool IsStereoLayout(const Layout& layout) {
93 const Layout kStereoLayout = {
94 .layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
95 .specific_layout = LoudspeakersSsConventionLayout{
96 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0}};
97 return layout == kStereoLayout;
98 }
99
GetIndicesForLayout(const std::vector<MixPresentationSubMix> & mix_presentation_sub_mixes,const Layout & layout,int & output_submix_index,int & output_layout_index)100 absl::Status GetIndicesForLayout(
101 const std::vector<MixPresentationSubMix>& mix_presentation_sub_mixes,
102 const Layout& layout, int& output_submix_index, int& output_layout_index) {
103 for (int s = 0; s < mix_presentation_sub_mixes.size(); s++) {
104 const auto& sub_mix = mix_presentation_sub_mixes[s];
105 for (int l = 0; l < sub_mix.layouts.size(); l++) {
106 const auto& mix_presentation_layout = sub_mix.layouts[l];
107 if (layout == mix_presentation_layout.loudness_layout) {
108 output_submix_index = s;
109 output_layout_index = l;
110 return absl::OkStatus();
111 }
112 }
113 }
114 return absl::InvalidArgumentError(
115 "No match found in the mix presentation submixes for the desired "
116 "layout.");
117 }
118
CollectAndValidateParamDefinitions(const absl::flat_hash_map<DecodedUleb128,AudioElementWithData> & audio_elements,const std::list<MixPresentationObu> & mix_presentation_obus,absl::flat_hash_map<DecodedUleb128,ParamDefinitionVariant> & param_definition_variants)119 absl::Status CollectAndValidateParamDefinitions(
120 const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>&
121 audio_elements,
122 const std::list<MixPresentationObu>& mix_presentation_obus,
123 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>&
124 param_definition_variants) {
125 param_definition_variants.clear();
126
127 // Collect all `param_definition`s in Audio Element and Mix Presentation
128 // OBUs.
129 for (const auto& [audio_element_id_for_debugging, audio_element] :
130 audio_elements) {
131 for (const auto& audio_element_param :
132 audio_element.obu.audio_element_params_) {
133 const auto param_definition_type = audio_element_param.GetType();
134 switch (param_definition_type) {
135 case ParamDefinition::kParameterDefinitionDemixing:
136 RETURN_IF_NOT_OK(InsertParamDefinitionAndCheckEquivalence(
137 std::get<DemixingParamDefinition>(
138 audio_element_param.param_definition),
139 param_definition_variants));
140 break;
141 case ParamDefinition::kParameterDefinitionReconGain: {
142 // Make a copy, which will be modified.
143 ReconGainParamDefinition recon_gain_param_definition =
144 std::get<ReconGainParamDefinition>(
145 audio_element_param.param_definition);
146 FillReconGainAuxiliaryData(audio_element,
147 recon_gain_param_definition.aux_data_);
148 RETURN_IF_NOT_OK(InsertParamDefinitionAndCheckEquivalence(
149 recon_gain_param_definition, param_definition_variants));
150 break;
151 }
152 default:
153 LOG(WARNING) << "Ignoring parameter definition of type= "
154 << param_definition_type << " in audio element= "
155 << audio_element_id_for_debugging;
156 continue;
157 }
158 }
159 }
160
161 for (const auto& mix_presentation_obu : mix_presentation_obus) {
162 for (const auto& sub_mix : mix_presentation_obu.sub_mixes_) {
163 for (const auto& audio_element : sub_mix.audio_elements) {
164 RETURN_IF_NOT_OK(InsertParamDefinitionAndCheckEquivalence(
165 audio_element.element_mix_gain, param_definition_variants));
166 }
167 RETURN_IF_NOT_OK(InsertParamDefinitionAndCheckEquivalence(
168 sub_mix.output_mix_gain, param_definition_variants));
169 }
170 }
171
172 return absl::OkStatus();
173 }
174
CompareTimestamps(InternalTimestamp expected_timestamp,InternalTimestamp actual_timestamp,absl::string_view prompt)175 absl::Status CompareTimestamps(InternalTimestamp expected_timestamp,
176 InternalTimestamp actual_timestamp,
177 absl::string_view prompt) {
178 if (expected_timestamp != actual_timestamp) {
179 return absl::InvalidArgumentError(
180 absl::StrCat(prompt, "Expected timestamp != actual timestamp: (",
181 expected_timestamp, " vs ", actual_timestamp, ")"));
182 }
183 return absl::OkStatus();
184 }
185
WritePcmFrameToBuffer(const std::vector<std::vector<int32_t>> & frame,uint8_t bit_depth,bool big_endian,std::vector<uint8_t> & buffer)186 absl::Status WritePcmFrameToBuffer(
187 const std::vector<std::vector<int32_t>>& frame, uint8_t bit_depth,
188 bool big_endian, std::vector<uint8_t>& buffer) {
189 if (bit_depth % 8 != 0) [[unlikely]] {
190 return absl::InvalidArgumentError(
191 "This function only supports an integer number of bytes.");
192 }
193 const size_t num_samples = frame.size() * frame[0].size();
194 buffer.resize(num_samples * (bit_depth / 8));
195
196 // The input frame is arranged in (time, channel) axes. Interlace these in
197 // the output PCM.
198 size_t write_position = 0;
199 for (int t = 0; t < frame.size(); t++) {
200 for (int c = 0; c < frame[0].size(); ++c) {
201 const uint32_t sample = static_cast<uint32_t>(frame[t][c]);
202 RETURN_IF_NOT_OK(WritePcmSample(sample, bit_depth, big_endian,
203 buffer.data(), write_position));
204 }
205 }
206
207 return absl::OkStatus();
208 }
209
GetCommonSampleRateAndBitDepth(const absl::flat_hash_set<uint32_t> & sample_rates,const absl::flat_hash_set<uint8_t> & bit_depths,uint32_t & common_sample_rate,uint8_t & common_bit_depth,bool & requires_resampling)210 absl::Status GetCommonSampleRateAndBitDepth(
211 const absl::flat_hash_set<uint32_t>& sample_rates,
212 const absl::flat_hash_set<uint8_t>& bit_depths,
213 uint32_t& common_sample_rate, uint8_t& common_bit_depth,
214 bool& requires_resampling) {
215 requires_resampling = false;
216 if (sample_rates.empty() || bit_depths.empty()) {
217 return absl::InvalidArgumentError(
218 "Expected at least one sample rate and bit depth.");
219 }
220
221 if (sample_rates.size() == 1) {
222 common_sample_rate = *sample_rates.begin();
223 } else {
224 // No common sample rate. The spec recommends the rendering output to be
225 // resampled to 48000 Hz.
226 common_sample_rate = 48000;
227 requires_resampling = true;
228 }
229
230 if (bit_depths.size() == 1) {
231 common_bit_depth = *bit_depths.begin();
232 } else {
233 // No common bit-depth. The spec recommends the rendering output to be
234 // resampled to 16-bits.
235 common_bit_depth = 16;
236 requires_resampling = true;
237 }
238
239 return absl::OkStatus();
240 }
241
GetCommonSamplesPerFrame(const absl::flat_hash_map<uint32_t,CodecConfigObu> & codec_config_obus,uint32_t & common_samples_per_frame)242 absl::Status GetCommonSamplesPerFrame(
243 const absl::flat_hash_map<uint32_t, CodecConfigObu>& codec_config_obus,
244 uint32_t& common_samples_per_frame) {
245 bool first = true;
246 for (const auto& [unused_id, codec_config_obu] : codec_config_obus) {
247 if (first) {
248 common_samples_per_frame = codec_config_obu.GetNumSamplesPerFrame();
249 first = false;
250 continue;
251 }
252
253 if (common_samples_per_frame != codec_config_obu.GetNumSamplesPerFrame()) {
254 return absl::UnknownError(
255 "The encoder does not support Codec Config OBUs with a different "
256 "number of samples per frame yet.");
257 }
258 }
259
260 return absl::OkStatus();
261 }
262
LogChannelNumbers(const std::string & name,const ChannelNumbers & channel_numbers)263 void LogChannelNumbers(const std::string& name,
264 const ChannelNumbers& channel_numbers) {
265 LOG(INFO) << name << ": [" << channel_numbers.surround << "."
266 << channel_numbers.lfe << "." << channel_numbers.height << "]";
267 }
268
269 } // namespace iamf_tools
270