• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       [&param_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