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/cli/proto_conversion/proto_to_obu/mix_presentation_generator.h"
13 
14 #include <cstddef>
15 #include <cstdint>
16 #include <list>
17 #include <optional>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/base/no_destructor.h"
23 #include "absl/log/log.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/types/span.h"
27 #include "iamf/cli/proto/mix_presentation.pb.h"
28 #include "iamf/cli/proto/param_definitions.pb.h"
29 #include "iamf/cli/proto/parameter_data.pb.h"
30 #include "iamf/cli/proto_conversion/lookup_tables.h"
31 #include "iamf/cli/proto_conversion/proto_utils.h"
32 #include "iamf/common/utils/macros.h"
33 #include "iamf/common/utils/map_utils.h"
34 #include "iamf/common/utils/numeric_utils.h"
35 #include "iamf/obu/mix_presentation.h"
36 #include "iamf/obu/param_definitions.h"
37 #include "iamf/obu/types.h"
38 
39 namespace iamf_tools {
40 
41 namespace {
42 
FillAnnotationsLanguageAndAnnotations(const iamf_tools_cli_proto::MixPresentationObuMetadata & mix_presentation_metadata,DecodedUleb128 & count_label,std::vector<std::string> & annotations_language,std::vector<std::string> & localized_presentation_annotations)43 void FillAnnotationsLanguageAndAnnotations(
44     const iamf_tools_cli_proto::MixPresentationObuMetadata&
45         mix_presentation_metadata,
46     DecodedUleb128& count_label, std::vector<std::string>& annotations_language,
47     std::vector<std::string>& localized_presentation_annotations) {
48   count_label = mix_presentation_metadata.count_label();
49 
50   annotations_language.reserve(mix_presentation_metadata.count_label());
51   // Prioritize the `annotations_language` field from IAMF v1.1.0.
52   if (!mix_presentation_metadata.annotations_language().empty()) {
53     for (const auto& language :
54          mix_presentation_metadata.annotations_language()) {
55       annotations_language.push_back(language);
56     }
57   } else if (!mix_presentation_metadata.language_labels().empty()) {
58     LOG(WARNING) << "Please upgrade `language_labels` to "
59                     "`annotations_language`.";
60     for (const auto& language_label :
61          mix_presentation_metadata.language_labels()) {
62       annotations_language.push_back(language_label);
63     }
64   }
65 
66   localized_presentation_annotations.reserve(
67       mix_presentation_metadata.count_label());
68   // Prioritize the `localized_presentation_annotations` field from
69   // IAMF v1.1.0.
70   if (!mix_presentation_metadata.localized_presentation_annotations().empty()) {
71     for (const auto& localized_presentation_annotation :
72          mix_presentation_metadata.localized_presentation_annotations()) {
73       localized_presentation_annotations.push_back(
74           localized_presentation_annotation);
75     }
76   } else if (!mix_presentation_metadata.mix_presentation_annotations_array()
77                   .empty()) {
78     LOG(WARNING) << "Please upgrade `mix_presentation_annotations_array` to "
79                     "`localized_presentation_annotations`.";
80     for (const auto& mix_presentation_annotation :
81          mix_presentation_metadata.mix_presentation_annotations_array()) {
82       localized_presentation_annotations.push_back(
83           mix_presentation_annotation.mix_presentation_friendly_label());
84     }
85   }
86 }
87 
ReserveNumSubMixes(const iamf_tools_cli_proto::MixPresentationObuMetadata & mix_presentation_metadata,std::vector<MixPresentationSubMix> & sub_mixes)88 void ReserveNumSubMixes(const iamf_tools_cli_proto::MixPresentationObuMetadata&
89                             mix_presentation_metadata,
90                         std::vector<MixPresentationSubMix>& sub_mixes) {
91   if (mix_presentation_metadata.has_num_sub_mixes()) {
92     LOG(WARNING) << "Ignoring deprecated `num_sub_mixes` field."
93                  << "Please remove it.";
94   }
95 
96   sub_mixes.reserve(mix_presentation_metadata.sub_mixes_size());
97 }
98 
ReserveSubMixNumAudioElements(const iamf_tools_cli_proto::MixPresentationSubMix & input_sub_mix,MixPresentationSubMix & sub_mix)99 void ReserveSubMixNumAudioElements(
100     const iamf_tools_cli_proto::MixPresentationSubMix& input_sub_mix,
101     MixPresentationSubMix& sub_mix) {
102   if (input_sub_mix.has_num_audio_elements()) {
103     LOG(WARNING) << "Ignoring deprecated `num_audio_elements` field."
104                  << "Please remove it.";
105   }
106   sub_mix.audio_elements.reserve(input_sub_mix.audio_elements_size());
107 }
108 
FillLocalizedElementAnnotations(const iamf_tools_cli_proto::SubMixAudioElement & input_sub_mix_audio_element,SubMixAudioElement & sub_mix_audio_element)109 absl::Status FillLocalizedElementAnnotations(
110     const iamf_tools_cli_proto::SubMixAudioElement& input_sub_mix_audio_element,
111     SubMixAudioElement& sub_mix_audio_element) {
112   if (!input_sub_mix_audio_element.localized_element_annotations().empty()) {
113     for (const auto& localized_element_annotation :
114          input_sub_mix_audio_element.localized_element_annotations()) {
115       sub_mix_audio_element.localized_element_annotations.push_back(
116           localized_element_annotation);
117     }
118   } else if (!input_sub_mix_audio_element
119                   .mix_presentation_element_annotations_array()
120                   .empty()) {
121     LOG(WARNING)
122         << "Please upgrade `mix_presentation_element_annotations_array` to "
123            "`localized_element_annotations`.";
124     for (const auto& input_audio_element_friendly_label :
125          input_sub_mix_audio_element
126              .mix_presentation_element_annotations_array()) {
127       sub_mix_audio_element.localized_element_annotations.push_back(
128           input_audio_element_friendly_label.audio_element_friendly_label());
129     }
130   }
131   return absl::OkStatus();
132 }
133 
FillRenderingConfig(const iamf_tools_cli_proto::RenderingConfig & input_rendering_config,RenderingConfig & rendering_config)134 absl::Status FillRenderingConfig(
135     const iamf_tools_cli_proto::RenderingConfig& input_rendering_config,
136     RenderingConfig& rendering_config) {
137   switch (input_rendering_config.headphones_rendering_mode()) {
138     using enum iamf_tools_cli_proto::HeadPhonesRenderingMode;
139     using enum RenderingConfig::HeadphonesRenderingMode;
140     case HEADPHONES_RENDERING_MODE_STEREO:
141       rendering_config.headphones_rendering_mode =
142           kHeadphonesRenderingModeStereo;
143       break;
144     case HEADPHONES_RENDERING_MODE_BINAURAL:
145       rendering_config.headphones_rendering_mode =
146           kHeadphonesRenderingModeBinaural;
147       break;
148     case HEADPHONES_RENDERING_MODE_RESERVED_2:
149       rendering_config.headphones_rendering_mode =
150           kHeadphonesRenderingModeReserved2;
151       break;
152     case HEADPHONES_RENDERING_MODE_RESERVED_3:
153       rendering_config.headphones_rendering_mode =
154           kHeadphonesRenderingModeReserved3;
155       break;
156     default:
157       return absl::InvalidArgumentError(
158           absl::StrCat("Unknown headphones_rendering_mode= ",
159                        input_rendering_config.headphones_rendering_mode()));
160   }
161 
162   RETURN_IF_NOT_OK(StaticCastIfInRange<uint32_t, uint8_t>(
163       "RenderingConfig.reserved", input_rendering_config.reserved(),
164       rendering_config.reserved));
165 
166   const auto user_size =
167       input_rendering_config.rendering_config_extension_size();
168   rendering_config.rendering_config_extension_size = user_size;
169   rendering_config.rendering_config_extension_bytes.resize(user_size);
170   return StaticCastSpanIfInRange(
171       "rendering_config_extension_bytes",
172       absl::MakeConstSpan(
173           input_rendering_config.rendering_config_extension_bytes()),
174       absl::MakeSpan(rendering_config.rendering_config_extension_bytes));
175 }
176 
177 // Prefers selecting `element_mix_gain` (IAMF v1.1.0 field) if it present over
178 // `element_mix_config.mix_gain` (deprecated in the proto based on IAMF v1.0
179 // spec).
SelectElementMixConfig(const iamf_tools_cli_proto::SubMixAudioElement & sub_mix_audio_element)180 const iamf_tools_cli_proto::MixGainParamDefinition& SelectElementMixConfig(
181     const iamf_tools_cli_proto::SubMixAudioElement& sub_mix_audio_element) {
182   if (sub_mix_audio_element.has_element_mix_gain()) {
183     return sub_mix_audio_element.element_mix_gain();
184   } else {
185     LOG(WARNING)
186         << "Please upgrade `element_mix_config` to `element_mix_gain`.";
187     return sub_mix_audio_element.element_mix_config().mix_gain();
188   }
189 }
190 
191 // Prefers selecting `output_mix_gain` (IAMF v1.1.0 field) if it present over
192 // `output_mix_config.output_mix_gain` (deprecated in the proto based on IAMF
193 // v1.0 spec).
SelectOutputMixConfig(const iamf_tools_cli_proto::MixPresentationSubMix & mix_presentation_sub_mix)194 const iamf_tools_cli_proto::MixGainParamDefinition& SelectOutputMixConfig(
195     const iamf_tools_cli_proto::MixPresentationSubMix&
196         mix_presentation_sub_mix) {
197   if (mix_presentation_sub_mix.has_output_mix_gain()) {
198     return mix_presentation_sub_mix.output_mix_gain();
199   } else {
200     LOG(WARNING) << "Please upgrade `output_mix_config` to `output_mix_gain`.";
201     return mix_presentation_sub_mix.output_mix_config().output_mix_gain();
202   }
203 }
204 
FillMixConfig(const iamf_tools_cli_proto::MixGainParamDefinition & input_mix_gain,MixGainParamDefinition & mix_gain)205 absl::Status FillMixConfig(
206     const iamf_tools_cli_proto::MixGainParamDefinition& input_mix_gain,
207     MixGainParamDefinition& mix_gain) {
208   RETURN_IF_NOT_OK(
209       CopyParamDefinition(input_mix_gain.param_definition(), mix_gain));
210   RETURN_IF_NOT_OK(StaticCastIfInRange<int32_t, int16_t>(
211       "MixGainParamDefinition.default_mix_gain",
212       input_mix_gain.default_mix_gain(), mix_gain.default_mix_gain_));
213 
214   return absl::OkStatus();
215 }
216 
CopyReservedOrBinauralLayout(Layout::LayoutType layout,const::iamf_tools_cli_proto::LoudspeakersReservedOrBinauralLayout & reserved_or_binaural_layout,Layout & obu_layout)217 absl::Status CopyReservedOrBinauralLayout(
218     Layout::LayoutType layout,
219     const ::iamf_tools_cli_proto::LoudspeakersReservedOrBinauralLayout&
220         reserved_or_binaural_layout,
221     Layout& obu_layout) {
222   obu_layout.layout_type = layout;
223   LoudspeakersReservedOrBinauralLayout obu_reserved_or_binaural_layout;
224   RETURN_IF_NOT_OK(StaticCastIfInRange<uint32_t, uint8_t>(
225       "LoudspeakersReservedOrBinauralLayout.reserved",
226       reserved_or_binaural_layout.reserved(),
227       obu_reserved_or_binaural_layout.reserved));
228 
229   obu_layout.specific_layout = obu_reserved_or_binaural_layout;
230   return absl::OkStatus();
231 }
232 
FillLayouts(const iamf_tools_cli_proto::MixPresentationSubMix & input_sub_mix,MixPresentationSubMix & sub_mix)233 absl::Status FillLayouts(
234     const iamf_tools_cli_proto::MixPresentationSubMix& input_sub_mix,
235     MixPresentationSubMix& sub_mix) {
236   if (input_sub_mix.has_num_layouts()) {
237     LOG(WARNING) << "Ignoring deprecated `num_layouts` field."
238                  << "Please remove it.";
239   }
240 
241   // Reserve the layouts vector and copy in the layouts.
242   sub_mix.layouts.reserve(input_sub_mix.layouts_size());
243 
244   for (const auto& input_layout : input_sub_mix.layouts()) {
245     const auto& input_loudness_layout = input_layout.loudness_layout();
246     MixPresentationLayout layout;
247 
248     switch (input_loudness_layout.layout_type()) {
249       using enum iamf_tools_cli_proto::LayoutType;
250       using enum Layout::LayoutType;
251       case LAYOUT_TYPE_RESERVED_0:
252         RETURN_IF_NOT_OK(CopyReservedOrBinauralLayout(
253             kLayoutTypeReserved0,
254             input_loudness_layout.reserved_or_binaural_layout(),
255             layout.loudness_layout));
256         break;
257       case LAYOUT_TYPE_RESERVED_1:
258         RETURN_IF_NOT_OK(CopyReservedOrBinauralLayout(
259             kLayoutTypeReserved1,
260             input_loudness_layout.reserved_or_binaural_layout(),
261             layout.loudness_layout));
262         break;
263       case LAYOUT_TYPE_BINAURAL:
264         RETURN_IF_NOT_OK(CopyReservedOrBinauralLayout(
265             kLayoutTypeBinaural,
266             input_loudness_layout.reserved_or_binaural_layout(),
267             layout.loudness_layout));
268         break;
269       case LAYOUT_TYPE_LOUDSPEAKERS_SS_CONVENTION: {
270         layout.loudness_layout.layout_type =
271             kLayoutTypeLoudspeakersSsConvention;
272         LoudspeakersSsConventionLayout obu_ss_layout;
273         RETURN_IF_NOT_OK(MixPresentationGenerator::CopySoundSystem(
274             input_loudness_layout.ss_layout().sound_system(),
275             obu_ss_layout.sound_system));
276         RETURN_IF_NOT_OK(StaticCastIfInRange<uint32_t, uint8_t>(
277             "LoudspeakersSsConventionLayout.reserved",
278             input_loudness_layout.ss_layout().reserved(),
279             obu_ss_layout.reserved));
280         layout.loudness_layout.specific_layout = obu_ss_layout;
281         break;
282       }
283       default:
284         return absl::InvalidArgumentError(absl::StrCat(
285             "Unknown layout_type= ", input_loudness_layout.layout_type()));
286     }
287 
288     RETURN_IF_NOT_OK(MixPresentationGenerator::CopyInfoType(
289         input_layout.loudness(), layout.loudness.info_type));
290 
291     RETURN_IF_NOT_OK(
292         MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
293             input_layout.loudness(), layout.loudness));
294     RETURN_IF_NOT_OK(MixPresentationGenerator::CopyUserAnchoredLoudness(
295         input_layout.loudness(), layout.loudness));
296     RETURN_IF_NOT_OK(MixPresentationGenerator::CopyUserLayoutExtension(
297         input_layout.loudness(), layout.loudness));
298 
299     sub_mix.layouts.push_back(layout);
300   }
301 
302   return absl::OkStatus();
303 }
304 
FillMixPresentationTags(bool append_build_information_tag,const iamf_tools_cli_proto::MixPresentationTags & mix_presentation_tags,std::optional<MixPresentationTags> & obu_mix_presentation_tags)305 absl::Status FillMixPresentationTags(
306     bool append_build_information_tag,
307     const iamf_tools_cli_proto::MixPresentationTags& mix_presentation_tags,
308     std::optional<MixPresentationTags>& obu_mix_presentation_tags) {
309   if (mix_presentation_tags.has_num_tags()) {
310     LOG(WARNING) << "Ignoring deprecated `num_tags` field. Please remove it.";
311   }
312   obu_mix_presentation_tags = MixPresentationTags{};
313 
314   // Calculate the total number of tags, including automatically added ones.
315   const size_t num_tags = mix_presentation_tags.tags().size() +
316                           (append_build_information_tag ? 1 : 0);
317   // At the OBU it must fit into a `uint8_t`.
318   uint8_t obu_num_tags;
319   RETURN_IF_NOT_OK(StaticCastIfInRange<size_t, uint8_t>(
320       "Total number of MixPresentationTags.tags", num_tags, obu_num_tags));
321   obu_mix_presentation_tags->tags.reserve(obu_num_tags);
322   for (const auto& input_tag : mix_presentation_tags.tags()) {
323     obu_mix_presentation_tags->tags.emplace_back(MixPresentationTags::Tag{
324         .tag_name = input_tag.tag_name(),
325         .tag_value = input_tag.tag_value(),
326     });
327   }
328   // Append the build information tag.
329   if (append_build_information_tag) {
330     // TODO(b/388577499): Include the commit hash at build time, in the
331     //                    `iamf_encoder` tag value.
332     obu_mix_presentation_tags->tags.emplace_back(MixPresentationTags::Tag{
333         .tag_name = "iamf_encoder",
334         .tag_value = "GitHub/iamf-tools",
335     });
336   }
337 
338   return absl::OkStatus();
339 }
340 
341 }  // namespace
342 
CopySoundSystem(iamf_tools_cli_proto::SoundSystem input_sound_system,LoudspeakersSsConventionLayout::SoundSystem & output_sound_system)343 absl::Status MixPresentationGenerator::CopySoundSystem(
344     iamf_tools_cli_proto::SoundSystem input_sound_system,
345     LoudspeakersSsConventionLayout::SoundSystem& output_sound_system) {
346   static const auto kProtoToInternalSoundSystem =
347       BuildStaticMapFromPairs(LookupTables::kProtoAndInternalSoundSystems);
348 
349   return CopyFromMap(
350       *kProtoToInternalSoundSystem, input_sound_system,
351       "Internal version of proto `SoundSystem`= ", output_sound_system);
352 }
353 
CopyInfoType(const iamf_tools_cli_proto::LoudnessInfo & input_loudness_info,uint8_t & loudness_info_type)354 absl::Status MixPresentationGenerator::CopyInfoType(
355     const iamf_tools_cli_proto::LoudnessInfo& input_loudness_info,
356     uint8_t& loudness_info_type) {
357   if (input_loudness_info.has_deprecated_info_type()) {
358     return absl::InvalidArgumentError(
359         "Please upgrade the `deprecated_info_type` "
360         "field to the new `info_type_bit_masks` field."
361         "\nSuggested upgrades:\n"
362         "- `deprecated_info_type: 0` -> `info_type_bit_masks: []`\n"
363         "- `deprecated_info_type: 1` -> `info_type_bit_masks: "
364         "[LOUDNESS_INFO_TYPE_TRUE_PEAK]`\n"
365         "- `deprecated_info_type: 2` -> `info_type_bit_masks: "
366         "[LOUDNESS_INFO_TYPE_ANCHORED_LOUDNESS]`\n"
367         "- `deprecated_info_type: 3` -> `info_type_bit_masks: "
368         "[LOUDNESS_INFO_TYPE_TRUE_PEAK, "
369         "LOUDNESS_INFO_TYPE_ANCHORED_LOUDNESS]`\n");
370   }
371 
372   static const auto kProtoToInternalInfoTypeBitmask =
373       BuildStaticMapFromPairs(LookupTables::kProtoAndInternalInfoTypeBitmasks);
374 
375   uint8_t accumulated_info_type_bitmask = 0;
376   for (int i = 0; i < input_loudness_info.info_type_bit_masks_size(); ++i) {
377     LoudnessInfo::InfoTypeBitmask user_output_bit_mask;
378     RETURN_IF_NOT_OK(CopyFromMap(
379         *kProtoToInternalInfoTypeBitmask,
380         input_loudness_info.info_type_bit_masks(i),
381         absl::StrCat("Internal version of proto `LoudnessInfoTypeBitMask(", i,
382                      ")= "),
383         user_output_bit_mask));
384 
385     // Track the accumulated bit mask.
386     accumulated_info_type_bitmask |= static_cast<uint8_t>(user_output_bit_mask);
387   }
388 
389   loudness_info_type = accumulated_info_type_bitmask;
390   return absl::OkStatus();
391 }
392 
CopyUserIntegratedLoudnessAndPeaks(const iamf_tools_cli_proto::LoudnessInfo & user_loudness,LoudnessInfo & output_loudness)393 absl::Status MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
394     const iamf_tools_cli_proto::LoudnessInfo& user_loudness,
395     LoudnessInfo& output_loudness) {
396   RETURN_IF_NOT_OK(StaticCastIfInRange<int32_t, int16_t>(
397       "LoudnessInfo.integrated_loudness", user_loudness.integrated_loudness(),
398       output_loudness.integrated_loudness));
399   RETURN_IF_NOT_OK(StaticCastIfInRange<int32_t, int16_t>(
400       "LoudnessInfo.digital_peak", user_loudness.digital_peak(),
401       output_loudness.digital_peak));
402 
403   if ((output_loudness.info_type & LoudnessInfo::kTruePeak) != 0) {
404     RETURN_IF_NOT_OK(StaticCastIfInRange<int32_t, int16_t>(
405         "LoudnessInfo.true_peak", user_loudness.true_peak(),
406         output_loudness.true_peak));
407   }
408 
409   return absl::OkStatus();
410 }
411 
CopyUserAnchoredLoudness(const iamf_tools_cli_proto::LoudnessInfo & user_loudness,LoudnessInfo & output_loudness)412 absl::Status MixPresentationGenerator::CopyUserAnchoredLoudness(
413     const iamf_tools_cli_proto::LoudnessInfo& user_loudness,
414     LoudnessInfo& output_loudness) {
415   if ((output_loudness.info_type & LoudnessInfo::kAnchoredLoudness) == 0) {
416     // Not using anchored loudness.
417     return absl::OkStatus();
418   }
419   if (user_loudness.anchored_loudness().has_num_anchored_loudness()) {
420     LOG(WARNING) << "Ignoring deprecated `num_anchored_loudness` field. Please "
421                     "remove it.";
422   }
423 
424   uint8_t num_anchored_loudness;
425   RETURN_IF_NOT_OK(StaticCastIfInRange<size_t, uint8_t>(
426       "Number of LoudnessInfo.anchored_loudness",
427       user_loudness.anchored_loudness().anchor_elements_size(),
428       num_anchored_loudness));
429 
430   for (const auto& metadata_anchor_element :
431        user_loudness.anchored_loudness().anchor_elements()) {
432     AnchoredLoudnessElement::AnchorElement obu_anchor_element;
433     switch (metadata_anchor_element.anchor_element()) {
434       using enum iamf_tools_cli_proto::AnchorType;
435       using enum AnchoredLoudnessElement::AnchorElement;
436       case ANCHOR_TYPE_UNKNOWN:
437         obu_anchor_element = kAnchorElementUnknown;
438         break;
439       case ANCHOR_TYPE_DIALOGUE:
440         obu_anchor_element = kAnchorElementDialogue;
441         break;
442       case ANCHOR_TYPE_ALBUM:
443         obu_anchor_element = kAnchorElementAlbum;
444         break;
445       default:
446         return absl::InvalidArgumentError(
447             absl::StrCat("Unknown anchor_element= ",
448                          metadata_anchor_element.anchor_element()));
449     }
450 
451     int16_t obu_anchored_loudness;
452     RETURN_IF_NOT_OK(StaticCastIfInRange<int32_t, int16_t>(
453         "AnchorElement.anchored_loudness",
454         metadata_anchor_element.anchored_loudness(), obu_anchored_loudness));
455     output_loudness.anchored_loudness.anchor_elements.push_back(
456         {obu_anchor_element, obu_anchored_loudness});
457   }
458 
459   return absl::OkStatus();
460 }
461 
CopyUserLayoutExtension(const iamf_tools_cli_proto::LoudnessInfo & user_loudness,LoudnessInfo & output_loudness)462 absl::Status MixPresentationGenerator::CopyUserLayoutExtension(
463     const iamf_tools_cli_proto::LoudnessInfo& user_loudness,
464     LoudnessInfo& output_loudness) {
465   if ((output_loudness.info_type & LoudnessInfo::kAnyLayoutExtension) == 0) {
466     // Not using layout extension.
467     return absl::OkStatus();
468   }
469   auto user_size = user_loudness.info_type_size();
470   output_loudness.layout_extension.info_type_size = user_size;
471   output_loudness.layout_extension.info_type_bytes.resize(user_size);
472   return StaticCastSpanIfInRange(
473       "layout_extension_bytes",
474       absl::MakeConstSpan(user_loudness.info_type_bytes()),
475       absl::MakeSpan(output_loudness.layout_extension.info_type_bytes));
476 }
477 
Generate(bool append_build_information_tag,std::list<MixPresentationObu> & mix_presentation_obus)478 absl::Status MixPresentationGenerator::Generate(
479     bool append_build_information_tag,
480     std::list<MixPresentationObu>& mix_presentation_obus) {
481   for (const auto& mix_presentation_metadata : mix_presentation_metadata_) {
482     struct {
483       DecodedUleb128 mix_presentation_id;
484       DecodedUleb128 count_label;
485       std::vector<std::string> annotations_language;
486       // Length `count_label`.
487       std::vector<std::string> localized_presentation_annotations;
488 
489       // Length `num_sub_mixes`.
490       std::vector<MixPresentationSubMix> sub_mixes;
491 
492       std::optional<MixPresentationTags> mix_presentation_tags;
493     } obu_args;
494 
495     obu_args.mix_presentation_id =
496         mix_presentation_metadata.mix_presentation_id();
497 
498     FillAnnotationsLanguageAndAnnotations(
499         mix_presentation_metadata, obu_args.count_label,
500         obu_args.annotations_language,
501         obu_args.localized_presentation_annotations);
502 
503     ReserveNumSubMixes(mix_presentation_metadata, obu_args.sub_mixes);
504     for (const auto& input_sub_mix : mix_presentation_metadata.sub_mixes()) {
505       MixPresentationSubMix sub_mix;
506 
507       ReserveSubMixNumAudioElements(input_sub_mix, sub_mix);
508       for (const auto& input_sub_mix_audio_element :
509            input_sub_mix.audio_elements()) {
510         SubMixAudioElement sub_mix_audio_element;
511         sub_mix_audio_element.audio_element_id =
512             input_sub_mix_audio_element.audio_element_id();
513 
514         RETURN_IF_NOT_OK(FillLocalizedElementAnnotations(
515             input_sub_mix_audio_element, sub_mix_audio_element));
516 
517         RETURN_IF_NOT_OK(
518             FillRenderingConfig(input_sub_mix_audio_element.rendering_config(),
519                                 sub_mix_audio_element.rendering_config));
520 
521         RETURN_IF_NOT_OK(
522             FillMixConfig(SelectElementMixConfig(input_sub_mix_audio_element),
523                           sub_mix_audio_element.element_mix_gain));
524         sub_mix.audio_elements.push_back(sub_mix_audio_element);
525       }
526 
527       RETURN_IF_NOT_OK(FillMixConfig(SelectOutputMixConfig(input_sub_mix),
528                                      sub_mix.output_mix_gain));
529 
530       RETURN_IF_NOT_OK(FillLayouts(input_sub_mix, sub_mix));
531       obu_args.sub_mixes.push_back(std::move(sub_mix));
532     }
533     if (mix_presentation_metadata.include_mix_presentation_tags() ||
534         append_build_information_tag) {
535       RETURN_IF_NOT_OK(FillMixPresentationTags(
536           append_build_information_tag,
537           mix_presentation_metadata.mix_presentation_tags(),
538           obu_args.mix_presentation_tags));
539     } else {
540       obu_args.mix_presentation_tags = std::nullopt;
541     }
542 
543     MixPresentationObu obu(
544         GetHeaderFromMetadata(mix_presentation_metadata.obu_header()),
545         obu_args.mix_presentation_id, obu_args.count_label,
546         obu_args.annotations_language,
547         obu_args.localized_presentation_annotations, obu_args.sub_mixes);
548     obu.mix_presentation_tags_ = obu_args.mix_presentation_tags;
549     mix_presentation_obus.emplace_back(std::move(obu));
550   }
551   return absl::OkStatus();
552 }
553 
554 }  // namespace iamf_tools
555