• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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