blob: 40b32c5af245169028c96a5cdd7d1a3f2798300b [file] [log] [blame]
/*
* Copyright (c) 2023, 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/obu/mix_presentation.h"
#include <cstdint>
#include <string>
#include <vector>
#include "absl/base/no_destructor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "iamf/common/read_bit_buffer.h"
#include "iamf/common/utils/macros.h"
#include "iamf/common/utils/map_utils.h"
#include "iamf/common/utils/numeric_utils.h"
#include "iamf/common/utils/validation_utils.h"
#include "iamf/common/write_bit_buffer.h"
#include "iamf/obu/obu_header.h"
#include "iamf/obu/param_definitions.h"
#include "iamf/obu/types.h"
namespace iamf_tools {
namespace {
absl::Status ValidateUniqueAudioElementIds(
const std::vector<MixPresentationSubMix>& sub_mixes) {
std::vector<DecodedUleb128> collected_audio_element_ids;
// Audio Element IDs must be unique across all sub-mixes.
for (const auto& sub_mix : sub_mixes) {
for (const auto& audio_element : sub_mix.audio_elements) {
collected_audio_element_ids.push_back(audio_element.audio_element_id);
}
}
return ValidateUnique(collected_audio_element_ids.begin(),
collected_audio_element_ids.end(), "Audio element IDs");
}
absl::Status ValidateUniqueAnchorElements(
const std::vector<AnchoredLoudnessElement>& anchor_elements) {
std::vector<uint8_t> anchor_elements_as_uint8;
anchor_elements_as_uint8.reserve(anchor_elements.size());
for (const auto& anchor_element : anchor_elements) {
anchor_elements_as_uint8.push_back(
static_cast<uint8_t>(anchor_element.anchor_element));
}
return ValidateUnique(anchor_elements_as_uint8.begin(),
anchor_elements_as_uint8.end(),
"Anchored loudness types");
}
absl::Status ValidateAndWriteSubMixAudioElement(
DecodedUleb128 count_label, const SubMixAudioElement& element,
WriteBitBuffer& wb) {
// Write the main portion of an `SubMixAudioElement`.
RETURN_IF_NOT_OK(wb.WriteUleb128(element.audio_element_id));
RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
absl::StrCat("localized_element_annotations with audio_element_id= ",
element.audio_element_id),
element.localized_element_annotations, count_label));
for (const auto& localized_element_annotation :
element.localized_element_annotations) {
RETURN_IF_NOT_OK(wb.WriteString(localized_element_annotation));
}
// Write out `rendering_config`.
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
static_cast<uint8_t>(element.rendering_config.headphones_rendering_mode),
2));
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
static_cast<uint8_t>(element.rendering_config.reserved), 6));
RETURN_IF_NOT_OK(wb.WriteUleb128(
element.rendering_config.rendering_config_extension_size));
RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
"rendering_config_extension_bytes",
element.rendering_config.rendering_config_extension_bytes,
element.rendering_config.rendering_config_extension_size));
RETURN_IF_NOT_OK(wb.WriteUint8Span(absl::MakeConstSpan(
element.rendering_config.rendering_config_extension_bytes)));
RETURN_IF_NOT_OK(element.element_mix_gain.ValidateAndWrite(wb));
return absl::OkStatus();
}
// Writes and validates a `MixPresentationLayout and sets `found_stereo_layout`
// to if it is a stereo layout.
absl::Status ValidateAndWriteLayout(const MixPresentationLayout& layout,
bool& found_stereo_layout,
WriteBitBuffer& wb) {
// Write the `loudness_layout` portion of a `MixPresentationLayout`.
RETURN_IF_NOT_OK(
wb.WriteUnsignedLiteral(layout.loudness_layout.layout_type, 2));
// Write the specific type of `Layout` dependent on `layout_type`.
switch (layout.loudness_layout.layout_type) {
using enum Layout::LayoutType;
case kLayoutTypeLoudspeakersSsConvention:
RETURN_IF_NOT_OK(std::get<LoudspeakersSsConventionLayout>(
layout.loudness_layout.specific_layout)
.Write(found_stereo_layout, wb));
break;
case kLayoutTypeReserved0:
case kLayoutTypeReserved1:
case kLayoutTypeBinaural:
RETURN_IF_NOT_OK(std::get<LoudspeakersReservedOrBinauralLayout>(
layout.loudness_layout.specific_layout)
.Write(wb));
break;
}
// Write the `loudness` portion of a `MixPresentationLayout`.
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(layout.loudness.info_type, 8));
RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.integrated_loudness));
RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.digital_peak));
// Conditionally write `true_peak` based on `info_type`.
if ((layout.loudness.info_type & LoudnessInfo::kTruePeak) != 0) {
RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.true_peak));
}
// Conditionally write `anchored_loudness` based on `info_type`.
if ((layout.loudness.info_type & LoudnessInfo::kAnchoredLoudness) != 0) {
MAYBE_RETURN_IF_NOT_OK(ValidateUniqueAnchorElements(
layout.loudness.anchored_loudness.anchor_elements));
const AnchoredLoudness& anchored_loudness =
layout.loudness.anchored_loudness;
uint8_t num_anchor_elements;
RETURN_IF_NOT_OK(StaticCastIfInRange(
"num_anchor_elements", anchored_loudness.anchor_elements.size(),
num_anchor_elements));
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(num_anchor_elements, 8));
for (const auto& anchor_element : anchored_loudness.anchor_elements) {
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
static_cast<uint8_t>(anchor_element.anchor_element), 8));
RETURN_IF_NOT_OK(wb.WriteSigned16(anchor_element.anchored_loudness));
}
}
// Conditionally write `layout_extension` based on `info_type`.
if ((layout.loudness.info_type & LoudnessInfo::kAnyLayoutExtension) != 0) {
RETURN_IF_NOT_OK(
wb.WriteUleb128(layout.loudness.layout_extension.info_type_size));
RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
"info_type_bytes", layout.loudness.layout_extension.info_type_bytes,
layout.loudness.layout_extension.info_type_size));
RETURN_IF_NOT_OK(wb.WriteUint8Span(
absl::MakeConstSpan(layout.loudness.layout_extension.info_type_bytes)));
}
return absl::OkStatus();
}
absl::Status ValidateAndWriteSubMix(DecodedUleb128 count_label,
const MixPresentationSubMix& sub_mix,
WriteBitBuffer& wb) {
// IAMF requires there to be at least one audio element.
const DecodedUleb128 num_audio_elements = sub_mix.audio_elements.size();
RETURN_IF_NOT_OK(ValidateNotEqual(DecodedUleb128{0}, num_audio_elements,
"num_audio_elements"));
// Write the main portion of a `MixPresentationSubMix`.
RETURN_IF_NOT_OK(wb.WriteUleb128(num_audio_elements));
// Loop to write the `audio_elements` array.
for (const auto& sub_mix_audio_element : sub_mix.audio_elements) {
RETURN_IF_NOT_OK(ValidateAndWriteSubMixAudioElement(
count_label, sub_mix_audio_element, wb));
}
RETURN_IF_NOT_OK(sub_mix.output_mix_gain.ValidateAndWrite(wb));
const DecodedUleb128 num_layouts = sub_mix.layouts.size();
RETURN_IF_NOT_OK(wb.WriteUleb128(num_layouts));
// Loop to write the `layouts` array.
bool found_stereo_layout = false;
for (const auto& layout : sub_mix.layouts) {
RETURN_IF_NOT_OK(ValidateAndWriteLayout(layout, found_stereo_layout, wb));
}
if (!found_stereo_layout) {
return absl::InvalidArgumentError(
"Every sub-mix must have a stereo layout.");
}
return absl::OkStatus();
}
absl::Status ValidateNumSubMixes(DecodedUleb128 num_sub_mixes) {
MAYBE_RETURN_IF_NOT_OK(
ValidateNotEqual(DecodedUleb128{0}, num_sub_mixes, "num_sub_mixes"));
return absl::OkStatus();
}
} // namespace
absl::Status Layout::ReadAndValidate(ReadBitBuffer& rb) {
uint8_t layout_type_uint;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, layout_type_uint));
layout_type = static_cast<Layout::LayoutType>(layout_type_uint);
// Read the specific type of `Layout` dependent on `layout_type`.
switch (layout_type) {
using enum Layout::LayoutType;
case kLayoutTypeLoudspeakersSsConvention:
specific_layout = LoudspeakersSsConventionLayout();
return std::get<LoudspeakersSsConventionLayout>(specific_layout).Read(rb);
case kLayoutTypeReserved0:
case kLayoutTypeReserved1:
// Reserved layouts are identical to binaural layouts as of IAMF
// v1.1.0 aomediacodec.github.io/iamf/v1.1.0.html#syntax-layout.
case kLayoutTypeBinaural:
specific_layout = LoudspeakersReservedOrBinauralLayout();
return std::get<LoudspeakersReservedOrBinauralLayout>(specific_layout)
.Read(rb);
}
return absl::InternalError(absl::StrCat(
"Unexpected value for 2-bit Layout::LayoutType = ", layout_type));
}
absl::Status MixPresentationLayout::ReadAndValidate(ReadBitBuffer& rb) {
// Read the `loudness_layout` portion of a `MixPresentationLayout`.
RETURN_IF_NOT_OK(loudness_layout.ReadAndValidate(rb));
// Read the `loudness` portion of a `MixPresentationLayout`.
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, loudness.info_type));
RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.integrated_loudness));
RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.digital_peak));
// Conditionally read `true_peak` based on `info_type`.
if (loudness.info_type & LoudnessInfo::kTruePeak) {
RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.true_peak));
}
// Conditionally read `anchored_loudness` based on `info_type`.
if (loudness.info_type & LoudnessInfo::kAnchoredLoudness) {
uint8_t num_anchored_loudness;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, num_anchored_loudness));
for (int i = 0; i < num_anchored_loudness; ++i) {
AnchoredLoudnessElement anchor_loudness_element;
uint8_t anchor_element;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, anchor_element));
anchor_loudness_element.anchor_element =
static_cast<AnchoredLoudnessElement::AnchorElement>(anchor_element);
RETURN_IF_NOT_OK(
rb.ReadSigned16(anchor_loudness_element.anchored_loudness));
loudness.anchored_loudness.anchor_elements.push_back(
anchor_loudness_element);
}
RETURN_IF_NOT_OK(ValidateUniqueAnchorElements(
loudness.anchored_loudness.anchor_elements));
}
// Conditionally read `layout_extension` based on `info_type`.
if (loudness.info_type & LoudnessInfo::kAnyLayoutExtension) {
RETURN_IF_NOT_OK(rb.ReadULeb128(loudness.layout_extension.info_type_size));
loudness.layout_extension.info_type_bytes.resize(
loudness.layout_extension.info_type_size);
RETURN_IF_NOT_OK(rb.ReadUint8Span(
absl::MakeSpan(loudness.layout_extension.info_type_bytes)));
}
return absl::OkStatus();
}
absl::Status SubMixAudioElement::ReadAndValidate(const int32_t& count_label,
ReadBitBuffer& rb) {
// Read the main portion of an `SubMixAudioElement`.
RETURN_IF_NOT_OK(rb.ReadULeb128(audio_element_id));
for (int i = 0; i < count_label; ++i) {
std::string localized_element_annotation;
RETURN_IF_NOT_OK(rb.ReadString(localized_element_annotation));
localized_element_annotations.push_back(localized_element_annotation);
}
// Read `rendering_config`.
uint8_t headphones_rendering_mode;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, headphones_rendering_mode));
rendering_config.headphones_rendering_mode =
static_cast<RenderingConfig::HeadphonesRenderingMode>(
headphones_rendering_mode);
uint8_t reserved;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(6, reserved));
rendering_config.reserved = reserved;
RETURN_IF_NOT_OK(
rb.ReadULeb128(rendering_config.rendering_config_extension_size));
rendering_config.rendering_config_extension_bytes.resize(
rendering_config.rendering_config_extension_size);
RETURN_IF_NOT_OK(rb.ReadUint8Span(
absl::MakeSpan(rendering_config.rendering_config_extension_bytes)));
RETURN_IF_NOT_OK(element_mix_gain.ReadAndValidate(rb));
return absl::OkStatus();
}
absl::Status MixPresentationSubMix::ReadAndValidate(const int32_t& count_label,
ReadBitBuffer& rb) {
DecodedUleb128 num_audio_elements;
RETURN_IF_NOT_OK(rb.ReadULeb128(num_audio_elements));
// IAMF requires there to be at least one audio element.
RETURN_IF_NOT_OK(ValidateNotEqual(DecodedUleb128{0}, num_audio_elements,
"num_audio_elements"));
for (int i = 0; i < num_audio_elements; ++i) {
SubMixAudioElement sub_mix_audio_element;
RETURN_IF_NOT_OK(sub_mix_audio_element.ReadAndValidate(count_label, rb));
audio_elements.push_back(sub_mix_audio_element);
}
RETURN_IF_NOT_OK(output_mix_gain.ReadAndValidate(rb));
DecodedUleb128 num_layouts;
RETURN_IF_NOT_OK(rb.ReadULeb128(num_layouts));
for (int i = 0; i < num_layouts; ++i) {
MixPresentationLayout mix_presentation_layout;
RETURN_IF_NOT_OK(mix_presentation_layout.ReadAndValidate(rb));
layouts.push_back(mix_presentation_layout);
}
return absl::OkStatus();
}
absl::Status ValidateCompliesWithIso639_2(absl::string_view string) {
if (string.size() == 3) {
// Consider any any three character string valid. A stricter
// implementation could check it actually is present in the list of valid
// ISO-639-2 code.
return absl::OkStatus();
} else {
return absl::InvalidArgumentError(absl::StrCat(
"Expected an ISO-639-2 code. ISO-639-2 codes should have three "
"characters. string= ",
string));
}
}
absl::Status MixPresentationTags::ValidateAndWrite(WriteBitBuffer& wb) const {
uint8_t num_tags;
RETURN_IF_NOT_OK(StaticCastIfInRange("num_tags", tags.size(), num_tags));
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(num_tags, 8));
int count_content_language_tag = 0;
for (const auto& tag : tags) {
if (tag.tag_name == "content_language") {
RETURN_IF_NOT_OK(ValidateCompliesWithIso639_2(tag.tag_value));
count_content_language_tag++;
}
RETURN_IF_NOT_OK(wb.WriteString(tag.tag_name));
RETURN_IF_NOT_OK(wb.WriteString(tag.tag_value));
}
// Tags are freeform and may be duplicated. Except for the "content_language"
// tag which SHALL appear at most once.
if (count_content_language_tag > 1) {
return absl::InvalidArgumentError(
"Expected zero or one content_language tag.");
}
return absl::OkStatus();
}
// Validates and writes a `LoudspeakersSsConventionLayout` and sets
// `found_stereo_layout` to true if it is a stereo layout.
absl::Status LoudspeakersSsConventionLayout::Write(bool& found_stereo_layout,
WriteBitBuffer& wb) const {
if (sound_system == kSoundSystemA_0_2_0) {
found_stereo_layout = true;
}
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(sound_system, 4));
return wb.WriteUnsignedLiteral(reserved, 2);
}
// Reads and validates a `LoudspeakersSsConventionLayout`
// TODO(b/339855338): Set `found_stereo_layout` to true if it is a stereo layout
// and check that its been found in MixPresentationSubMix::Read.
absl::Status LoudspeakersSsConventionLayout::Read(ReadBitBuffer& rb) {
uint8_t sound_system_uint;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(4, sound_system_uint));
sound_system = static_cast<SoundSystem>(sound_system_uint);
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, reserved));
return absl::OkStatus();
}
absl::Status LoudspeakersReservedOrBinauralLayout::Write(
WriteBitBuffer& wb) const {
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(reserved, 6));
return absl::OkStatus();
}
absl::Status LoudspeakersReservedOrBinauralLayout::Read(ReadBitBuffer& rb) {
return rb.ReadUnsignedLiteral(6, reserved);
}
absl::Status MixPresentationObu::GetNumChannelsFromLayout(
const Layout& loudness_layout, int32_t& num_channels) {
switch (loudness_layout.layout_type) {
using enum Layout::LayoutType;
case kLayoutTypeBinaural:
num_channels = 2;
return absl::OkStatus();
case kLayoutTypeLoudspeakersSsConvention: {
using enum LoudspeakersSsConventionLayout::SoundSystem;
static const absl::NoDestructor<absl::flat_hash_map<
LoudspeakersSsConventionLayout::SoundSystem, int32_t>>
kSoundSystemToNumChannels({
{kSoundSystemA_0_2_0, 2},
{kSoundSystemB_0_5_0, 6},
{kSoundSystemC_2_5_0, 8},
{kSoundSystemD_4_5_0, 10},
{kSoundSystemE_4_5_1, 11},
{kSoundSystemF_3_7_0, 12},
{kSoundSystemG_4_9_0, 14},
{kSoundSystemH_9_10_3, 24},
{kSoundSystemI_0_7_0, 8},
{kSoundSystemJ_4_7_0, 12},
{kSoundSystem10_2_7_0, 10},
{kSoundSystem11_2_3_0, 6},
{kSoundSystem12_0_1_0, 1},
{kSoundSystem13_6_9_0, 16},
});
const auto sound_system = std::get<LoudspeakersSsConventionLayout>(
loudness_layout.specific_layout)
.sound_system;
return CopyFromMap(*kSoundSystemToNumChannels, sound_system,
"Number of channels for `SoundSystem`", num_channels);
}
case kLayoutTypeReserved0:
case kLayoutTypeReserved1:
default:
return absl::InvalidArgumentError(
absl::StrCat("Unknown layout_type= ", loudness_layout.layout_type));
}
}
absl::Status MixPresentationObu::ValidateAndWritePayload(
WriteBitBuffer& wb) const {
const std::string with_mix_presentation_id =
absl::StrCat(" with mix_presentation_id= ", mix_presentation_id_);
// Write the main portion of the OBU.
RETURN_IF_NOT_OK(wb.WriteUleb128(mix_presentation_id_));
RETURN_IF_NOT_OK(wb.WriteUleb128(count_label_));
RETURN_IF_NOT_OK(ValidateUnique(
annotations_language_.begin(), annotations_language_.end(),
absl::StrCat("annotations_language", with_mix_presentation_id)));
RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
absl::StrCat("annotations_language", with_mix_presentation_id),
annotations_language_, count_label_));
for (const auto& annotations_language : annotations_language_) {
RETURN_IF_NOT_OK(wb.WriteString(annotations_language));
}
RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
absl::StrCat("localized_presentation_annotation",
with_mix_presentation_id),
localized_presentation_annotations_, count_label_));
for (const auto& localized_presentation_annotation :
localized_presentation_annotations_) {
RETURN_IF_NOT_OK(wb.WriteString(localized_presentation_annotation));
}
const DecodedUleb128 num_sub_mixes = sub_mixes_.size();
RETURN_IF_NOT_OK(wb.WriteUleb128(num_sub_mixes));
// Loop to write the `sub_mixes` array.
RETURN_IF_NOT_OK(ValidateNumSubMixes(num_sub_mixes));
RETURN_IF_NOT_OK(ValidateUniqueAudioElementIds(sub_mixes_));
for (const auto& sub_mix : sub_mixes_) {
RETURN_IF_NOT_OK(ValidateAndWriteSubMix(count_label_, sub_mix, wb));
}
if (mix_presentation_tags_.has_value()) {
RETURN_IF_NOT_OK(mix_presentation_tags_->ValidateAndWrite(wb));
}
return absl::OkStatus();
}
absl::StatusOr<MixPresentationObu> MixPresentationObu::CreateFromBuffer(
const ObuHeader& header, int64_t payload_size, ReadBitBuffer& rb) {
MixPresentationObu mix_presentation_obu(header);
RETURN_IF_NOT_OK(
mix_presentation_obu.ReadAndValidatePayload(payload_size, rb));
return mix_presentation_obu;
}
absl::Status MixPresentationObu::ReadAndValidatePayloadDerived(
int64_t /*payload_size*/, ReadBitBuffer& rb) {
// Read the main portion of the OBU.
RETURN_IF_NOT_OK(rb.ReadULeb128(mix_presentation_id_));
RETURN_IF_NOT_OK(rb.ReadULeb128(count_label_));
for (int i = 0; i < count_label_; ++i) {
std::string annotations_language;
RETURN_IF_NOT_OK(rb.ReadString(annotations_language));
annotations_language_.push_back(annotations_language);
}
RETURN_IF_NOT_OK(ValidateUnique(annotations_language_.begin(),
annotations_language_.end(),
"Annotation languages"));
for (int i = 0; i < count_label_; ++i) {
std::string localized_presentation_annotation;
RETURN_IF_NOT_OK(rb.ReadString(localized_presentation_annotation));
localized_presentation_annotations_.push_back(
localized_presentation_annotation);
}
DecodedUleb128 num_sub_mixes;
RETURN_IF_NOT_OK(rb.ReadULeb128(num_sub_mixes));
// Loop to read the `sub_mixes` array.
for (int i = 0; i < num_sub_mixes; ++i) {
MixPresentationSubMix sub_mix;
RETURN_IF_NOT_OK(sub_mix.ReadAndValidate(count_label_, rb));
sub_mixes_.push_back(sub_mix);
}
// TODO(b/329705373): Examine how many bytes were read so far. Use this to
// determine if Mix Presentation Tags should be read.
RETURN_IF_NOT_OK(ValidateNumSubMixes(num_sub_mixes));
RETURN_IF_NOT_OK(ValidateUniqueAudioElementIds(sub_mixes_));
return absl::OkStatus();
}
void LoudspeakersSsConventionLayout::Print() const {
LOG(INFO) << " sound_system= " << absl::StrCat(sound_system);
LOG(INFO) << " reserved= " << absl::StrCat(reserved);
}
void LoudspeakersReservedOrBinauralLayout::Print() const {
LOG(INFO) << " reserved= " << absl::StrCat(reserved);
}
void MixPresentationObu::PrintObu() const {
LOG(INFO) << "Mix Presentation OBU:";
LOG(INFO) << " mix_presentation_id= " << mix_presentation_id_;
LOG(INFO) << " count_label= " << count_label_;
LOG(INFO) << " annotations_language:";
for (int i = 0; i < count_label_; ++i) {
LOG(INFO) << " annotations_languages[" << i << "]= \""
<< annotations_language_[i] << "\"";
}
LOG(INFO) << " localized_presentation_annotations:";
for (int i = 0; i < count_label_; ++i) {
LOG(INFO) << " localized_presentation_annotations[" << i << "]= \""
<< localized_presentation_annotations_[i] << "\"";
}
LOG(INFO) << " num_sub_mixes= " << sub_mixes_.size();
// Submixes.
for (int i = 0; i < sub_mixes_.size(); ++i) {
const auto& sub_mix = sub_mixes_[i];
LOG(INFO) << " // sub_mixes[" << i << "]:";
LOG(INFO) << " num_audio_elements= " << sub_mix.audio_elements.size();
// Audio elements.
for (int j = 0; j < sub_mix.audio_elements.size(); ++j) {
const auto& audio_element = sub_mix.audio_elements[j];
LOG(INFO) << " // audio_elements[" << j << "]:";
LOG(INFO) << " audio_element_id= " << audio_element.audio_element_id;
LOG(INFO) << " localized_element_annotations:";
for (int k = 0; k < count_label_; ++k) {
LOG(INFO) << " localized_element_annotations[" << k << "]= \""
<< audio_element.localized_element_annotations[k] << "\"";
}
LOG(INFO) << " rendering_config:";
LOG(INFO) << " headphones_rendering_mode= "
<< absl::StrCat(audio_element.rendering_config
.headphones_rendering_mode);
LOG(INFO) << " reserved= "
<< absl::StrCat(audio_element.rendering_config.reserved);
LOG(INFO)
<< " rendering_config_extension_size= "
<< audio_element.rendering_config.rendering_config_extension_size;
LOG(INFO) << " rendering_config_extension_bytes omitted.";
LOG(INFO) << " element_mix_gain:";
audio_element.element_mix_gain.Print();
}
LOG(INFO) << " output_mix_gain:";
sub_mix.output_mix_gain.Print();
LOG(INFO) << " num_layouts= " << sub_mix.layouts.size();
// Layouts.
for (int j = 0; j < sub_mix.layouts.size(); j++) {
const auto& layout = sub_mix.layouts[j];
LOG(INFO) << " // layouts[" << j << "]:";
LOG(INFO) << " loudness_layout:";
LOG(INFO) << " layout_type= "
<< absl::StrCat(layout.loudness_layout.layout_type);
// SpecificLayout.
switch (layout.loudness_layout.layout_type) {
using enum Layout::LayoutType;
case kLayoutTypeLoudspeakersSsConvention:
std::get<LoudspeakersSsConventionLayout>(
layout.loudness_layout.specific_layout)
.Print();
break;
case kLayoutTypeReserved0:
case kLayoutTypeReserved1:
case kLayoutTypeBinaural:
std::get<LoudspeakersReservedOrBinauralLayout>(
layout.loudness_layout.specific_layout)
.Print();
}
const auto& loudness = layout.loudness;
LOG(INFO) << " loudness:";
LOG(INFO) << " info_type= " << absl::StrCat(loudness.info_type);
LOG(INFO) << " integrated_loudness= "
<< loudness.integrated_loudness;
LOG(INFO) << " digital_peak= " << loudness.digital_peak;
if ((loudness.info_type & LoudnessInfo::kTruePeak) != 0) {
LOG(INFO) << " true_peak= " << layout.loudness.true_peak;
}
if ((loudness.info_type & LoudnessInfo::kAnchoredLoudness) != 0) {
const auto& anchored_loudness = loudness.anchored_loudness;
LOG(INFO) << " anchored_loudness: ";
LOG(INFO) << " num_anchored_loudness= "
<< absl::StrCat(anchored_loudness.anchor_elements.size());
for (int i = 0; i < anchored_loudness.anchor_elements.size(); i++) {
LOG(INFO) << " anchor_element[" << i << "]= "
<< absl::StrCat(
anchored_loudness.anchor_elements[i].anchor_element);
LOG(INFO) << " anchored_loudness[" << i << "]= "
<< anchored_loudness.anchor_elements[i].anchored_loudness;
}
}
if ((loudness.info_type & LoudnessInfo::kAnyLayoutExtension) != 0) {
const auto& layout_extension = loudness.layout_extension;
LOG(INFO) << " layout_extension: ";
LOG(INFO) << " info_type_size= "
<< layout_extension.info_type_size;
for (int i = 0; i < layout_extension.info_type_bytes.size(); ++i) {
LOG(INFO) << " info_type_bytes[" << i
<< "]= " << layout_extension.info_type_bytes[i];
}
}
}
}
if (mix_presentation_tags_.has_value()) {
LOG(INFO) << " mix_presentation_tags:";
for (int i = 0; i < mix_presentation_tags_->tags.size(); ++i) {
const auto& tag = mix_presentation_tags_->tags[i];
LOG(INFO) << " tags[" << i << "]:";
LOG(INFO) << " tag_name= \"" << tag.tag_name << "\"";
LOG(INFO) << " tag_value= \"" << tag.tag_value << "\"";
}
} else {
LOG(INFO) << " No mix_presentation_tags detected.";
}
}
} // namespace iamf_tools