• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024, 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/parameters_manager.h"
13 
14 #include <algorithm>
15 #include <cstdint>
16 #include <optional>
17 #include <variant>
18 
19 #include "absl/container/flat_hash_map.h"
20 #include "absl/log/check.h"
21 #include "absl/log/log.h"
22 #include "absl/status/status.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/string_view.h"
25 #include "iamf/cli/audio_element_with_data.h"
26 #include "iamf/cli/cli_util.h"
27 #include "iamf/cli/parameter_block_with_data.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/obu/demixing_info_parameter_data.h"
30 #include "iamf/obu/demixing_param_definition.h"
31 #include "iamf/obu/param_definitions.h"
32 #include "iamf/obu/parameter_block.h"
33 #include "iamf/obu/recon_gain_info_parameter_data.h"
34 #include "iamf/obu/types.h"
35 
36 namespace iamf_tools {
37 
38 namespace {
39 
40 template <typename StateType>
UpdateParameterState(DecodedUleb128 audio_element_id,InternalTimestamp expected_next_timestamp,absl::flat_hash_map<DecodedUleb128,StateType> & parameter_states,absl::flat_hash_map<DecodedUleb128,const ParameterBlockWithData * > & parameter_blocks,absl::string_view parameter_name,std::optional<StateType * > & parameter_state)41 absl::Status UpdateParameterState(
42     DecodedUleb128 audio_element_id, InternalTimestamp expected_next_timestamp,
43     absl::flat_hash_map<DecodedUleb128, StateType>& parameter_states,
44     absl::flat_hash_map<DecodedUleb128, const ParameterBlockWithData*>&
45         parameter_blocks,
46     absl::string_view parameter_name,
47     std::optional<StateType*>& parameter_state) {
48   const auto parameter_states_iter = parameter_states.find(audio_element_id);
49   if (parameter_states_iter == parameter_states.end()) {
50     // No parameter definition found for the audio element ID, so
51     // nothing to update.
52     return absl::OkStatus();
53   }
54 
55   // Validate the timestamps before updating.
56   parameter_state = &parameter_states_iter->second;
57 
58   // Using `.at()` here is safe because if the parameter state exists for the
59   // `audio_element_id`, an entry in `parameter_blocks` with the key
60   // `parameter_state->param_definition->parameter_id_` has already been
61   // created during `Initialize()`.
62   auto& parameter_block =
63       parameter_blocks.at((*parameter_state)->param_definition->parameter_id_);
64   if (parameter_block == nullptr) {
65     // No parameter block found for this ID. Do not validate the timestamp
66     // or update anything else. Setting `parameter_state` to `std::nullopt`
67     // prevents the state being updated later.
68     parameter_state = std::nullopt;
69     return absl::OkStatus();
70   }
71 
72   // Update the next timestamp for the next frame.
73   (*parameter_state)->next_timestamp = parameter_block->end_timestamp;
74   RETURN_IF_NOT_OK(CompareTimestamps(
75       expected_next_timestamp, (*parameter_state)->next_timestamp,
76       absl::StrCat("When updating states for ", parameter_name,
77                    " parameters: ")));
78 
79   // Clear out the parameter block, which should not be used before a new
80   // one is added via `Add*ParameterBlock()`.
81   parameter_block = nullptr;
82 
83   return absl::OkStatus();
84 }
85 
86 }  // namespace
87 
ParametersManager(const absl::flat_hash_map<DecodedUleb128,AudioElementWithData> & audio_elements)88 ParametersManager::ParametersManager(
89     const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>&
90         audio_elements)
91     : audio_elements_(audio_elements) {}
92 
Initialize()93 absl::Status ParametersManager::Initialize() {
94   // Collect all `DemixingParamDefinition`s and all `ReconGainParamDefinitions`
95   // in all Audio Elements. Validate there is no more than one per Audio
96   // Element.
97   for (const auto& [audio_element_id, audio_element] : audio_elements_) {
98     const DemixingParamDefinition* demixing_param_definition = nullptr;
99     const ReconGainParamDefinition* recon_gain_param_definition = nullptr;
100     for (const auto& param : audio_element.obu.audio_element_params_) {
101       const auto param_definition_type = param.GetType();
102       if (param_definition_type ==
103           ParamDefinition::kParameterDefinitionDemixing) {
104         if (demixing_param_definition != nullptr) {
105           return absl::InvalidArgumentError(
106               "Not allowed to have multiple demixing parameters in a "
107               "single Audio Element.");
108         }
109         demixing_param_definition =
110             std::get_if<DemixingParamDefinition>(&param.param_definition);
111         CHECK_NE(demixing_param_definition, nullptr);
112 
113         // Continue searching. Only to validate that there is at most one
114         // `DemixingParamDefinition`.
115       } else if (param_definition_type ==
116                  ParamDefinition::kParameterDefinitionReconGain) {
117         if (recon_gain_param_definition != nullptr) {
118           return absl::InvalidArgumentError(
119               "Not allowed to have multiple recon gain parameters in a "
120               "single Audio Element.");
121         }
122         recon_gain_param_definition =
123             std::get_if<ReconGainParamDefinition>(&param.param_definition);
124         CHECK_NE(recon_gain_param_definition, nullptr);
125       }
126     }
127 
128     if (demixing_param_definition != nullptr) {
129       // Insert a `nullptr` for a parameter ID. If no parameter blocks have
130       // this parameter ID, then it will remain null and default values will
131       // be used.
132       demixing_parameter_blocks_.insert(
133           {demixing_param_definition->parameter_id_, nullptr});
134       demixing_states_[audio_element_id] = {
135           .param_definition = demixing_param_definition,
136           .previous_w_idx = 0,
137           .next_timestamp = 0,
138           .update_rule = DemixingInfoParameterData::kFirstFrame,
139       };
140     }
141     if (recon_gain_param_definition != nullptr) {
142       // Insert a `nullptr` for a parameter ID. If no parameter blocks have
143       // this parameter ID, then it will remain null and default values will
144       // be used.
145       recon_gain_parameter_blocks_.insert(
146           {recon_gain_param_definition->parameter_id_, nullptr});
147       recon_gain_states_[audio_element_id] = {
148           .param_definition = recon_gain_param_definition,
149           .next_timestamp = 0,
150       };
151     }
152   }
153 
154   return absl::OkStatus();
155 }
156 
DemixingParamDefinitionAvailable(const DecodedUleb128 audio_element_id)157 bool ParametersManager::DemixingParamDefinitionAvailable(
158     const DecodedUleb128 audio_element_id) {
159   return demixing_states_.find(audio_element_id) != demixing_states_.end();
160 }
161 
162 // This function will populate `down_mixing_params` as follows:
163 // 1) If no parameter definition of type kParameterDefinitionDemixing
164 //    exists, it will use sensible defaults.
165 // 2) If such a parameter definition exists but no demixing parameter
166 //    blocks are present, it will use the parameter definition defaults.
167 // 3) If such a parameter definition exists AND a demixing parameter block
168 //    is present, it will use the values provided in the parameter block.
GetDownMixingParameters(const DecodedUleb128 audio_element_id,DownMixingParams & down_mixing_params)169 absl::Status ParametersManager::GetDownMixingParameters(
170     const DecodedUleb128 audio_element_id,
171     DownMixingParams& down_mixing_params) {
172   const auto demixing_states_iter = demixing_states_.find(audio_element_id);
173   if (demixing_states_iter == demixing_states_.end()) {
174     LOG_FIRST_N(WARNING, 1)
175         << "No demixing parameter definition found for Audio "
176         << "Element with ID= " << audio_element_id
177         << "; using some sensible values.";
178 
179     down_mixing_params = {
180         0.707, 0.707, 0.707, 0.707, 0, 0, /*in_bitstream=*/false};
181     return absl::OkStatus();
182   }
183   auto& demixing_state = demixing_states_iter->second;
184   const auto* param_definition = demixing_state.param_definition;
185   const auto* demixing_parameter_block =
186       demixing_parameter_blocks_.at(param_definition->parameter_id_);
187   if (demixing_parameter_block == nullptr) {
188     // Failed to find a parameter block that overlaps this frame. Use the
189     // default value from the parameter definition. This is OK when there are
190     // no parameter blocks covering this substream. If there is only partial
191     // coverage this will be marked invalid when the coverage of parameter
192     // blocks is checked.
193     LOG_FIRST_N(WARNING, 10)
194         << "Failed to find a parameter block; using the default values";
195     RETURN_IF_NOT_OK(DemixingInfoParameterData::DMixPModeToDownMixingParams(
196         param_definition->default_demixing_info_parameter_data_.dmixp_mode,
197         param_definition->default_demixing_info_parameter_data_.default_w,
198         DemixingInfoParameterData::kDefault, down_mixing_params));
199     return absl::OkStatus();
200   }
201 
202   RETURN_IF_NOT_OK(CompareTimestamps(
203       demixing_state.next_timestamp, demixing_parameter_block->start_timestamp,
204       absl::StrCat("Getting down-mixing parameters for audio element ID= ",
205                    audio_element_id, ": ")));
206 
207   RETURN_IF_NOT_OK(DemixingInfoParameterData::DMixPModeToDownMixingParams(
208       static_cast<DemixingInfoParameterData*>(
209           demixing_parameter_block->obu->subblocks_[0].param_data.get())
210           ->dmixp_mode,
211       demixing_state.previous_w_idx, demixing_state.update_rule,
212       down_mixing_params));
213   demixing_state.w_idx = down_mixing_params.w_idx_used;
214   return absl::OkStatus();
215 }
216 
GetReconGainInfoParameterData(DecodedUleb128 audio_element_id,int32_t num_layers,ReconGainInfoParameterData & recon_gain_info_parameter_data)217 absl::Status ParametersManager::GetReconGainInfoParameterData(
218     DecodedUleb128 audio_element_id, int32_t num_layers,
219     ReconGainInfoParameterData& recon_gain_info_parameter_data) {
220   const auto recon_gain_states_iter = recon_gain_states_.find(audio_element_id);
221   if (recon_gain_states_iter == recon_gain_states_.end()) {
222     LOG_FIRST_N(WARNING, 1)
223         << "No recon gain parameter definition found for Audio "
224         << "Element with ID= " << audio_element_id
225         << "; setting recon gain to 255 (which represents a multiplier of 1.0, "
226            "i.e. a gain of 0 dB) in all layers";
227     for (int i = 0; i < num_layers; ++i) {
228       ReconGainElement recon_gain_element;
229       recon_gain_element.recon_gain_flag = DecodedUleb128(0);
230       std::fill(recon_gain_element.recon_gain.begin(),
231                 recon_gain_element.recon_gain.end(), 255);
232       recon_gain_info_parameter_data.recon_gain_elements.push_back(
233           recon_gain_element);
234     }
235     return absl::OkStatus();
236   }
237 
238   auto& recon_gain_state = recon_gain_states_iter->second;
239   const auto* param_definition = recon_gain_state.param_definition;
240   const auto* recon_gain_parameter_block =
241       recon_gain_parameter_blocks_.at(param_definition->parameter_id_);
242   if (recon_gain_parameter_block == nullptr) {
243     // Failed to find a parameter block that overlaps this frame. A default
244     // recon gain value of 0 dB is implied when there are no Parameter Block
245     // OBUs provided. This is OK when there are no parameter blocks covering
246     // this substream. If there is only partial coverage this will be marked
247     // invalid when the coverage of parameter blocks is checked.
248     LOG_FIRST_N(WARNING, 10)
249         << "Failed to find a recon gain parameter block; "
250            "A default recon gain value of 0 dB is implied when there are no "
251            "Parameter Block OBUs provided";
252 
253     for (int i = 0; i < num_layers; ++i) {
254       ReconGainElement recon_gain_element;
255       recon_gain_element.recon_gain_flag = DecodedUleb128(0);
256       std::fill(recon_gain_element.recon_gain.begin(),
257                 recon_gain_element.recon_gain.end(), 255);
258       recon_gain_info_parameter_data.recon_gain_elements.push_back(
259           recon_gain_element);
260     }
261 
262     return absl::OkStatus();
263   }
264 
265   RETURN_IF_NOT_OK(CompareTimestamps(
266       recon_gain_state.next_timestamp,
267       recon_gain_parameter_block->start_timestamp,
268       absl::StrCat("Getting recon gain parameters for audio element ID= ",
269                    audio_element_id, ": ")));
270 
271   auto recon_gain_info_parameter_data_in_obu =
272       static_cast<ReconGainInfoParameterData*>(
273           recon_gain_parameter_block->obu->subblocks_[0].param_data.get());
274   recon_gain_info_parameter_data.recon_gain_elements =
275       recon_gain_info_parameter_data_in_obu->recon_gain_elements;
276   return absl::OkStatus();
277 }
278 
AddDemixingParameterBlock(const ParameterBlockWithData * parameter_block)279 void ParametersManager::AddDemixingParameterBlock(
280     const ParameterBlockWithData* parameter_block) {
281   demixing_parameter_blocks_[parameter_block->obu->parameter_id_] =
282       parameter_block;
283 }
284 
AddReconGainParameterBlock(const ParameterBlockWithData * parameter_block)285 void ParametersManager::AddReconGainParameterBlock(
286     const ParameterBlockWithData* parameter_block) {
287   recon_gain_parameter_blocks_[parameter_block->obu->parameter_id_] =
288       parameter_block;
289 }
290 
UpdateDemixingState(DecodedUleb128 audio_element_id,InternalTimestamp expected_timestamp)291 absl::Status ParametersManager::UpdateDemixingState(
292     DecodedUleb128 audio_element_id, InternalTimestamp expected_timestamp) {
293   std::optional<DemixingState*> demixing_state = std::nullopt;
294   RETURN_IF_NOT_OK(UpdateParameterState(
295       audio_element_id, expected_timestamp, demixing_states_,
296       demixing_parameter_blocks_, "down-mixing", demixing_state));
297 
298   // Additional updating steps to perform after `UpdateParameterState()`.
299   if (demixing_state.has_value()) {
300     // Update `previous_w_idx` for the next frame.
301     (*demixing_state)->previous_w_idx = (*demixing_state)->w_idx;
302 
303     // Update the `update_rule` of the first frame to be "normally updating".
304     if ((*demixing_state)->update_rule ==
305         DemixingInfoParameterData::kFirstFrame) {
306       (*demixing_state)->update_rule = DemixingInfoParameterData::kNormal;
307     }
308   }
309   return absl::OkStatus();
310 }
311 
UpdateReconGainState(DecodedUleb128 audio_element_id,int32_t expected_timestamp)312 absl::Status ParametersManager::UpdateReconGainState(
313     DecodedUleb128 audio_element_id, int32_t expected_timestamp) {
314   std::optional<ReconGainState*> recon_gain_state = std::nullopt;
315 
316   // No additional updating needed.
317   return UpdateParameterState(audio_element_id, expected_timestamp,
318                               recon_gain_states_, recon_gain_parameter_blocks_,
319                               "down-mixing", recon_gain_state);
320 }
321 
322 }  // namespace iamf_tools
323