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 = ¶meter_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>(¶m.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>(¶m.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