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/obu/mix_presentation.h"
13
14 #include <cstdint>
15 #include <string>
16 #include <vector>
17
18 #include "absl/base/no_destructor.h"
19 #include "absl/container/flat_hash_map.h"
20 #include "absl/log/log.h"
21 #include "absl/status/status.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/types/span.h"
25 #include "iamf/common/read_bit_buffer.h"
26 #include "iamf/common/utils/macros.h"
27 #include "iamf/common/utils/map_utils.h"
28 #include "iamf/common/utils/numeric_utils.h"
29 #include "iamf/common/utils/validation_utils.h"
30 #include "iamf/common/write_bit_buffer.h"
31 #include "iamf/obu/obu_header.h"
32 #include "iamf/obu/param_definitions.h"
33 #include "iamf/obu/types.h"
34
35 namespace iamf_tools {
36
37 namespace {
38
ValidateUniqueAudioElementIds(const std::vector<MixPresentationSubMix> & sub_mixes)39 absl::Status ValidateUniqueAudioElementIds(
40 const std::vector<MixPresentationSubMix>& sub_mixes) {
41 std::vector<DecodedUleb128> collected_audio_element_ids;
42
43 // Audio Element IDs must be unique across all sub-mixes.
44 for (const auto& sub_mix : sub_mixes) {
45 for (const auto& audio_element : sub_mix.audio_elements) {
46 collected_audio_element_ids.push_back(audio_element.audio_element_id);
47 }
48 }
49
50 return ValidateUnique(collected_audio_element_ids.begin(),
51 collected_audio_element_ids.end(), "Audio element IDs");
52 }
53
ValidateUniqueAnchorElements(const std::vector<AnchoredLoudnessElement> & anchor_elements)54 absl::Status ValidateUniqueAnchorElements(
55 const std::vector<AnchoredLoudnessElement>& anchor_elements) {
56 std::vector<uint8_t> anchor_elements_as_uint8;
57 anchor_elements_as_uint8.reserve(anchor_elements.size());
58 for (const auto& anchor_element : anchor_elements) {
59 anchor_elements_as_uint8.push_back(
60 static_cast<uint8_t>(anchor_element.anchor_element));
61 }
62 return ValidateUnique(anchor_elements_as_uint8.begin(),
63 anchor_elements_as_uint8.end(),
64 "Anchored loudness types");
65 }
66
ValidateAndWriteSubMixAudioElement(DecodedUleb128 count_label,const SubMixAudioElement & element,WriteBitBuffer & wb)67 absl::Status ValidateAndWriteSubMixAudioElement(
68 DecodedUleb128 count_label, const SubMixAudioElement& element,
69 WriteBitBuffer& wb) {
70 // Write the main portion of an `SubMixAudioElement`.
71 RETURN_IF_NOT_OK(wb.WriteUleb128(element.audio_element_id));
72 RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
73 absl::StrCat("localized_element_annotations with audio_element_id= ",
74 element.audio_element_id),
75 element.localized_element_annotations, count_label));
76 for (const auto& localized_element_annotation :
77 element.localized_element_annotations) {
78 RETURN_IF_NOT_OK(wb.WriteString(localized_element_annotation));
79 }
80
81 // Write out `rendering_config`.
82 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
83 static_cast<uint8_t>(element.rendering_config.headphones_rendering_mode),
84 2));
85 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
86 static_cast<uint8_t>(element.rendering_config.reserved), 6));
87 RETURN_IF_NOT_OK(wb.WriteUleb128(
88 element.rendering_config.rendering_config_extension_size));
89 RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
90 "rendering_config_extension_bytes",
91 element.rendering_config.rendering_config_extension_bytes,
92 element.rendering_config.rendering_config_extension_size));
93 RETURN_IF_NOT_OK(wb.WriteUint8Span(absl::MakeConstSpan(
94 element.rendering_config.rendering_config_extension_bytes)));
95
96 RETURN_IF_NOT_OK(element.element_mix_gain.ValidateAndWrite(wb));
97 return absl::OkStatus();
98 }
99
100 // Writes and validates a `MixPresentationLayout and sets `found_stereo_layout`
101 // to if it is a stereo layout.
ValidateAndWriteLayout(const MixPresentationLayout & layout,bool & found_stereo_layout,WriteBitBuffer & wb)102 absl::Status ValidateAndWriteLayout(const MixPresentationLayout& layout,
103 bool& found_stereo_layout,
104 WriteBitBuffer& wb) {
105 // Write the `loudness_layout` portion of a `MixPresentationLayout`.
106 RETURN_IF_NOT_OK(
107 wb.WriteUnsignedLiteral(layout.loudness_layout.layout_type, 2));
108
109 // Write the specific type of `Layout` dependent on `layout_type`.
110 switch (layout.loudness_layout.layout_type) {
111 using enum Layout::LayoutType;
112 case kLayoutTypeLoudspeakersSsConvention:
113 RETURN_IF_NOT_OK(std::get<LoudspeakersSsConventionLayout>(
114 layout.loudness_layout.specific_layout)
115 .Write(found_stereo_layout, wb));
116 break;
117 case kLayoutTypeReserved0:
118 case kLayoutTypeReserved1:
119 case kLayoutTypeBinaural:
120 RETURN_IF_NOT_OK(std::get<LoudspeakersReservedOrBinauralLayout>(
121 layout.loudness_layout.specific_layout)
122 .Write(wb));
123 break;
124 }
125
126 // Write the `loudness` portion of a `MixPresentationLayout`.
127 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(layout.loudness.info_type, 8));
128 RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.integrated_loudness));
129 RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.digital_peak));
130
131 // Conditionally write `true_peak` based on `info_type`.
132 if ((layout.loudness.info_type & LoudnessInfo::kTruePeak) != 0) {
133 RETURN_IF_NOT_OK(wb.WriteSigned16(layout.loudness.true_peak));
134 }
135 // Conditionally write `anchored_loudness` based on `info_type`.
136 if ((layout.loudness.info_type & LoudnessInfo::kAnchoredLoudness) != 0) {
137 MAYBE_RETURN_IF_NOT_OK(ValidateUniqueAnchorElements(
138 layout.loudness.anchored_loudness.anchor_elements));
139 const AnchoredLoudness& anchored_loudness =
140 layout.loudness.anchored_loudness;
141 uint8_t num_anchor_elements;
142 RETURN_IF_NOT_OK(StaticCastIfInRange(
143 "num_anchor_elements", anchored_loudness.anchor_elements.size(),
144 num_anchor_elements));
145 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(num_anchor_elements, 8));
146 for (const auto& anchor_element : anchored_loudness.anchor_elements) {
147 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(
148 static_cast<uint8_t>(anchor_element.anchor_element), 8));
149 RETURN_IF_NOT_OK(wb.WriteSigned16(anchor_element.anchored_loudness));
150 }
151 }
152 // Conditionally write `layout_extension` based on `info_type`.
153 if ((layout.loudness.info_type & LoudnessInfo::kAnyLayoutExtension) != 0) {
154 RETURN_IF_NOT_OK(
155 wb.WriteUleb128(layout.loudness.layout_extension.info_type_size));
156 RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
157 "info_type_bytes", layout.loudness.layout_extension.info_type_bytes,
158 layout.loudness.layout_extension.info_type_size));
159 RETURN_IF_NOT_OK(wb.WriteUint8Span(
160 absl::MakeConstSpan(layout.loudness.layout_extension.info_type_bytes)));
161 }
162
163 return absl::OkStatus();
164 }
165
ValidateAndWriteSubMix(DecodedUleb128 count_label,const MixPresentationSubMix & sub_mix,WriteBitBuffer & wb)166 absl::Status ValidateAndWriteSubMix(DecodedUleb128 count_label,
167 const MixPresentationSubMix& sub_mix,
168 WriteBitBuffer& wb) {
169 // IAMF requires there to be at least one audio element.
170 const DecodedUleb128 num_audio_elements = sub_mix.audio_elements.size();
171 RETURN_IF_NOT_OK(ValidateNotEqual(DecodedUleb128{0}, num_audio_elements,
172 "num_audio_elements"));
173
174 // Write the main portion of a `MixPresentationSubMix`.
175 RETURN_IF_NOT_OK(wb.WriteUleb128(num_audio_elements));
176
177 // Loop to write the `audio_elements` array.
178 for (const auto& sub_mix_audio_element : sub_mix.audio_elements) {
179 RETURN_IF_NOT_OK(ValidateAndWriteSubMixAudioElement(
180 count_label, sub_mix_audio_element, wb));
181 }
182
183 RETURN_IF_NOT_OK(sub_mix.output_mix_gain.ValidateAndWrite(wb));
184 const DecodedUleb128 num_layouts = sub_mix.layouts.size();
185 RETURN_IF_NOT_OK(wb.WriteUleb128(num_layouts));
186
187 // Loop to write the `layouts` array.
188 bool found_stereo_layout = false;
189 for (const auto& layout : sub_mix.layouts) {
190 RETURN_IF_NOT_OK(ValidateAndWriteLayout(layout, found_stereo_layout, wb));
191 }
192 if (!found_stereo_layout) {
193 return absl::InvalidArgumentError(
194 "Every sub-mix must have a stereo layout.");
195 }
196
197 return absl::OkStatus();
198 }
199
ValidateNumSubMixes(DecodedUleb128 num_sub_mixes)200 absl::Status ValidateNumSubMixes(DecodedUleb128 num_sub_mixes) {
201 MAYBE_RETURN_IF_NOT_OK(
202 ValidateNotEqual(DecodedUleb128{0}, num_sub_mixes, "num_sub_mixes"));
203 return absl::OkStatus();
204 }
205
206 } // namespace
207
ReadAndValidate(ReadBitBuffer & rb)208 absl::Status Layout::ReadAndValidate(ReadBitBuffer& rb) {
209 uint8_t layout_type_uint;
210 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, layout_type_uint));
211 layout_type = static_cast<Layout::LayoutType>(layout_type_uint);
212
213 // Read the specific type of `Layout` dependent on `layout_type`.
214 switch (layout_type) {
215 using enum Layout::LayoutType;
216 case kLayoutTypeLoudspeakersSsConvention:
217 specific_layout = LoudspeakersSsConventionLayout();
218 return std::get<LoudspeakersSsConventionLayout>(specific_layout).Read(rb);
219 case kLayoutTypeReserved0:
220 case kLayoutTypeReserved1:
221 // Reserved layouts are identical to binaural layouts as of IAMF
222 // v1.1.0 aomediacodec.github.io/iamf/v1.1.0.html#syntax-layout.
223 case kLayoutTypeBinaural:
224 specific_layout = LoudspeakersReservedOrBinauralLayout();
225 return std::get<LoudspeakersReservedOrBinauralLayout>(specific_layout)
226 .Read(rb);
227 }
228
229 return absl::InternalError(absl::StrCat(
230 "Unexpected value for 2-bit Layout::LayoutType = ", layout_type));
231 }
232
ReadAndValidate(ReadBitBuffer & rb)233 absl::Status MixPresentationLayout::ReadAndValidate(ReadBitBuffer& rb) {
234 // Read the `loudness_layout` portion of a `MixPresentationLayout`.
235 RETURN_IF_NOT_OK(loudness_layout.ReadAndValidate(rb));
236
237 // Read the `loudness` portion of a `MixPresentationLayout`.
238 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, loudness.info_type));
239 RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.integrated_loudness));
240 RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.digital_peak));
241
242 // Conditionally read `true_peak` based on `info_type`.
243 if (loudness.info_type & LoudnessInfo::kTruePeak) {
244 RETURN_IF_NOT_OK(rb.ReadSigned16(loudness.true_peak));
245 }
246 // Conditionally read `anchored_loudness` based on `info_type`.
247 if (loudness.info_type & LoudnessInfo::kAnchoredLoudness) {
248 uint8_t num_anchored_loudness;
249 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, num_anchored_loudness));
250
251 for (int i = 0; i < num_anchored_loudness; ++i) {
252 AnchoredLoudnessElement anchor_loudness_element;
253 uint8_t anchor_element;
254 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, anchor_element));
255 anchor_loudness_element.anchor_element =
256 static_cast<AnchoredLoudnessElement::AnchorElement>(anchor_element);
257 RETURN_IF_NOT_OK(
258 rb.ReadSigned16(anchor_loudness_element.anchored_loudness));
259 loudness.anchored_loudness.anchor_elements.push_back(
260 anchor_loudness_element);
261 }
262 RETURN_IF_NOT_OK(ValidateUniqueAnchorElements(
263 loudness.anchored_loudness.anchor_elements));
264 }
265 // Conditionally read `layout_extension` based on `info_type`.
266 if (loudness.info_type & LoudnessInfo::kAnyLayoutExtension) {
267 RETURN_IF_NOT_OK(rb.ReadULeb128(loudness.layout_extension.info_type_size));
268 loudness.layout_extension.info_type_bytes.resize(
269 loudness.layout_extension.info_type_size);
270 RETURN_IF_NOT_OK(rb.ReadUint8Span(
271 absl::MakeSpan(loudness.layout_extension.info_type_bytes)));
272 }
273
274 return absl::OkStatus();
275 }
276
ReadAndValidate(const int32_t & count_label,ReadBitBuffer & rb)277 absl::Status SubMixAudioElement::ReadAndValidate(const int32_t& count_label,
278 ReadBitBuffer& rb) {
279 // Read the main portion of an `SubMixAudioElement`.
280 RETURN_IF_NOT_OK(rb.ReadULeb128(audio_element_id));
281 for (int i = 0; i < count_label; ++i) {
282 std::string localized_element_annotation;
283 RETURN_IF_NOT_OK(rb.ReadString(localized_element_annotation));
284 localized_element_annotations.push_back(localized_element_annotation);
285 }
286
287 // Read `rendering_config`.
288 uint8_t headphones_rendering_mode;
289 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, headphones_rendering_mode));
290 rendering_config.headphones_rendering_mode =
291 static_cast<RenderingConfig::HeadphonesRenderingMode>(
292 headphones_rendering_mode);
293
294 uint8_t reserved;
295 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(6, reserved));
296 rendering_config.reserved = reserved;
297 RETURN_IF_NOT_OK(
298 rb.ReadULeb128(rendering_config.rendering_config_extension_size));
299 rendering_config.rendering_config_extension_bytes.resize(
300 rendering_config.rendering_config_extension_size);
301 RETURN_IF_NOT_OK(rb.ReadUint8Span(
302 absl::MakeSpan(rendering_config.rendering_config_extension_bytes)));
303
304 RETURN_IF_NOT_OK(element_mix_gain.ReadAndValidate(rb));
305 return absl::OkStatus();
306 }
307
ReadAndValidate(const int32_t & count_label,ReadBitBuffer & rb)308 absl::Status MixPresentationSubMix::ReadAndValidate(const int32_t& count_label,
309 ReadBitBuffer& rb) {
310 DecodedUleb128 num_audio_elements;
311 RETURN_IF_NOT_OK(rb.ReadULeb128(num_audio_elements));
312 // IAMF requires there to be at least one audio element.
313 RETURN_IF_NOT_OK(ValidateNotEqual(DecodedUleb128{0}, num_audio_elements,
314 "num_audio_elements"));
315 for (int i = 0; i < num_audio_elements; ++i) {
316 SubMixAudioElement sub_mix_audio_element;
317 RETURN_IF_NOT_OK(sub_mix_audio_element.ReadAndValidate(count_label, rb));
318 audio_elements.push_back(sub_mix_audio_element);
319 }
320
321 RETURN_IF_NOT_OK(output_mix_gain.ReadAndValidate(rb));
322 DecodedUleb128 num_layouts;
323 RETURN_IF_NOT_OK(rb.ReadULeb128(num_layouts));
324 for (int i = 0; i < num_layouts; ++i) {
325 MixPresentationLayout mix_presentation_layout;
326 RETURN_IF_NOT_OK(mix_presentation_layout.ReadAndValidate(rb));
327 layouts.push_back(mix_presentation_layout);
328 }
329 return absl::OkStatus();
330 }
331
ValidateCompliesWithIso639_2(absl::string_view string)332 absl::Status ValidateCompliesWithIso639_2(absl::string_view string) {
333 if (string.size() == 3) {
334 // Consider any any three character string valid. A stricter
335 // implementation could check it actually is present in the list of valid
336 // ISO-639-2 code.
337 return absl::OkStatus();
338 } else {
339 return absl::InvalidArgumentError(absl::StrCat(
340 "Expected an ISO-639-2 code. ISO-639-2 codes should have three "
341 "characters. string= ",
342 string));
343 }
344 }
345
ValidateAndWrite(WriteBitBuffer & wb) const346 absl::Status MixPresentationTags::ValidateAndWrite(WriteBitBuffer& wb) const {
347 uint8_t num_tags;
348 RETURN_IF_NOT_OK(StaticCastIfInRange("num_tags", tags.size(), num_tags));
349 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(num_tags, 8));
350
351 int count_content_language_tag = 0;
352
353 for (const auto& tag : tags) {
354 if (tag.tag_name == "content_language") {
355 RETURN_IF_NOT_OK(ValidateCompliesWithIso639_2(tag.tag_value));
356
357 count_content_language_tag++;
358 }
359 RETURN_IF_NOT_OK(wb.WriteString(tag.tag_name));
360 RETURN_IF_NOT_OK(wb.WriteString(tag.tag_value));
361 }
362 // Tags are freeform and may be duplicated. Except for the "content_language"
363 // tag which SHALL appear at most once.
364 if (count_content_language_tag > 1) {
365 return absl::InvalidArgumentError(
366 "Expected zero or one content_language tag.");
367 }
368
369 return absl::OkStatus();
370 }
371
372 // Validates and writes a `LoudspeakersSsConventionLayout` and sets
373 // `found_stereo_layout` to true if it is a stereo layout.
Write(bool & found_stereo_layout,WriteBitBuffer & wb) const374 absl::Status LoudspeakersSsConventionLayout::Write(bool& found_stereo_layout,
375 WriteBitBuffer& wb) const {
376 if (sound_system == kSoundSystemA_0_2_0) {
377 found_stereo_layout = true;
378 }
379 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(sound_system, 4));
380
381 return wb.WriteUnsignedLiteral(reserved, 2);
382 }
383
384 // Reads and validates a `LoudspeakersSsConventionLayout`
385 // TODO(b/339855338): Set `found_stereo_layout` to true if it is a stereo layout
386 // and check that its been found in MixPresentationSubMix::Read.
Read(ReadBitBuffer & rb)387 absl::Status LoudspeakersSsConventionLayout::Read(ReadBitBuffer& rb) {
388 uint8_t sound_system_uint;
389 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(4, sound_system_uint));
390 sound_system = static_cast<SoundSystem>(sound_system_uint);
391 RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, reserved));
392 return absl::OkStatus();
393 }
394
Write(WriteBitBuffer & wb) const395 absl::Status LoudspeakersReservedOrBinauralLayout::Write(
396 WriteBitBuffer& wb) const {
397 RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(reserved, 6));
398 return absl::OkStatus();
399 }
400
Read(ReadBitBuffer & rb)401 absl::Status LoudspeakersReservedOrBinauralLayout::Read(ReadBitBuffer& rb) {
402 return rb.ReadUnsignedLiteral(6, reserved);
403 }
404
GetNumChannelsFromLayout(const Layout & loudness_layout,int32_t & num_channels)405 absl::Status MixPresentationObu::GetNumChannelsFromLayout(
406 const Layout& loudness_layout, int32_t& num_channels) {
407 switch (loudness_layout.layout_type) {
408 using enum Layout::LayoutType;
409 case kLayoutTypeBinaural:
410 num_channels = 2;
411 return absl::OkStatus();
412 case kLayoutTypeLoudspeakersSsConvention: {
413 using enum LoudspeakersSsConventionLayout::SoundSystem;
414 static const absl::NoDestructor<absl::flat_hash_map<
415 LoudspeakersSsConventionLayout::SoundSystem, int32_t>>
416 kSoundSystemToNumChannels({
417 {kSoundSystemA_0_2_0, 2},
418 {kSoundSystemB_0_5_0, 6},
419 {kSoundSystemC_2_5_0, 8},
420 {kSoundSystemD_4_5_0, 10},
421 {kSoundSystemE_4_5_1, 11},
422 {kSoundSystemF_3_7_0, 12},
423 {kSoundSystemG_4_9_0, 14},
424 {kSoundSystemH_9_10_3, 24},
425 {kSoundSystemI_0_7_0, 8},
426 {kSoundSystemJ_4_7_0, 12},
427 {kSoundSystem10_2_7_0, 10},
428 {kSoundSystem11_2_3_0, 6},
429 {kSoundSystem12_0_1_0, 1},
430 {kSoundSystem13_6_9_0, 16},
431 });
432
433 const auto sound_system = std::get<LoudspeakersSsConventionLayout>(
434 loudness_layout.specific_layout)
435 .sound_system;
436
437 return CopyFromMap(*kSoundSystemToNumChannels, sound_system,
438 "Number of channels for `SoundSystem`", num_channels);
439 }
440 case kLayoutTypeReserved0:
441 case kLayoutTypeReserved1:
442 default:
443 return absl::InvalidArgumentError(
444 absl::StrCat("Unknown layout_type= ", loudness_layout.layout_type));
445 }
446 }
447
ValidateAndWritePayload(WriteBitBuffer & wb) const448 absl::Status MixPresentationObu::ValidateAndWritePayload(
449 WriteBitBuffer& wb) const {
450 const std::string with_mix_presentation_id =
451 absl::StrCat(" with mix_presentation_id= ", mix_presentation_id_);
452
453 // Write the main portion of the OBU.
454 RETURN_IF_NOT_OK(wb.WriteUleb128(mix_presentation_id_));
455 RETURN_IF_NOT_OK(wb.WriteUleb128(count_label_));
456
457 RETURN_IF_NOT_OK(ValidateUnique(
458 annotations_language_.begin(), annotations_language_.end(),
459 absl::StrCat("annotations_language", with_mix_presentation_id)));
460
461 RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
462 absl::StrCat("annotations_language", with_mix_presentation_id),
463 annotations_language_, count_label_));
464 for (const auto& annotations_language : annotations_language_) {
465 RETURN_IF_NOT_OK(wb.WriteString(annotations_language));
466 }
467
468 RETURN_IF_NOT_OK(ValidateContainerSizeEqual(
469 absl::StrCat("localized_presentation_annotation",
470 with_mix_presentation_id),
471 localized_presentation_annotations_, count_label_));
472 for (const auto& localized_presentation_annotation :
473 localized_presentation_annotations_) {
474 RETURN_IF_NOT_OK(wb.WriteString(localized_presentation_annotation));
475 }
476
477 const DecodedUleb128 num_sub_mixes = sub_mixes_.size();
478 RETURN_IF_NOT_OK(wb.WriteUleb128(num_sub_mixes));
479
480 // Loop to write the `sub_mixes` array.
481 RETURN_IF_NOT_OK(ValidateNumSubMixes(num_sub_mixes));
482 RETURN_IF_NOT_OK(ValidateUniqueAudioElementIds(sub_mixes_));
483 for (const auto& sub_mix : sub_mixes_) {
484 RETURN_IF_NOT_OK(ValidateAndWriteSubMix(count_label_, sub_mix, wb));
485 }
486
487 if (mix_presentation_tags_.has_value()) {
488 RETURN_IF_NOT_OK(mix_presentation_tags_->ValidateAndWrite(wb));
489 }
490
491 return absl::OkStatus();
492 }
493
CreateFromBuffer(const ObuHeader & header,int64_t payload_size,ReadBitBuffer & rb)494 absl::StatusOr<MixPresentationObu> MixPresentationObu::CreateFromBuffer(
495 const ObuHeader& header, int64_t payload_size, ReadBitBuffer& rb) {
496 MixPresentationObu mix_presentation_obu(header);
497 RETURN_IF_NOT_OK(
498 mix_presentation_obu.ReadAndValidatePayload(payload_size, rb));
499 return mix_presentation_obu;
500 }
501
ReadAndValidatePayloadDerived(int64_t,ReadBitBuffer & rb)502 absl::Status MixPresentationObu::ReadAndValidatePayloadDerived(
503 int64_t /*payload_size*/, ReadBitBuffer& rb) {
504 // Read the main portion of the OBU.
505 RETURN_IF_NOT_OK(rb.ReadULeb128(mix_presentation_id_));
506 RETURN_IF_NOT_OK(rb.ReadULeb128(count_label_));
507
508 for (int i = 0; i < count_label_; ++i) {
509 std::string annotations_language;
510 RETURN_IF_NOT_OK(rb.ReadString(annotations_language));
511 annotations_language_.push_back(annotations_language);
512 }
513 RETURN_IF_NOT_OK(ValidateUnique(annotations_language_.begin(),
514 annotations_language_.end(),
515 "Annotation languages"));
516
517 for (int i = 0; i < count_label_; ++i) {
518 std::string localized_presentation_annotation;
519 RETURN_IF_NOT_OK(rb.ReadString(localized_presentation_annotation));
520 localized_presentation_annotations_.push_back(
521 localized_presentation_annotation);
522 }
523
524 DecodedUleb128 num_sub_mixes;
525 RETURN_IF_NOT_OK(rb.ReadULeb128(num_sub_mixes));
526
527 // Loop to read the `sub_mixes` array.
528 for (int i = 0; i < num_sub_mixes; ++i) {
529 MixPresentationSubMix sub_mix;
530 RETURN_IF_NOT_OK(sub_mix.ReadAndValidate(count_label_, rb));
531 sub_mixes_.push_back(sub_mix);
532 }
533 // TODO(b/329705373): Examine how many bytes were read so far. Use this to
534 // determine if Mix Presentation Tags should be read.
535
536 RETURN_IF_NOT_OK(ValidateNumSubMixes(num_sub_mixes));
537 RETURN_IF_NOT_OK(ValidateUniqueAudioElementIds(sub_mixes_));
538
539 return absl::OkStatus();
540 }
541
Print() const542 void LoudspeakersSsConventionLayout::Print() const {
543 LOG(INFO) << " sound_system= " << absl::StrCat(sound_system);
544 LOG(INFO) << " reserved= " << absl::StrCat(reserved);
545 }
546
Print() const547 void LoudspeakersReservedOrBinauralLayout::Print() const {
548 LOG(INFO) << " reserved= " << absl::StrCat(reserved);
549 }
550
PrintObu() const551 void MixPresentationObu::PrintObu() const {
552 LOG(INFO) << "Mix Presentation OBU:";
553 LOG(INFO) << " mix_presentation_id= " << mix_presentation_id_;
554 LOG(INFO) << " count_label= " << count_label_;
555 LOG(INFO) << " annotations_language:";
556 for (int i = 0; i < count_label_; ++i) {
557 LOG(INFO) << " annotations_languages[" << i << "]= \""
558 << annotations_language_[i] << "\"";
559 }
560 LOG(INFO) << " localized_presentation_annotations:";
561 for (int i = 0; i < count_label_; ++i) {
562 LOG(INFO) << " localized_presentation_annotations[" << i << "]= \""
563 << localized_presentation_annotations_[i] << "\"";
564 }
565 LOG(INFO) << " num_sub_mixes= " << sub_mixes_.size();
566
567 // Submixes.
568 for (int i = 0; i < sub_mixes_.size(); ++i) {
569 const auto& sub_mix = sub_mixes_[i];
570 LOG(INFO) << " // sub_mixes[" << i << "]:";
571 LOG(INFO) << " num_audio_elements= " << sub_mix.audio_elements.size();
572 // Audio elements.
573 for (int j = 0; j < sub_mix.audio_elements.size(); ++j) {
574 const auto& audio_element = sub_mix.audio_elements[j];
575 LOG(INFO) << " // audio_elements[" << j << "]:";
576 LOG(INFO) << " audio_element_id= " << audio_element.audio_element_id;
577 LOG(INFO) << " localized_element_annotations:";
578 for (int k = 0; k < count_label_; ++k) {
579 LOG(INFO) << " localized_element_annotations[" << k << "]= \""
580 << audio_element.localized_element_annotations[k] << "\"";
581 }
582 LOG(INFO) << " rendering_config:";
583 LOG(INFO) << " headphones_rendering_mode= "
584 << absl::StrCat(audio_element.rendering_config
585 .headphones_rendering_mode);
586 LOG(INFO) << " reserved= "
587 << absl::StrCat(audio_element.rendering_config.reserved);
588 LOG(INFO)
589 << " rendering_config_extension_size= "
590 << audio_element.rendering_config.rendering_config_extension_size;
591 LOG(INFO) << " rendering_config_extension_bytes omitted.";
592 LOG(INFO) << " element_mix_gain:";
593 audio_element.element_mix_gain.Print();
594 }
595
596 LOG(INFO) << " output_mix_gain:";
597 sub_mix.output_mix_gain.Print();
598
599 LOG(INFO) << " num_layouts= " << sub_mix.layouts.size();
600
601 // Layouts.
602 for (int j = 0; j < sub_mix.layouts.size(); j++) {
603 const auto& layout = sub_mix.layouts[j];
604 LOG(INFO) << " // layouts[" << j << "]:";
605 LOG(INFO) << " loudness_layout:";
606 LOG(INFO) << " layout_type= "
607 << absl::StrCat(layout.loudness_layout.layout_type);
608
609 // SpecificLayout.
610 switch (layout.loudness_layout.layout_type) {
611 using enum Layout::LayoutType;
612 case kLayoutTypeLoudspeakersSsConvention:
613 std::get<LoudspeakersSsConventionLayout>(
614 layout.loudness_layout.specific_layout)
615 .Print();
616 break;
617 case kLayoutTypeReserved0:
618 case kLayoutTypeReserved1:
619 case kLayoutTypeBinaural:
620 std::get<LoudspeakersReservedOrBinauralLayout>(
621 layout.loudness_layout.specific_layout)
622 .Print();
623 }
624
625 const auto& loudness = layout.loudness;
626 LOG(INFO) << " loudness:";
627 LOG(INFO) << " info_type= " << absl::StrCat(loudness.info_type);
628 LOG(INFO) << " integrated_loudness= "
629 << loudness.integrated_loudness;
630 LOG(INFO) << " digital_peak= " << loudness.digital_peak;
631 if ((loudness.info_type & LoudnessInfo::kTruePeak) != 0) {
632 LOG(INFO) << " true_peak= " << layout.loudness.true_peak;
633 }
634
635 if ((loudness.info_type & LoudnessInfo::kAnchoredLoudness) != 0) {
636 const auto& anchored_loudness = loudness.anchored_loudness;
637 LOG(INFO) << " anchored_loudness: ";
638 LOG(INFO) << " num_anchored_loudness= "
639 << absl::StrCat(anchored_loudness.anchor_elements.size());
640 for (int i = 0; i < anchored_loudness.anchor_elements.size(); i++) {
641 LOG(INFO) << " anchor_element[" << i << "]= "
642 << absl::StrCat(
643 anchored_loudness.anchor_elements[i].anchor_element);
644 LOG(INFO) << " anchored_loudness[" << i << "]= "
645 << anchored_loudness.anchor_elements[i].anchored_loudness;
646 }
647 }
648
649 if ((loudness.info_type & LoudnessInfo::kAnyLayoutExtension) != 0) {
650 const auto& layout_extension = loudness.layout_extension;
651 LOG(INFO) << " layout_extension: ";
652 LOG(INFO) << " info_type_size= "
653 << layout_extension.info_type_size;
654 for (int i = 0; i < layout_extension.info_type_bytes.size(); ++i) {
655 LOG(INFO) << " info_type_bytes[" << i
656 << "]= " << layout_extension.info_type_bytes[i];
657 }
658 }
659 }
660 }
661 if (mix_presentation_tags_.has_value()) {
662 LOG(INFO) << " mix_presentation_tags:";
663 for (int i = 0; i < mix_presentation_tags_->tags.size(); ++i) {
664 const auto& tag = mix_presentation_tags_->tags[i];
665 LOG(INFO) << " tags[" << i << "]:";
666 LOG(INFO) << " tag_name= \"" << tag.tag_name << "\"";
667 LOG(INFO) << " tag_value= \"" << tag.tag_value << "\"";
668 }
669 } else {
670 LOG(INFO) << " No mix_presentation_tags detected.";
671 }
672 }
673
674 } // namespace iamf_tools
675