blob: ee4835efacb1af0d23a637fed25e4ecceb84f794 [file] [log] [blame]
/*
* Copyright (c) 2024, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 3-Clause Clear License
* and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
* License was not distributed with this source code in the LICENSE file, you
* can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
* Alliance for Open Media Patent License 1.0 was not distributed with this
* source code in the PATENTS file, you can obtain it at
* www.aomedia.org/license/patent.
*/
#include "iamf/cli/parameters_manager.h"
#include <algorithm>
#include <cstdint>
#include <optional>
#include <variant>
#include "absl/container/flat_hash_map.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "iamf/cli/audio_element_with_data.h"
#include "iamf/cli/cli_util.h"
#include "iamf/cli/parameter_block_with_data.h"
#include "iamf/common/utils/macros.h"
#include "iamf/obu/demixing_info_parameter_data.h"
#include "iamf/obu/demixing_param_definition.h"
#include "iamf/obu/param_definitions.h"
#include "iamf/obu/parameter_block.h"
#include "iamf/obu/recon_gain_info_parameter_data.h"
#include "iamf/obu/types.h"
namespace iamf_tools {
namespace {
template <typename StateType>
absl::Status 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) {
const auto parameter_states_iter = parameter_states.find(audio_element_id);
if (parameter_states_iter == parameter_states.end()) {
// No parameter definition found for the audio element ID, so
// nothing to update.
return absl::OkStatus();
}
// Validate the timestamps before updating.
parameter_state = &parameter_states_iter->second;
// Using `.at()` here is safe because if the parameter state exists for the
// `audio_element_id`, an entry in `parameter_blocks` with the key
// `parameter_state->param_definition->parameter_id_` has already been
// created during `Initialize()`.
auto& parameter_block =
parameter_blocks.at((*parameter_state)->param_definition->parameter_id_);
if (parameter_block == nullptr) {
// No parameter block found for this ID. Do not validate the timestamp
// or update anything else. Setting `parameter_state` to `std::nullopt`
// prevents the state being updated later.
parameter_state = std::nullopt;
return absl::OkStatus();
}
// Update the next timestamp for the next frame.
(*parameter_state)->next_timestamp = parameter_block->end_timestamp;
RETURN_IF_NOT_OK(CompareTimestamps(
expected_next_timestamp, (*parameter_state)->next_timestamp,
absl::StrCat("When updating states for ", parameter_name,
" parameters: ")));
// Clear out the parameter block, which should not be used before a new
// one is added via `Add*ParameterBlock()`.
parameter_block = nullptr;
return absl::OkStatus();
}
} // namespace
ParametersManager::ParametersManager(
const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>&
audio_elements)
: audio_elements_(audio_elements) {}
absl::Status ParametersManager::Initialize() {
// Collect all `DemixingParamDefinition`s and all `ReconGainParamDefinitions`
// in all Audio Elements. Validate there is no more than one per Audio
// Element.
for (const auto& [audio_element_id, audio_element] : audio_elements_) {
const DemixingParamDefinition* demixing_param_definition = nullptr;
const ReconGainParamDefinition* recon_gain_param_definition = nullptr;
for (const auto& param : audio_element.obu.audio_element_params_) {
const auto param_definition_type = param.GetType();
if (param_definition_type ==
ParamDefinition::kParameterDefinitionDemixing) {
if (demixing_param_definition != nullptr) {
return absl::InvalidArgumentError(
"Not allowed to have multiple demixing parameters in a "
"single Audio Element.");
}
demixing_param_definition =
std::get_if<DemixingParamDefinition>(&param.param_definition);
CHECK_NE(demixing_param_definition, nullptr);
// Continue searching. Only to validate that there is at most one
// `DemixingParamDefinition`.
} else if (param_definition_type ==
ParamDefinition::kParameterDefinitionReconGain) {
if (recon_gain_param_definition != nullptr) {
return absl::InvalidArgumentError(
"Not allowed to have multiple recon gain parameters in a "
"single Audio Element.");
}
recon_gain_param_definition =
std::get_if<ReconGainParamDefinition>(&param.param_definition);
CHECK_NE(recon_gain_param_definition, nullptr);
}
}
if (demixing_param_definition != nullptr) {
// Insert a `nullptr` for a parameter ID. If no parameter blocks have
// this parameter ID, then it will remain null and default values will
// be used.
demixing_parameter_blocks_.insert(
{demixing_param_definition->parameter_id_, nullptr});
demixing_states_[audio_element_id] = {
.param_definition = demixing_param_definition,
.previous_w_idx = 0,
.next_timestamp = 0,
.update_rule = DemixingInfoParameterData::kFirstFrame,
};
}
if (recon_gain_param_definition != nullptr) {
// Insert a `nullptr` for a parameter ID. If no parameter blocks have
// this parameter ID, then it will remain null and default values will
// be used.
recon_gain_parameter_blocks_.insert(
{recon_gain_param_definition->parameter_id_, nullptr});
recon_gain_states_[audio_element_id] = {
.param_definition = recon_gain_param_definition,
.next_timestamp = 0,
};
}
}
return absl::OkStatus();
}
bool ParametersManager::DemixingParamDefinitionAvailable(
const DecodedUleb128 audio_element_id) {
return demixing_states_.find(audio_element_id) != demixing_states_.end();
}
// This function will populate `down_mixing_params` as follows:
// 1) If no parameter definition of type kParameterDefinitionDemixing
// exists, it will use sensible defaults.
// 2) If such a parameter definition exists but no demixing parameter
// blocks are present, it will use the parameter definition defaults.
// 3) If such a parameter definition exists AND a demixing parameter block
// is present, it will use the values provided in the parameter block.
absl::Status ParametersManager::GetDownMixingParameters(
const DecodedUleb128 audio_element_id,
DownMixingParams& down_mixing_params) {
const auto demixing_states_iter = demixing_states_.find(audio_element_id);
if (demixing_states_iter == demixing_states_.end()) {
LOG_FIRST_N(WARNING, 1)
<< "No demixing parameter definition found for Audio "
<< "Element with ID= " << audio_element_id
<< "; using some sensible values.";
down_mixing_params = {
0.707, 0.707, 0.707, 0.707, 0, 0, /*in_bitstream=*/false};
return absl::OkStatus();
}
auto& demixing_state = demixing_states_iter->second;
const auto* param_definition = demixing_state.param_definition;
const auto* demixing_parameter_block =
demixing_parameter_blocks_.at(param_definition->parameter_id_);
if (demixing_parameter_block == nullptr) {
// Failed to find a parameter block that overlaps this frame. Use the
// default value from the parameter definition. This is OK when there are
// no parameter blocks covering this substream. If there is only partial
// coverage this will be marked invalid when the coverage of parameter
// blocks is checked.
LOG_FIRST_N(WARNING, 10)
<< "Failed to find a parameter block; using the default values";
RETURN_IF_NOT_OK(DemixingInfoParameterData::DMixPModeToDownMixingParams(
param_definition->default_demixing_info_parameter_data_.dmixp_mode,
param_definition->default_demixing_info_parameter_data_.default_w,
DemixingInfoParameterData::kDefault, down_mixing_params));
return absl::OkStatus();
}
RETURN_IF_NOT_OK(CompareTimestamps(
demixing_state.next_timestamp, demixing_parameter_block->start_timestamp,
absl::StrCat("Getting down-mixing parameters for audio element ID= ",
audio_element_id, ": ")));
RETURN_IF_NOT_OK(DemixingInfoParameterData::DMixPModeToDownMixingParams(
static_cast<DemixingInfoParameterData*>(
demixing_parameter_block->obu->subblocks_[0].param_data.get())
->dmixp_mode,
demixing_state.previous_w_idx, demixing_state.update_rule,
down_mixing_params));
demixing_state.w_idx = down_mixing_params.w_idx_used;
return absl::OkStatus();
}
absl::Status ParametersManager::GetReconGainInfoParameterData(
DecodedUleb128 audio_element_id, int32_t num_layers,
ReconGainInfoParameterData& recon_gain_info_parameter_data) {
const auto recon_gain_states_iter = recon_gain_states_.find(audio_element_id);
if (recon_gain_states_iter == recon_gain_states_.end()) {
LOG_FIRST_N(WARNING, 1)
<< "No recon gain parameter definition found for Audio "
<< "Element with ID= " << audio_element_id
<< "; setting recon gain to 255 (which represents a multiplier of 1.0, "
"i.e. a gain of 0 dB) in all layers";
for (int i = 0; i < num_layers; ++i) {
ReconGainElement recon_gain_element;
recon_gain_element.recon_gain_flag = DecodedUleb128(0);
std::fill(recon_gain_element.recon_gain.begin(),
recon_gain_element.recon_gain.end(), 255);
recon_gain_info_parameter_data.recon_gain_elements.push_back(
recon_gain_element);
}
return absl::OkStatus();
}
auto& recon_gain_state = recon_gain_states_iter->second;
const auto* param_definition = recon_gain_state.param_definition;
const auto* recon_gain_parameter_block =
recon_gain_parameter_blocks_.at(param_definition->parameter_id_);
if (recon_gain_parameter_block == nullptr) {
// Failed to find a parameter block that overlaps this frame. A default
// recon gain value of 0 dB is implied when there are no Parameter Block
// OBUs provided. This is OK when there are no parameter blocks covering
// this substream. If there is only partial coverage this will be marked
// invalid when the coverage of parameter blocks is checked.
LOG_FIRST_N(WARNING, 10)
<< "Failed to find a recon gain parameter block; "
"A default recon gain value of 0 dB is implied when there are no "
"Parameter Block OBUs provided";
for (int i = 0; i < num_layers; ++i) {
ReconGainElement recon_gain_element;
recon_gain_element.recon_gain_flag = DecodedUleb128(0);
std::fill(recon_gain_element.recon_gain.begin(),
recon_gain_element.recon_gain.end(), 255);
recon_gain_info_parameter_data.recon_gain_elements.push_back(
recon_gain_element);
}
return absl::OkStatus();
}
RETURN_IF_NOT_OK(CompareTimestamps(
recon_gain_state.next_timestamp,
recon_gain_parameter_block->start_timestamp,
absl::StrCat("Getting recon gain parameters for audio element ID= ",
audio_element_id, ": ")));
auto recon_gain_info_parameter_data_in_obu =
static_cast<ReconGainInfoParameterData*>(
recon_gain_parameter_block->obu->subblocks_[0].param_data.get());
recon_gain_info_parameter_data.recon_gain_elements =
recon_gain_info_parameter_data_in_obu->recon_gain_elements;
return absl::OkStatus();
}
void ParametersManager::AddDemixingParameterBlock(
const ParameterBlockWithData* parameter_block) {
demixing_parameter_blocks_[parameter_block->obu->parameter_id_] =
parameter_block;
}
void ParametersManager::AddReconGainParameterBlock(
const ParameterBlockWithData* parameter_block) {
recon_gain_parameter_blocks_[parameter_block->obu->parameter_id_] =
parameter_block;
}
absl::Status ParametersManager::UpdateDemixingState(
DecodedUleb128 audio_element_id, InternalTimestamp expected_timestamp) {
std::optional<DemixingState*> demixing_state = std::nullopt;
RETURN_IF_NOT_OK(UpdateParameterState(
audio_element_id, expected_timestamp, demixing_states_,
demixing_parameter_blocks_, "down-mixing", demixing_state));
// Additional updating steps to perform after `UpdateParameterState()`.
if (demixing_state.has_value()) {
// Update `previous_w_idx` for the next frame.
(*demixing_state)->previous_w_idx = (*demixing_state)->w_idx;
// Update the `update_rule` of the first frame to be "normally updating".
if ((*demixing_state)->update_rule ==
DemixingInfoParameterData::kFirstFrame) {
(*demixing_state)->update_rule = DemixingInfoParameterData::kNormal;
}
}
return absl::OkStatus();
}
absl::Status ParametersManager::UpdateReconGainState(
DecodedUleb128 audio_element_id, int32_t expected_timestamp) {
std::optional<ReconGainState*> recon_gain_state = std::nullopt;
// No additional updating needed.
return UpdateParameterState(audio_element_id, expected_timestamp,
recon_gain_states_, recon_gain_parameter_blocks_,
"down-mixing", recon_gain_state);
}
} // namespace iamf_tools