• 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
5  * License and the Alliance for Open Media Patent License 1.0. If the BSD
6  * 3-Clause Clear License was not distributed with this source code in the
7  * LICENSE file, you can obtain it at
8  * www.aomedia.org/license/software-license/bsd-3-c-c. If the Alliance for
9  * Open Media Patent License 1.0 was not distributed with this source code
10  * in the PATENTS file, you can obtain it at www.aomedia.org/license/patent.
11  */
12 #include "iamf/cli/proto_conversion/proto_to_obu/mix_presentation_generator.h"
13 
14 #include <array>
15 #include <cstddef>
16 #include <cstdint>
17 #include <limits>
18 #include <list>
19 #include <optional>
20 #include <string>
21 #include <variant>
22 #include <vector>
23 
24 #include "absl/status/status_matchers.h"
25 #include "absl/strings/string_view.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include "iamf/cli/proto/mix_presentation.pb.h"
29 #include "iamf/cli/proto/param_definitions.pb.h"
30 #include "iamf/cli/tests/cli_test_utils.h"
31 #include "iamf/obu/mix_presentation.h"
32 #include "iamf/obu/types.h"
33 #include "src/google/protobuf/repeated_ptr_field.h"
34 #include "src/google/protobuf/text_format.h"
35 
36 // TODO(b/296346506): Add more tests for `MixPresentationGenerator`.
37 
38 namespace iamf_tools {
39 namespace {
40 
41 using ::absl_testing::IsOk;
42 using ::testing::ElementsAreArray;
43 using ::testing::Not;
44 typedef ::google::protobuf::RepeatedPtrField<
45     iamf_tools_cli_proto::MixPresentationObuMetadata>
46     MixPresentationObuMetadatas;
47 using enum iamf_tools_cli_proto::HeadPhonesRenderingMode;
48 
49 constexpr DecodedUleb128 kMixPresentationId = 42;
50 constexpr DecodedUleb128 kAudioElementId = 300;
51 constexpr DecodedUleb128 kCommonParameterId = 999;
52 constexpr DecodedUleb128 kCommonParameterRate = 16000;
53 constexpr bool kParamDefinitionMode = true;
54 constexpr uint8_t kParamDefinitionReserved = 0;
55 constexpr int16_t kZeroMixGain = 0;
56 constexpr int16_t kNonZeroMixGain = 100;
57 constexpr bool kOmitBuildInformationTag = false;
58 constexpr bool kAppendBuildInformationTag = true;
59 
FillMixGainParamDefinition(uint32_t parameter_id,int16_t output_mix_gain,iamf_tools_cli_proto::MixGainParamDefinition & mix_gain_param_definition)60 void FillMixGainParamDefinition(
61     uint32_t parameter_id, int16_t output_mix_gain,
62     iamf_tools_cli_proto::MixGainParamDefinition& mix_gain_param_definition) {
63   mix_gain_param_definition.mutable_param_definition()->set_parameter_id(
64       parameter_id);
65   mix_gain_param_definition.mutable_param_definition()->set_parameter_rate(
66       kCommonParameterRate);
67   mix_gain_param_definition.mutable_param_definition()
68       ->set_param_definition_mode(kParamDefinitionMode);
69   mix_gain_param_definition.mutable_param_definition()->set_reserved(
70       kParamDefinitionReserved);
71   mix_gain_param_definition.set_default_mix_gain(output_mix_gain);
72 }
73 
74 // Fills `mix_presentation_metadata` with a single submix that contains a single
75 // stereo audio element.
FillMixPresentationMetadata(iamf_tools_cli_proto::MixPresentationObuMetadata * mix_presentation_metadata)76 void FillMixPresentationMetadata(
77     iamf_tools_cli_proto::MixPresentationObuMetadata*
78         mix_presentation_metadata) {
79   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
80       R"pb(
81         mix_presentation_id: 42
82         count_label: 0
83         sub_mixes {
84           audio_elements {
85             audio_element_id: 300
86             rendering_config {
87               headphones_rendering_mode: HEADPHONES_RENDERING_MODE_STEREO
88             }
89           }
90           num_layouts: 1
91           layouts {
92             loudness_layout {
93               layout_type: LAYOUT_TYPE_LOUDSPEAKERS_SS_CONVENTION
94               ss_layout { sound_system: SOUND_SYSTEM_A_0_2_0 reserved: 0 }
95             }
96             loudness {
97               info_type_bit_masks: []
98               integrated_loudness: 0
99               digital_peak: 0
100             }
101           }
102         }
103       )pb",
104       mix_presentation_metadata));
105   // Also fill in some default values for the per-element and per-submix mix
106   // gain parameters.
107   FillMixGainParamDefinition(kCommonParameterId, kZeroMixGain,
108                              *mix_presentation_metadata->mutable_sub_mixes(0)
109                                   ->mutable_audio_elements(0)
110                                   ->mutable_element_mix_gain());
111   FillMixGainParamDefinition(kCommonParameterId, kZeroMixGain,
112                              *mix_presentation_metadata->mutable_sub_mixes(0)
113                                   ->mutable_output_mix_gain());
114 }
115 
TEST(Generate,CopiesSoundSystem13_6_9_0)116 TEST(Generate, CopiesSoundSystem13_6_9_0) {
117   const auto kExpectedSoundSystem =
118       LoudspeakersSsConventionLayout::SoundSystem::kSoundSystem13_6_9_0;
119   MixPresentationObuMetadatas mix_presentation_metadata = {};
120   FillMixPresentationMetadata(mix_presentation_metadata.Add());
121   mix_presentation_metadata.at(0)
122       .mutable_sub_mixes(0)
123       ->mutable_layouts(0)
124       ->mutable_loudness_layout()
125       ->mutable_ss_layout()
126       ->set_sound_system(iamf_tools_cli_proto::SOUND_SYSTEM_13_6_9_0);
127   MixPresentationGenerator generator(mix_presentation_metadata);
128 
129   std::list<MixPresentationObu> generated_obus;
130   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
131               IsOk());
132 
133   const auto& generated_specific_layout = generated_obus.front()
134                                               .sub_mixes_[0]
135                                               .layouts[0]
136                                               .loudness_layout.specific_layout;
137   EXPECT_TRUE(std::holds_alternative<LoudspeakersSsConventionLayout>(
138       generated_specific_layout));
139   EXPECT_EQ(std::get<LoudspeakersSsConventionLayout>(generated_specific_layout)
140                 .sound_system,
141             kExpectedSoundSystem);
142 }
143 
TEST(Generate,CopiesReservedHeadphonesRenderingMode2)144 TEST(Generate, CopiesReservedHeadphonesRenderingMode2) {
145   const auto kExpectedHeadphonesRenderingMode2 =
146       RenderingConfig::kHeadphonesRenderingModeReserved2;
147   MixPresentationObuMetadatas mix_presentation_metadata = {};
148   FillMixPresentationMetadata(mix_presentation_metadata.Add());
149   mix_presentation_metadata.at(0)
150       .mutable_sub_mixes(0)
151       ->mutable_audio_elements(0)
152       ->mutable_rendering_config()
153       ->set_headphones_rendering_mode(
154           iamf_tools_cli_proto::HEADPHONES_RENDERING_MODE_RESERVED_2);
155   MixPresentationGenerator generator(mix_presentation_metadata);
156 
157   std::list<MixPresentationObu> generated_obus;
158   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
159               IsOk());
160 
161   EXPECT_EQ(generated_obus.front()
162                 .sub_mixes_[0]
163                 .audio_elements[0]
164                 .rendering_config.headphones_rendering_mode,
165             kExpectedHeadphonesRenderingMode2);
166 }
167 
TEST(Generate,CopiesReservedHeadphonesRenderingMode3)168 TEST(Generate, CopiesReservedHeadphonesRenderingMode3) {
169   const auto kExpectedHeadphonesRenderingMode3 =
170       RenderingConfig::kHeadphonesRenderingModeReserved3;
171   MixPresentationObuMetadatas mix_presentation_metadata;
172   FillMixPresentationMetadata(mix_presentation_metadata.Add());
173   mix_presentation_metadata.at(0)
174       .mutable_sub_mixes(0)
175       ->mutable_audio_elements(0)
176       ->mutable_rendering_config()
177       ->set_headphones_rendering_mode(HEADPHONES_RENDERING_MODE_RESERVED_3);
178   MixPresentationGenerator generator(mix_presentation_metadata);
179 
180   std::list<MixPresentationObu> generated_obus;
181   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
182               IsOk());
183 
184   const auto& generated_rendering_config =
185       generated_obus.front().sub_mixes_[0].audio_elements[0].rendering_config;
186   EXPECT_EQ(generated_rendering_config.headphones_rendering_mode,
187             kExpectedHeadphonesRenderingMode3);
188   EXPECT_EQ(generated_rendering_config.rendering_config_extension_size, 0);
189   EXPECT_TRUE(
190       generated_rendering_config.rendering_config_extension_bytes.empty());
191 }
192 
TEST(Generate,CopiesRenderingConfigExtension)193 TEST(Generate, CopiesRenderingConfigExtension) {
194   MixPresentationObuMetadatas mix_presentation_metadata;
195   FillMixPresentationMetadata(mix_presentation_metadata.Add());
196   auto& first_rendering_config = *mix_presentation_metadata.at(0)
197                                       .mutable_sub_mixes(0)
198                                       ->mutable_audio_elements(0)
199                                       ->mutable_rendering_config();
200   first_rendering_config.set_headphones_rendering_mode(
201       HEADPHONES_RENDERING_MODE_RESERVED_3);
202   first_rendering_config.set_rendering_config_extension_size(5);
203   first_rendering_config.set_rendering_config_extension_bytes("extra");
204   constexpr std::array<char, 5> kExpectedRenderingConfigExtensionBytes = {
205       'e', 'x', 't', 'r', 'a'};
206 
207   MixPresentationGenerator generator(mix_presentation_metadata);
208   std::list<MixPresentationObu> generated_obus;
209   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
210               IsOk());
211 
212   const auto& generated_rendering_config =
213       generated_obus.front().sub_mixes_[0].audio_elements[0].rendering_config;
214   EXPECT_EQ(generated_rendering_config.rendering_config_extension_size, 5);
215   EXPECT_THAT(generated_rendering_config.rendering_config_extension_bytes,
216               ElementsAreArray(kExpectedRenderingConfigExtensionBytes));
217 }
218 
TEST(Generate,InvalidWhenRenderingConfigExtensionIsMismatched)219 TEST(Generate, InvalidWhenRenderingConfigExtensionIsMismatched) {
220   constexpr size_t kMismatchedSize = 6;
221   MixPresentationObuMetadatas mix_presentation_metadata;
222   FillMixPresentationMetadata(mix_presentation_metadata.Add());
223   auto& first_rendering_config = *mix_presentation_metadata.at(0)
224                                       .mutable_sub_mixes(0)
225                                       ->mutable_audio_elements(0)
226                                       ->mutable_rendering_config();
227   first_rendering_config.set_headphones_rendering_mode(
228       HEADPHONES_RENDERING_MODE_RESERVED_3);
229   first_rendering_config.set_rendering_config_extension_size(kMismatchedSize);
230   first_rendering_config.set_rendering_config_extension_bytes("extra");
231 
232   MixPresentationGenerator generator(mix_presentation_metadata);
233   std::list<MixPresentationObu> undefined_generated_obus;
234 
235   EXPECT_FALSE(
236       generator.Generate(kAppendBuildInformationTag, undefined_generated_obus)
237           .ok());
238 }
239 
TEST(Generate,CopiesNoAnnotations)240 TEST(Generate, CopiesNoAnnotations) {
241   MixPresentationObuMetadatas mix_presentation_metadata;
242   FillMixPresentationMetadata(mix_presentation_metadata.Add());
243   mix_presentation_metadata.at(0).set_count_label(0);
244   mix_presentation_metadata.at(0).clear_annotations_language();
245   mix_presentation_metadata.at(0).clear_localized_presentation_annotations();
246   mix_presentation_metadata.at(0)
247       .mutable_sub_mixes(0)
248       ->mutable_audio_elements(0)
249       ->clear_localized_element_annotations();
250 
251   MixPresentationGenerator generator(mix_presentation_metadata);
252 
253   std::list<MixPresentationObu> generated_obus;
254   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
255               IsOk());
256 
257   const auto& first_obu = generated_obus.front();
258   EXPECT_TRUE(first_obu.GetAnnotationsLanguage().empty());
259   EXPECT_TRUE(first_obu.GetLocalizedPresentationAnnotations().empty());
260   EXPECT_TRUE(first_obu.sub_mixes_[0]
261                   .audio_elements[0]
262                   .localized_element_annotations.empty());
263 }
264 
TEST(Generate,CopiesDeprecatedAnnotations)265 TEST(Generate, CopiesDeprecatedAnnotations) {
266   constexpr int kCountLabel = 2;
267   const std::vector<std::string> kAnnotationsLanguage = {"en-us", "en-gb"};
268   const std::vector<std::string> kLocalizedPresentationAnnotations = {
269       "US Label", "GB Label"};
270   const std::vector<std::string> kAudioElementLocalizedElementAnnotations = {
271       "US AE Label", "GB AE Label"};
272   MixPresentationObuMetadatas mix_presentation_metadata;
273   FillMixPresentationMetadata(mix_presentation_metadata.Add());
274   auto& mix_presentation = mix_presentation_metadata.at(0);
275   mix_presentation.set_count_label(kCountLabel);
276   mix_presentation.mutable_language_labels()->Add(kAnnotationsLanguage.begin(),
277                                                   kAnnotationsLanguage.end());
278   *mix_presentation.mutable_mix_presentation_annotations_array()
279        ->Add()
280        ->mutable_mix_presentation_friendly_label() =
281       kLocalizedPresentationAnnotations[0];
282   *mix_presentation.mutable_mix_presentation_annotations_array()
283        ->Add()
284        ->mutable_mix_presentation_friendly_label() =
285       kLocalizedPresentationAnnotations[1];
286   auto* first_element_annotations_array =
287       mix_presentation.mutable_sub_mixes(0)
288           ->mutable_audio_elements(0)
289           ->mutable_mix_presentation_element_annotations_array();
290   first_element_annotations_array->Add()->set_audio_element_friendly_label(
291       kAudioElementLocalizedElementAnnotations[0]);
292   first_element_annotations_array->Add()->set_audio_element_friendly_label(
293       kAudioElementLocalizedElementAnnotations[1]);
294 
295   MixPresentationGenerator generator(mix_presentation_metadata);
296 
297   std::list<MixPresentationObu> generated_obus;
298   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
299               IsOk());
300 
301   const auto& first_obu = generated_obus.front();
302   EXPECT_EQ(first_obu.GetAnnotationsLanguage(), kAnnotationsLanguage);
303   EXPECT_EQ(first_obu.GetLocalizedPresentationAnnotations(),
304             kLocalizedPresentationAnnotations);
305   EXPECT_EQ(
306       first_obu.sub_mixes_[0].audio_elements[0].localized_element_annotations,
307       kAudioElementLocalizedElementAnnotations);
308 }
309 
TEST(Generate,CopiesAnnotations)310 TEST(Generate, CopiesAnnotations) {
311   constexpr int kCountLabel = 2;
312   const std::vector<std::string> kAnnotationsLanguage = {"en-us", "en-gb"};
313   const std::vector<std::string> kLocalizedPresentationAnnotations = {
314       "US Label", "GB Label"};
315   const std::vector<std::string> kAudioElementLocalizedElementAnnotations = {
316       "US AE Label", "GB AE Label"};
317   MixPresentationObuMetadatas mix_presentation_metadata;
318   FillMixPresentationMetadata(mix_presentation_metadata.Add());
319   auto& mix_presentation = mix_presentation_metadata.at(0);
320   mix_presentation.set_count_label(kCountLabel);
321   mix_presentation.mutable_annotations_language()->Add(
322       kAnnotationsLanguage.begin(), kAnnotationsLanguage.end());
323   mix_presentation.mutable_localized_presentation_annotations()->Add(
324       kLocalizedPresentationAnnotations.begin(),
325       kLocalizedPresentationAnnotations.end());
326   mix_presentation.mutable_sub_mixes(0)
327       ->mutable_audio_elements(0)
328       ->mutable_localized_element_annotations()
329       ->Add(kAudioElementLocalizedElementAnnotations.begin(),
330             kAudioElementLocalizedElementAnnotations.end());
331 
332   MixPresentationGenerator generator(mix_presentation_metadata);
333 
334   std::list<MixPresentationObu> generated_obus;
335   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
336               IsOk());
337 
338   const auto& first_obu = generated_obus.front();
339   EXPECT_EQ(first_obu.GetAnnotationsLanguage(), kAnnotationsLanguage);
340   EXPECT_EQ(first_obu.GetLocalizedPresentationAnnotations(),
341             kLocalizedPresentationAnnotations);
342   EXPECT_EQ(
343       first_obu.sub_mixes_[0].audio_elements[0].localized_element_annotations,
344       kAudioElementLocalizedElementAnnotations);
345 }
346 
TEST(Generate,NonDeprecatedAnnotationsTakePrecedence)347 TEST(Generate, NonDeprecatedAnnotationsTakePrecedence) {
348   constexpr int kCountLabel = 1;
349   const std::vector<std::string> kDeprecatedAnotations = {"Deprecated"};
350   const std::vector<std::string> kAnnotationsLanguage = {"en-us"};
351   const std::vector<std::string> kLocalizedPresentationAnnotations = {
352       "US Label"};
353   const std::vector<std::string> kAudioElementLocalizedElementAnnotations = {
354       "US AE Label"};
355   MixPresentationObuMetadatas mix_presentation_metadata;
356   FillMixPresentationMetadata(mix_presentation_metadata.Add());
357   auto& mix_presentation = mix_presentation_metadata.at(0);
358   mix_presentation.set_count_label(kCountLabel);
359   mix_presentation.mutable_annotations_language()->Add(
360       kAnnotationsLanguage.begin(), kAnnotationsLanguage.end());
361   mix_presentation.mutable_localized_presentation_annotations()->Add(
362       kLocalizedPresentationAnnotations.begin(),
363       kLocalizedPresentationAnnotations.end());
364   mix_presentation.mutable_sub_mixes(0)
365       ->mutable_audio_elements(0)
366       ->mutable_localized_element_annotations()
367       ->Add(kAudioElementLocalizedElementAnnotations.begin(),
368             kAudioElementLocalizedElementAnnotations.end());
369   mix_presentation.mutable_language_labels()->Add(kDeprecatedAnotations.begin(),
370                                                   kDeprecatedAnotations.end());
371   *mix_presentation.mutable_mix_presentation_annotations_array()
372        ->Add()
373        ->mutable_mix_presentation_friendly_label() = kDeprecatedAnotations[0];
374   *mix_presentation.mutable_sub_mixes(0)
375        ->mutable_audio_elements(0)
376        ->mutable_mix_presentation_element_annotations_array()
377        ->Add()
378        ->mutable_audio_element_friendly_label() = kDeprecatedAnotations[0];
379 
380   MixPresentationGenerator generator(mix_presentation_metadata);
381 
382   std::list<MixPresentationObu> generated_obus;
383   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
384               IsOk());
385 
386   const auto& first_obu = generated_obus.front();
387   EXPECT_EQ(first_obu.GetAnnotationsLanguage(), kAnnotationsLanguage);
388   EXPECT_EQ(first_obu.GetLocalizedPresentationAnnotations(),
389             kLocalizedPresentationAnnotations);
390   EXPECT_EQ(
391       first_obu.sub_mixes_[0].audio_elements[0].localized_element_annotations,
392       kAudioElementLocalizedElementAnnotations);
393 }
394 
TEST(Generate,ObeysInconsistentNumberOfLabels)395 TEST(Generate, ObeysInconsistentNumberOfLabels) {
396   const std::vector<std::string> kAnnotationsLanguage = {"Language 1",
397                                                          "Language 2"};
398   const std::vector<std::string> kOnlyOneLocalizedPresentationAnnotation = {
399       "Localized annotation 1"};
400   const std::vector<std::string> kNoAudioElementLocalizedElementAnnotations =
401       {};
402   MixPresentationObuMetadatas mix_presentation_metadata;
403   FillMixPresentationMetadata(mix_presentation_metadata.Add());
404   auto& mix_presentation = mix_presentation_metadata.at(0);
405   mix_presentation.set_count_label(2);
406   mix_presentation.mutable_annotations_language()->Add(
407       kAnnotationsLanguage.begin(), kAnnotationsLanguage.end());
408   mix_presentation.mutable_localized_presentation_annotations()->Add(
409       kOnlyOneLocalizedPresentationAnnotation.begin(),
410       kOnlyOneLocalizedPresentationAnnotation.end());
411 
412   MixPresentationGenerator generator(mix_presentation_metadata);
413 
414   std::list<MixPresentationObu> generated_obus;
415   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
416               IsOk());
417 
418   const auto& first_obu = generated_obus.front();
419   EXPECT_EQ(first_obu.GetAnnotationsLanguage(), kAnnotationsLanguage);
420   EXPECT_EQ(first_obu.GetLocalizedPresentationAnnotations(),
421             kOnlyOneLocalizedPresentationAnnotation);
422   EXPECT_EQ(
423       first_obu.sub_mixes_[0].audio_elements[0].localized_element_annotations,
424       kNoAudioElementLocalizedElementAnnotations);
425 }
426 
TEST(Generate,CopiesMixPresentationTagsWithZeroTags)427 TEST(Generate, CopiesMixPresentationTagsWithZeroTags) {
428   MixPresentationObuMetadatas mix_presentation_metadata;
429   FillMixPresentationMetadata(mix_presentation_metadata.Add());
430   auto& mix_presentation = mix_presentation_metadata.at(0);
431   mix_presentation.set_include_mix_presentation_tags(true);
432 
433   MixPresentationGenerator generator(mix_presentation_metadata);
434 
435   std::list<MixPresentationObu> generated_obus;
436   // We must avoid appending the build information tag, to exercise the "zero
437   // tags" case.
438   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus),
439               IsOk());
440 
441   const auto& first_obu = generated_obus.front();
442   ASSERT_TRUE(first_obu.mix_presentation_tags_.has_value());
443   EXPECT_TRUE(first_obu.mix_presentation_tags_->tags.empty());
444 }
445 
TEST(Generate,IgnoresDeprecatedNumTags)446 TEST(Generate, IgnoresDeprecatedNumTags) {
447   MixPresentationObuMetadatas mix_presentation_metadata;
448   FillMixPresentationMetadata(mix_presentation_metadata.Add());
449   auto& mix_presentation = mix_presentation_metadata.at(0);
450   mix_presentation.set_include_mix_presentation_tags(true);
451   constexpr uint32_t kIncorrectIgnoredNumTags = 1;
452   mix_presentation.mutable_mix_presentation_tags()->set_num_tags(
453       kIncorrectIgnoredNumTags);
454 
455   MixPresentationGenerator generator(mix_presentation_metadata);
456 
457   std::list<MixPresentationObu> generated_obus;
458   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus),
459               IsOk());
460 
461   // Ok safely ignore the deprecated `num_tags` field.
462   const auto& first_obu = generated_obus.front();
463   ASSERT_TRUE(first_obu.mix_presentation_tags_.has_value());
464   EXPECT_TRUE(first_obu.mix_presentation_tags_->tags.empty());
465 }
466 
TEST(Generate,ReturnsErrorIfUserSpecifies256TagsWithoutBuildInformation)467 TEST(Generate, ReturnsErrorIfUserSpecifies256TagsWithoutBuildInformation) {
468   MixPresentationObuMetadatas mix_presentation_metadata;
469   FillMixPresentationMetadata(mix_presentation_metadata.Add());
470   auto& mix_presentation = mix_presentation_metadata.at(0);
471   mix_presentation.set_include_mix_presentation_tags(true);
472   constexpr int kMaxNumTags = 255;
473   for (int i = 0; i < kMaxNumTags + 1; ++i) {
474     auto* tag = mix_presentation.mutable_mix_presentation_tags()->add_tags();
475     tag->set_tag_name("tag_name");
476     tag->set_tag_value("tag_value");
477   }
478 
479   MixPresentationGenerator generator(mix_presentation_metadata);
480 
481   std::list<MixPresentationObu> ignored_generated_obus;
482   EXPECT_THAT(
483       generator.Generate(kOmitBuildInformationTag, ignored_generated_obus),
484       Not(IsOk()));
485 }
486 
TEST(Generate,ReturnsErrorIfUserSpecifies255TagsWithoutBuildInformation)487 TEST(Generate, ReturnsErrorIfUserSpecifies255TagsWithoutBuildInformation) {
488   MixPresentationObuMetadatas mix_presentation_metadata;
489   FillMixPresentationMetadata(mix_presentation_metadata.Add());
490   auto& mix_presentation = mix_presentation_metadata.at(0);
491   mix_presentation.set_include_mix_presentation_tags(true);
492   constexpr int kMaxNumTags = 255;
493   for (int i = 0; i < kMaxNumTags; ++i) {
494     auto* tag = mix_presentation.mutable_mix_presentation_tags()->add_tags();
495     tag->set_tag_name("tag_name");
496     tag->set_tag_value("tag_value");
497   }
498 
499   MixPresentationGenerator generator(mix_presentation_metadata);
500 
501   // It would be OK to generate 255 tags, but the build information tag pushes
502   // the final count over the limit.
503   std::list<MixPresentationObu> ignored_generated_obus;
504   EXPECT_THAT(
505       generator.Generate(kAppendBuildInformationTag, ignored_generated_obus),
506       Not(IsOk()));
507 }
508 
TEST(Generate,CopiesDuplicateContentLanguageTags)509 TEST(Generate, CopiesDuplicateContentLanguageTags) {
510   MixPresentationObuMetadatas mix_presentation_metadata;
511   FillMixPresentationMetadata(mix_presentation_metadata.Add());
512   auto& mix_presentation = mix_presentation_metadata.at(0);
513   mix_presentation.set_include_mix_presentation_tags(true);
514   mix_presentation.mutable_mix_presentation_tags()->set_num_tags(2);
515   auto* first_tag =
516       mix_presentation.mutable_mix_presentation_tags()->add_tags();
517   first_tag->set_tag_name("content_language");
518   first_tag->set_tag_value("eng");
519   auto* second_tag =
520       mix_presentation.mutable_mix_presentation_tags()->add_tags();
521   second_tag->set_tag_name("content_language");
522   second_tag->set_tag_value("kor");
523 
524   MixPresentationGenerator generator(mix_presentation_metadata);
525 
526   std::list<MixPresentationObu> generated_obus;
527   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus),
528               IsOk());
529 
530   const auto& first_obu = generated_obus.front();
531   ASSERT_TRUE(first_obu.mix_presentation_tags_.has_value());
532   ASSERT_EQ(first_obu.mix_presentation_tags_->tags.size(), 2);
533   EXPECT_EQ(first_obu.mix_presentation_tags_->tags[0].tag_name,
534             "content_language");
535   EXPECT_EQ(first_obu.mix_presentation_tags_->tags[0].tag_value, "eng");
536   EXPECT_EQ(first_obu.mix_presentation_tags_->tags[1].tag_name,
537             "content_language");
538   EXPECT_EQ(first_obu.mix_presentation_tags_->tags[1].tag_value, "kor");
539 }
540 
TEST(Generate,MixPresentationTagsAreAbsentIncludeMixPresentationTagsAndOmitBuildInformationTagAreFalse)541 TEST(
542     Generate,
543     MixPresentationTagsAreAbsentIncludeMixPresentationTagsAndOmitBuildInformationTagAreFalse) {
544   MixPresentationObuMetadatas mix_presentation_metadata;
545   FillMixPresentationMetadata(mix_presentation_metadata.Add());
546   auto& mix_presentation = mix_presentation_metadata.at(0);
547   mix_presentation.set_include_mix_presentation_tags(false);
548   auto* tag = mix_presentation.mutable_mix_presentation_tags()->add_tags();
549   tag->set_tag_name("ignored_tag_name");
550   tag->set_tag_value("ignored_tag_value");
551 
552   MixPresentationGenerator generator(mix_presentation_metadata);
553 
554   // To exercise the "absent" tags case, we must avoid appending the build
555   // information tag.
556   std::list<MixPresentationObu> generated_obus;
557   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus),
558               IsOk());
559 
560   const auto& first_obu = generated_obus.front();
561   EXPECT_FALSE(first_obu.mix_presentation_tags_.has_value());
562 }
563 
564 struct MixPresentationTagsPresenceTestCase {
565   bool include_mix_presentation_tags;
566   bool append_build_information_tag;
567 
568   // `std::nullopt` means that the tags are expected to be absent.
569   std::optional<size_t> expected_num_tags;
570   bool expect_build_information_tag_to_be_present;
571 };
572 
573 class MixPresentationTagsPresenceTest
574     : public ::testing::TestWithParam<MixPresentationTagsPresenceTestCase> {};
575 
TEST_P(MixPresentationTagsPresenceTest,MixPresentationTagsArePresentOrAbsent)576 TEST_P(MixPresentationTagsPresenceTest, MixPresentationTagsArePresentOrAbsent) {
577   const auto& test_case = GetParam();
578   MixPresentationObuMetadatas mix_presentation_metadata;
579   FillMixPresentationMetadata(mix_presentation_metadata.Add());
580   auto& mix_presentation = mix_presentation_metadata.at(0);
581   mix_presentation.set_include_mix_presentation_tags(
582       test_case.include_mix_presentation_tags);
583   MixPresentationGenerator generator(mix_presentation_metadata);
584 
585   std::list<MixPresentationObu> generated_obus;
586   EXPECT_THAT(generator.Generate(test_case.append_build_information_tag,
587                                  generated_obus),
588               IsOk());
589   ASSERT_FALSE(generated_obus.empty());
590   const auto& first_obu = generated_obus.front();
591 
592   if (test_case.expected_num_tags.has_value()) {
593     EXPECT_TRUE(first_obu.mix_presentation_tags_.has_value());
594     EXPECT_EQ(first_obu.mix_presentation_tags_->tags.size(),
595               *test_case.expected_num_tags);
596     // If the tags are present, the last tag may be the build information tag.
597     if (test_case.expect_build_information_tag_to_be_present) {
598       ASSERT_FALSE(first_obu.mix_presentation_tags_->tags.empty());
599       EXPECT_THAT(first_obu.mix_presentation_tags_->tags.back(),
600                   TagMatchesBuildInformation());
601     }
602   } else {
603     EXPECT_FALSE(first_obu.mix_presentation_tags_.has_value());
604   }
605 }
606 
607 // To strictly exercise Simple or Base profile bitstream with no extensions, we
608 // can disable both the mix presentation tags and the build information tag.
609 INSTANTIATE_TEST_SUITE_P(
610     MixPresentationTagsAreAbsent, MixPresentationTagsPresenceTest,
611     testing::ValuesIn<MixPresentationTagsPresenceTestCase>({{
612         .include_mix_presentation_tags = false,
613         .append_build_information_tag = false,
614         .expected_num_tags = std::nullopt,
615         .expect_build_information_tag_to_be_present = false,
616     }}));
617 
618 // The spec supports an edge case where the bitstream signals zero tags are
619 // present. This mode also is useful if a user wants to provide their own tags,
620 // but disable the build information tag.
621 INSTANTIATE_TEST_SUITE_P(
622     MixPresentationTagsArePresentWithZeroTags, MixPresentationTagsPresenceTest,
623     testing::ValuesIn<MixPresentationTagsPresenceTestCase>({{
624         .include_mix_presentation_tags = true,
625         .append_build_information_tag = false,
626         .expected_num_tags = 0,
627         .expect_build_information_tag_to_be_present = false,
628     }}));
629 
630 // Other modes result in a tag describing the build information. A compliant
631 // Simple or Base profile decoder should handle the presence of the tag, but
632 // it's not required to understand what it means.
633 INSTANTIATE_TEST_SUITE_P(
634     MixPresentationTagsArePresentWithBuildInformationTag,
635     MixPresentationTagsPresenceTest,
636     testing::ValuesIn<MixPresentationTagsPresenceTestCase>(
637         {{
638              .include_mix_presentation_tags = true,
639              .append_build_information_tag = true,
640              .expected_num_tags = 1,
641              .expect_build_information_tag_to_be_present = true,
642          },
643          {
644              .include_mix_presentation_tags = false,
645              .append_build_information_tag = true,
646              .expected_num_tags = 1,
647              .expect_build_information_tag_to_be_present = true,
648          }}));
649 
TEST(Generate,CopiesOutputMixGain)650 TEST(Generate, CopiesOutputMixGain) {
651   MixPresentationObuMetadatas mix_presentation_metadata;
652   FillMixPresentationMetadata(mix_presentation_metadata.Add());
653   FillMixGainParamDefinition(kCommonParameterId, kNonZeroMixGain,
654                              *mix_presentation_metadata.at(0)
655                                   .mutable_sub_mixes(0)
656                                   ->mutable_output_mix_gain());
657   MixPresentationGenerator generator(mix_presentation_metadata);
658 
659   std::list<MixPresentationObu> generated_obus;
660   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
661               IsOk());
662 
663   const auto& first_output_mix_gain =
664       generated_obus.front().sub_mixes_[0].output_mix_gain;
665   EXPECT_EQ(first_output_mix_gain.parameter_id_, kCommonParameterId);
666   EXPECT_EQ(first_output_mix_gain.parameter_rate_, kCommonParameterRate);
667   EXPECT_EQ(first_output_mix_gain.param_definition_mode_, kParamDefinitionMode);
668   EXPECT_EQ(first_output_mix_gain.reserved_, kParamDefinitionReserved);
669   EXPECT_EQ(first_output_mix_gain.default_mix_gain_, kNonZeroMixGain);
670 }
671 
TEST(Generate,CopiesElementMixGain)672 TEST(Generate, CopiesElementMixGain) {
673   MixPresentationObuMetadatas mix_presentation_metadata;
674   FillMixPresentationMetadata(mix_presentation_metadata.Add());
675   FillMixGainParamDefinition(kCommonParameterId, kNonZeroMixGain,
676                              *mix_presentation_metadata.at(0)
677                                   .mutable_sub_mixes(0)
678                                   ->mutable_audio_elements(0)
679                                   ->mutable_element_mix_gain());
680   MixPresentationGenerator generator(mix_presentation_metadata);
681 
682   std::list<MixPresentationObu> generated_obus;
683   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
684               IsOk());
685 
686   const auto& first_element_mix_gain =
687       generated_obus.front().sub_mixes_[0].audio_elements[0].element_mix_gain;
688   EXPECT_EQ(first_element_mix_gain.parameter_id_, kCommonParameterId);
689   EXPECT_EQ(first_element_mix_gain.parameter_rate_, kCommonParameterRate);
690   EXPECT_EQ(first_element_mix_gain.param_definition_mode_,
691             kParamDefinitionMode);
692   EXPECT_EQ(first_element_mix_gain.reserved_, kParamDefinitionReserved);
693   EXPECT_EQ(first_element_mix_gain.default_mix_gain_, kNonZeroMixGain);
694 }
695 
TEST(Generate,CopiesDeprecatedOutputMixConfig)696 TEST(Generate, CopiesDeprecatedOutputMixConfig) {
697   MixPresentationObuMetadatas mix_presentation_metadata;
698   FillMixPresentationMetadata(mix_presentation_metadata.Add());
699   mix_presentation_metadata.at(0).mutable_sub_mixes(0)->clear_output_mix_gain();
700   FillMixGainParamDefinition(kCommonParameterId, kNonZeroMixGain,
701                              *mix_presentation_metadata.at(0)
702                                   .mutable_sub_mixes(0)
703                                   ->mutable_output_mix_config()
704                                   ->mutable_output_mix_gain());
705   MixPresentationGenerator generator(mix_presentation_metadata);
706 
707   std::list<MixPresentationObu> generated_obus;
708   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
709               IsOk());
710 
711   const auto& first_output_mix_gain =
712       generated_obus.front().sub_mixes_[0].output_mix_gain;
713   EXPECT_EQ(first_output_mix_gain.parameter_id_, kCommonParameterId);
714   EXPECT_EQ(first_output_mix_gain.parameter_rate_, kCommonParameterRate);
715   EXPECT_EQ(first_output_mix_gain.param_definition_mode_, kParamDefinitionMode);
716   EXPECT_EQ(first_output_mix_gain.reserved_, kParamDefinitionReserved);
717   EXPECT_EQ(first_output_mix_gain.default_mix_gain_, kNonZeroMixGain);
718 }
719 
TEST(Generate,CopiesDeprecatedElementMixConfig)720 TEST(Generate, CopiesDeprecatedElementMixConfig) {
721   MixPresentationObuMetadatas mix_presentation_metadata;
722   FillMixPresentationMetadata(mix_presentation_metadata.Add());
723   mix_presentation_metadata.at(0)
724       .mutable_sub_mixes(0)
725       ->mutable_audio_elements(0)
726       ->clear_element_mix_gain();
727   FillMixGainParamDefinition(kCommonParameterId, kNonZeroMixGain,
728                              *mix_presentation_metadata.at(0)
729                                   .mutable_sub_mixes(0)
730                                   ->mutable_audio_elements(0)
731                                   ->mutable_element_mix_config()
732                                   ->mutable_mix_gain());
733   MixPresentationGenerator generator(mix_presentation_metadata);
734 
735   std::list<MixPresentationObu> generated_obus;
736   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
737               IsOk());
738 
739   const auto& first_element_mix_gain =
740       generated_obus.front().sub_mixes_[0].audio_elements[0].element_mix_gain;
741   EXPECT_EQ(first_element_mix_gain.parameter_id_, kCommonParameterId);
742   EXPECT_EQ(first_element_mix_gain.parameter_rate_, kCommonParameterRate);
743   EXPECT_EQ(first_element_mix_gain.param_definition_mode_,
744             kParamDefinitionMode);
745   EXPECT_EQ(first_element_mix_gain.reserved_, kParamDefinitionReserved);
746   EXPECT_EQ(first_element_mix_gain.default_mix_gain_, kNonZeroMixGain);
747 }
748 
TEST(Generate,NonDeprecatedElementMixConfigTakesPrecedence)749 TEST(Generate, NonDeprecatedElementMixConfigTakesPrecedence) {
750   constexpr uint32_t kDeprecatedParameterId = 2000;
751   constexpr uint32_t kNonDeprecatedParameterId = 3000;
752   constexpr int16_t kDeprecatedElementMixGain = 100;
753   constexpr int16_t kNonDeprecatedElementMixGain = 200;
754 
755   MixPresentationObuMetadatas mix_presentation_metadata;
756   FillMixPresentationMetadata(mix_presentation_metadata.Add());
757   // When both both the deprecated and non-deprecated element mix config are
758   // provided, the non-deprecated config takes precedence.
759   FillMixGainParamDefinition(kNonDeprecatedParameterId,
760                              kNonDeprecatedElementMixGain,
761                              *mix_presentation_metadata.at(0)
762                                   .mutable_sub_mixes(0)
763                                   ->mutable_audio_elements(0)
764                                   ->mutable_element_mix_gain());
765   FillMixGainParamDefinition(kDeprecatedParameterId, kDeprecatedElementMixGain,
766                              *mix_presentation_metadata.at(0)
767                                   .mutable_sub_mixes(0)
768                                   ->mutable_audio_elements(0)
769                                   ->mutable_element_mix_config()
770                                   ->mutable_mix_gain());
771   MixPresentationGenerator generator(mix_presentation_metadata);
772 
773   std::list<MixPresentationObu> generated_obus;
774   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
775               IsOk());
776 
777   const auto& first_element_mix_gain =
778       generated_obus.front().sub_mixes_[0].audio_elements[0].element_mix_gain;
779   EXPECT_EQ(first_element_mix_gain.parameter_id_, kNonDeprecatedParameterId);
780   EXPECT_EQ(first_element_mix_gain.default_mix_gain_,
781             kNonDeprecatedElementMixGain);
782 }
783 
TEST(Generate,NonDeprecatedOutputMixConfigTakesPrecedence)784 TEST(Generate, NonDeprecatedOutputMixConfigTakesPrecedence) {
785   constexpr uint32_t kDeprecatedParameterId = 2000;
786   constexpr uint32_t kNonDeprecatedParameterId = 3000;
787   constexpr int16_t kDeprecatedElementMixGain = 100;
788   constexpr int16_t kNonDeprecatedElementMixGain = 200;
789 
790   MixPresentationObuMetadatas mix_presentation_metadata;
791   FillMixPresentationMetadata(mix_presentation_metadata.Add());
792   // When both both the deprecated and non-deprecated element mix config are
793   // provided, the non-deprecated config takes precedence.
794   FillMixGainParamDefinition(kNonDeprecatedParameterId,
795                              kNonDeprecatedElementMixGain,
796                              *mix_presentation_metadata.at(0)
797                                   .mutable_sub_mixes(0)
798                                   ->mutable_output_mix_gain());
799   FillMixGainParamDefinition(kDeprecatedParameterId, kDeprecatedElementMixGain,
800                              *mix_presentation_metadata.at(0)
801                                   .mutable_sub_mixes(0)
802                                   ->mutable_output_mix_config()
803                                   ->mutable_output_mix_gain());
804   MixPresentationGenerator generator(mix_presentation_metadata);
805 
806   std::list<MixPresentationObu> generated_obus;
807   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus),
808               IsOk());
809 
810   const auto& first_output_mix_gain =
811       generated_obus.front().sub_mixes_[0].output_mix_gain;
812   EXPECT_EQ(first_output_mix_gain.parameter_id_, kNonDeprecatedParameterId);
813   EXPECT_EQ(first_output_mix_gain.default_mix_gain_,
814             kNonDeprecatedElementMixGain);
815 }
816 
817 class MixPresentationGeneratorTest : public ::testing::Test {
818  public:
SetUp()819   void SetUp() override {
820     FillMixPresentationMetadata(mix_presentation_metadata_.Add());
821 
822     AddMixPresentationObuWithAudioElementIds(
823         kMixPresentationId, {kAudioElementId}, kCommonParameterId,
824         kCommonParameterRate, expected_obus_);
825   }
826 
827  protected:
828   MixPresentationObuMetadatas mix_presentation_metadata_;
829   std::list<MixPresentationObu> generated_obus_;
830   std::list<MixPresentationObu> expected_obus_;
831 };
832 
TEST_F(MixPresentationGeneratorTest,EmptyUserMetadataGeneratesNoObus)833 TEST_F(MixPresentationGeneratorTest, EmptyUserMetadataGeneratesNoObus) {
834   MixPresentationGenerator generator(/*mix_presentation_metadata=*/{});
835   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
836               IsOk());
837 
838   EXPECT_TRUE(generated_obus_.empty());
839 }
840 
TEST_F(MixPresentationGeneratorTest,SSConventionWithOneStereoAudioElement)841 TEST_F(MixPresentationGeneratorTest, SSConventionWithOneStereoAudioElement) {
842   MixPresentationGenerator generator(mix_presentation_metadata_);
843   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus_),
844               IsOk());
845   EXPECT_EQ(generated_obus_, expected_obus_);
846 }
847 
TEST_F(MixPresentationGeneratorTest,SupportsUtf8)848 TEST_F(MixPresentationGeneratorTest, SupportsUtf8) {
849   const std::string kUtf8FourByteSequenceCode = "\xf0\x9d\x85\x9e\x00)";
850   mix_presentation_metadata_.at(0).set_count_label(1);
851   mix_presentation_metadata_.at(0).add_localized_presentation_annotations(
852       kUtf8FourByteSequenceCode);
853 
854   MixPresentationGenerator generator(mix_presentation_metadata_);
855   ASSERT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
856               IsOk());
857 
858   const auto generated_annotations =
859       generated_obus_.back().GetLocalizedPresentationAnnotations();
860   ASSERT_FALSE(generated_annotations.empty());
861   EXPECT_EQ(generated_annotations[0], kUtf8FourByteSequenceCode);
862 }
863 
TEST_F(MixPresentationGeneratorTest,InvalidHeadphonesRenderingMode)864 TEST_F(MixPresentationGeneratorTest, InvalidHeadphonesRenderingMode) {
865   mix_presentation_metadata_.at(0)
866       .mutable_sub_mixes(0)
867       ->mutable_audio_elements(0)
868       ->mutable_rendering_config()
869       ->set_headphones_rendering_mode(
870           iamf_tools_cli_proto::HEADPHONES_RENDERING_MODE_INVALID);
871   MixPresentationGenerator generator(mix_presentation_metadata_);
872 
873   EXPECT_FALSE(
874       generator.Generate(kAppendBuildInformationTag, generated_obus_).ok());
875 }
876 
TEST_F(MixPresentationGeneratorTest,IgnoresDeprecatedNumSubMixes)877 TEST_F(MixPresentationGeneratorTest, IgnoresDeprecatedNumSubMixes) {
878   // This test assumes the default metadata has one sub mix.
879   constexpr uint32_t kExpectedNumSubMixes = 1;
880   ASSERT_EQ(mix_presentation_metadata_.at(0).sub_mixes_size(),
881             kExpectedNumSubMixes);
882   // Include a strange value for the deprecated `num_sub_mixes` field.
883   constexpr uint32_t kIncorrectIgnoredNumSubMixes = 2;
884   mix_presentation_metadata_.at(0).set_num_sub_mixes(
885       kIncorrectIgnoredNumSubMixes);
886   MixPresentationGenerator generator(mix_presentation_metadata_);
887 
888   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
889               IsOk());
890 
891   // Regardless of the deprecated `num_layouts` field, the number of layouts is
892   // inferred the `layouts` array.
893   EXPECT_EQ(generated_obus_.back().GetNumSubMixes(), kExpectedNumSubMixes);
894   EXPECT_EQ(generated_obus_.back().sub_mixes_.size(), kExpectedNumSubMixes);
895 }
896 
TEST_F(MixPresentationGeneratorTest,IgnoresDeprecatedNumAudioElements)897 TEST_F(MixPresentationGeneratorTest, IgnoresDeprecatedNumAudioElements) {
898   // This test assumes the default metadata has one audio element.
899   constexpr uint32_t kExpectedNumAudioElements = 1;
900   ASSERT_EQ(mix_presentation_metadata_.at(0).sub_mixes(0).audio_elements_size(),
901             kExpectedNumAudioElements);
902   // Include a strange value for the deprecated `num_audio_elements`.
903   constexpr uint32_t kIncorrectIgnoredNumAudioElements = 2;
904   mix_presentation_metadata_.at(0).mutable_sub_mixes(0)->set_num_audio_elements(
905       kIncorrectIgnoredNumAudioElements);
906   MixPresentationGenerator generator(mix_presentation_metadata_);
907 
908   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
909               IsOk());
910 
911   // Regardless of the deprecated `num_audio_elements` field, the number of
912   // audio elements the `audio_elements` array.
913   EXPECT_EQ(generated_obus_.back().sub_mixes_[0].audio_elements.size(),
914             kExpectedNumAudioElements);
915 }
916 
TEST_F(MixPresentationGeneratorTest,IgnoresDeprecatedNumLayouts)917 TEST_F(MixPresentationGeneratorTest, IgnoresDeprecatedNumLayouts) {
918   // This test assumes the default metadata has one layout.
919   constexpr uint32_t kExpectedNumLayouts = 1;
920   ASSERT_EQ(mix_presentation_metadata_.at(0).sub_mixes(0).layouts().size(),
921             kExpectedNumLayouts);
922   // Include a strange value for the deprecated `num_layouts`.
923   constexpr uint32_t kIncorrectIgnoredNumLayouts = 2;
924   mix_presentation_metadata_.at(0).mutable_sub_mixes(0)->set_num_layouts(
925       kIncorrectIgnoredNumLayouts);
926   MixPresentationGenerator generator(mix_presentation_metadata_);
927 
928   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
929               IsOk());
930 
931   // Regardless of the deprecated `num_layouts` field, the number of layouts is
932   // inferred from the `layouts` array.
933   EXPECT_EQ(generated_obus_.back().sub_mixes_[0].layouts.size(),
934             kExpectedNumLayouts);
935 }
936 
TEST_F(MixPresentationGeneratorTest,CopiesUserLoudness)937 TEST_F(MixPresentationGeneratorTest, CopiesUserLoudness) {
938   const int16_t kIntegratedLoudness = -100;
939   const int16_t kDigitalPeak = -101;
940   const int16_t kTruePeak = -102;
941   auto* loudness = mix_presentation_metadata_.at(0)
942                        .mutable_sub_mixes(0)
943                        ->mutable_layouts(0)
944                        ->mutable_loudness();
945   loudness->add_info_type_bit_masks(
946       iamf_tools_cli_proto::LOUDNESS_INFO_TYPE_TRUE_PEAK);
947   loudness->set_integrated_loudness(kIntegratedLoudness);
948   loudness->set_digital_peak(kDigitalPeak);
949   loudness->set_true_peak(kTruePeak);
950   expected_obus_.back().sub_mixes_[0].layouts[0].loudness = {
951       .info_type = LoudnessInfo::kTruePeak,
952       .integrated_loudness = kIntegratedLoudness,
953       .digital_peak = kDigitalPeak,
954       .true_peak = kTruePeak};
955 
956   MixPresentationGenerator generator(mix_presentation_metadata_);
957 
958   EXPECT_THAT(generator.Generate(kAppendBuildInformationTag, generated_obus_),
959               IsOk());
960 }
961 
TEST_F(MixPresentationGeneratorTest,InvalidLayoutType)962 TEST_F(MixPresentationGeneratorTest, InvalidLayoutType) {
963   mix_presentation_metadata_.at(0)
964       .mutable_sub_mixes(0)
965       ->mutable_layouts(0)
966       ->mutable_loudness_layout()
967       ->set_layout_type(iamf_tools_cli_proto::LAYOUT_TYPE_INVALID);
968   MixPresentationGenerator generator(mix_presentation_metadata_);
969 
970   EXPECT_FALSE(
971       generator.Generate(kAppendBuildInformationTag, generated_obus_).ok());
972 }
973 
TEST_F(MixPresentationGeneratorTest,ReservedLayoutWithOneStereoAudioElement)974 TEST_F(MixPresentationGeneratorTest, ReservedLayoutWithOneStereoAudioElement) {
975   // Overwrite the user metadata with a reserved layout.
976   auto& input_sub_mix = *mix_presentation_metadata_.at(0).mutable_sub_mixes(0);
977   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
978       R"pb(
979         loudness_layout {
980           layout_type: LAYOUT_TYPE_RESERVED_1
981           reserved_or_binaural_layout { reserved: 0 }
982         }
983         loudness { info_type_bit_masks: [] }
984       )pb",
985       input_sub_mix.mutable_layouts(0)));
986 
987   // Overwrite the expected OBU with a reserved layout. The actual loudness
988   // measurements are not modified by the generator.
989   expected_obus_.back().sub_mixes_[0].layouts = {
990       {.loudness_layout = {.layout_type = Layout::kLayoutTypeReserved1,
991                            .specific_layout =
992                                LoudspeakersReservedOrBinauralLayout{.reserved =
993                                                                         0}},
994        .loudness = {.info_type = 0}}};
995 
996   MixPresentationGenerator generator(mix_presentation_metadata_);
997   EXPECT_THAT(generator.Generate(kOmitBuildInformationTag, generated_obus_),
998               IsOk());
999   EXPECT_EQ(generated_obus_, expected_obus_);
1000 }
1001 
TEST(CopySoundSystem,ValidSoundSystem)1002 TEST(CopySoundSystem, ValidSoundSystem) {
1003   iamf_tools_cli_proto::SoundSystem input_sound_system =
1004       iamf_tools_cli_proto::SOUND_SYSTEM_A_0_2_0;
1005 
1006   LoudspeakersSsConventionLayout::SoundSystem output_sound_system;
1007   EXPECT_THAT(MixPresentationGenerator::CopySoundSystem(input_sound_system,
1008                                                         output_sound_system),
1009               IsOk());
1010   EXPECT_EQ(output_sound_system,
1011             LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0);
1012 }
1013 
TEST(CopySoundSystem,InvalidSoundSystem)1014 TEST(CopySoundSystem, InvalidSoundSystem) {
1015   iamf_tools_cli_proto::SoundSystem input_sound_system =
1016       iamf_tools_cli_proto::SOUND_SYSTEM_INVALID;
1017 
1018   LoudspeakersSsConventionLayout::SoundSystem output_sound_system;
1019   EXPECT_FALSE(MixPresentationGenerator::CopySoundSystem(input_sound_system,
1020                                                          output_sound_system)
1021                    .ok());
1022 }
1023 
TEST(CopyInfoType,Zero)1024 TEST(CopyInfoType, Zero) {
1025   iamf_tools_cli_proto::LoudnessInfo user_loudness_info;
1026 
1027   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1028       R"pb(
1029         info_type_bit_masks: []
1030       )pb",
1031       &user_loudness_info));
1032 
1033   uint8_t output_info_type;
1034   EXPECT_THAT(MixPresentationGenerator::CopyInfoType(user_loudness_info,
1035                                                      output_info_type),
1036               IsOk());
1037   EXPECT_EQ(output_info_type, 0);
1038 }
1039 
TEST(CopyInfoType,SeveralLoudnessTypes)1040 TEST(CopyInfoType, SeveralLoudnessTypes) {
1041   iamf_tools_cli_proto::LoudnessInfo user_loudness_info;
1042 
1043   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1044       R"pb(
1045         # The order of provided flags does not matter.
1046         info_type_bit_masks: [
1047           LOUDNESS_INFO_TYPE_RESERVED_64,
1048           LOUDNESS_INFO_TYPE_TRUE_PEAK,
1049           LOUDNESS_INFO_TYPE_ANCHORED_LOUDNESS
1050         ]
1051       )pb",
1052       &user_loudness_info));
1053 
1054   uint8_t output_info_type;
1055   EXPECT_THAT(MixPresentationGenerator::CopyInfoType(user_loudness_info,
1056                                                      output_info_type),
1057               IsOk());
1058   EXPECT_EQ(output_info_type, LoudnessInfo::kInfoTypeBitMask64 |
1059                                   LoudnessInfo::kAnchoredLoudness |
1060                                   LoudnessInfo::kTruePeak);
1061 }
1062 
TEST(CopyInfoType,DeprecatedInfoTypeIsNotSupported)1063 TEST(CopyInfoType, DeprecatedInfoTypeIsNotSupported) {
1064   iamf_tools_cli_proto::LoudnessInfo user_loudness_info;
1065 
1066   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1067       R"pb(
1068         deprecated_info_type: 2  # Anchored Loudness.
1069       )pb",
1070       &user_loudness_info));
1071 
1072   uint8_t unused_output_info_type;
1073   EXPECT_FALSE(MixPresentationGenerator::CopyInfoType(user_loudness_info,
1074                                                       unused_output_info_type)
1075                    .ok());
1076 }
1077 
TEST(CopyUserIntegratedLoudnessAndPeaks,WithoutTruePeak)1078 TEST(CopyUserIntegratedLoudnessAndPeaks, WithoutTruePeak) {
1079   // `info_type` must be configured as a prerequisite.
1080   LoudnessInfo output_loudness = {.info_type = 0};
1081 
1082   // Configure user data to copy in.
1083   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1084   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1085       R"pb(
1086         # `integrated_loudness` and `digital_peak` are always included.
1087         integrated_loudness: -99 digital_peak: -100
1088       )pb",
1089       &user_loudness));
1090 
1091   // Configured expected data. The function only writes to the
1092   // integrated loudness and peak loudness fields.
1093   const LoudnessInfo expected_output_loudness = {
1094       .info_type = 0, .integrated_loudness = -99, .digital_peak = -100};
1095 
1096   EXPECT_THAT(MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
1097                   user_loudness, output_loudness),
1098               IsOk());
1099   EXPECT_EQ(output_loudness, expected_output_loudness);
1100 }
1101 
TEST(CopyUserIntegratedLoudnessAndPeaks,WithTruePeak)1102 TEST(CopyUserIntegratedLoudnessAndPeaks, WithTruePeak) {
1103   // `info_type` must be configured as a prerequisite.
1104   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kTruePeak};
1105 
1106   // Configure user data to copy in.
1107   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1108   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1109       R"pb(
1110         integrated_loudness: -99
1111         digital_peak: -100
1112         # `true_peak` is included when `info_type & kTruePeak == 1`.
1113         true_peak: -101
1114       )pb",
1115       &user_loudness));
1116 
1117   // Configured expected data. The function only writes to the
1118   // integrated loudness and peak loudness fields.
1119   const LoudnessInfo expected_output_loudness = {
1120       .info_type = LoudnessInfo::kTruePeak,
1121       .integrated_loudness = -99,
1122       .digital_peak = -100,
1123       .true_peak = -101};
1124 
1125   EXPECT_THAT(MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
1126                   user_loudness, output_loudness),
1127               IsOk());
1128   EXPECT_EQ(output_loudness, expected_output_loudness);
1129 }
1130 
TEST(CopyUserIntegratedLoudnessAndPeaks,ValidatesIntegratedLoudness)1131 TEST(CopyUserIntegratedLoudnessAndPeaks, ValidatesIntegratedLoudness) {
1132   // Configure valid prerequisites.
1133   LoudnessInfo output_loudness = {.info_type = 0};
1134   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1135   user_loudness.set_digital_peak(0);
1136 
1137   // Configure `integrated_loudness` that cannot fit into an `int16_t`.
1138   user_loudness.set_integrated_loudness(std::numeric_limits<int16_t>::max() +
1139                                         1);
1140   EXPECT_FALSE(MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
1141                    user_loudness, output_loudness)
1142                    .ok());
1143 }
1144 
TEST(CopyUserIntegratedLoudnessAndPeaks,ValidatesDigitalPeak)1145 TEST(CopyUserIntegratedLoudnessAndPeaks, ValidatesDigitalPeak) {
1146   // Configure valid prerequisites.
1147   LoudnessInfo output_loudness = {.info_type = 0};
1148   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1149   user_loudness.set_integrated_loudness(0);
1150 
1151   // Configure `digital_peak` that cannot fit into an `int16_t`.
1152   user_loudness.set_digital_peak(std::numeric_limits<int16_t>::min() - 1);
1153 
1154   EXPECT_FALSE(MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
1155                    user_loudness, output_loudness)
1156                    .ok());
1157 }
1158 
TEST(CopyUserIntegratedLoudnessAndPeaks,ValidatesTruePeak)1159 TEST(CopyUserIntegratedLoudnessAndPeaks, ValidatesTruePeak) {
1160   // Configure valid prerequisites.
1161   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kTruePeak};
1162   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1163   user_loudness.set_integrated_loudness(0);
1164   user_loudness.set_digital_peak(0);
1165 
1166   // Configure `true_peak` that cannot fit into an `int16_t`.
1167   user_loudness.set_true_peak(std::numeric_limits<int16_t>::max() + 1);
1168 
1169   EXPECT_FALSE(MixPresentationGenerator::CopyUserIntegratedLoudnessAndPeaks(
1170                    user_loudness, output_loudness)
1171                    .ok());
1172 }
1173 
TEST(CopyUserAnchoredLoudness,TwoAnchorElements)1174 TEST(CopyUserAnchoredLoudness, TwoAnchorElements) {
1175   // `info_type` must be configured as a prerequisite.
1176   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kAnchoredLoudness};
1177 
1178   // Configure user data to copy in.
1179   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1180   google::protobuf::TextFormat::ParseFromString(
1181       R"pb(
1182         anchored_loudness {
1183           anchor_elements:
1184           [ { anchor_element: ANCHOR_TYPE_DIALOGUE anchored_loudness: 1000 }
1185             , { anchor_element: ANCHOR_TYPE_ALBUM anchored_loudness: 1001 }]
1186       )pb",
1187       &user_loudness);
1188 
1189   // Configured expected data. The function only writes to the
1190   // `AnchoredLoudness`.
1191   const AnchoredLoudness expected_output_loudness = {
1192       .anchor_elements = {
1193           {.anchor_element = AnchoredLoudnessElement::kAnchorElementDialogue,
1194            .anchored_loudness = 1000},
1195           {.anchor_element = AnchoredLoudnessElement::kAnchorElementAlbum,
1196            .anchored_loudness = 1001}}};
1197 
1198   EXPECT_THAT(MixPresentationGenerator::CopyUserAnchoredLoudness(
1199                   user_loudness, output_loudness),
1200               IsOk());
1201   EXPECT_EQ(output_loudness.anchored_loudness, expected_output_loudness);
1202 }
1203 
TEST(CopyUserAnchoredLoudness,IgnoresDeprecatedNumAnchoredLoudnessField)1204 TEST(CopyUserAnchoredLoudness, IgnoresDeprecatedNumAnchoredLoudnessField) {
1205   // Set up an anchored loudness which no anchor elements, but incorrectly
1206   // claims there is one.
1207   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kAnchoredLoudness};
1208   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1209   user_loudness.mutable_anchored_loudness()->set_num_anchored_loudness(1);
1210 
1211   EXPECT_THAT(MixPresentationGenerator::CopyUserAnchoredLoudness(
1212                   user_loudness, output_loudness),
1213               IsOk());
1214 
1215   // Regardless of the deprecated `num_anchored_loudness` field, the number of
1216   // anchor elements is inferred from the `anchor_elements` array.
1217   EXPECT_TRUE(output_loudness.anchored_loudness.anchor_elements.empty());
1218 }
1219 
TEST(CopyUserAnchoredLoudness,IllegalUnknownAnchorElementEnum)1220 TEST(CopyUserAnchoredLoudness, IllegalUnknownAnchorElementEnum) {
1221   // `info_type` must be configured as a prerequisite.
1222   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kAnchoredLoudness};
1223 
1224   // Configure user data to copy in.
1225   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1226   google::protobuf::TextFormat::ParseFromString(
1227       R"pb(
1228         anchored_loudness {
1229           anchor_elements:
1230           [ { anchor_element: ANCHOR_TYPE_NOT_DEFINED anchored_loudness: 1000 }
1231       )pb",
1232       &user_loudness);
1233 
1234   EXPECT_FALSE(MixPresentationGenerator::CopyUserAnchoredLoudness(
1235                    user_loudness, output_loudness)
1236                    .ok());
1237 }
1238 
TEST(CopyUserLayoutExtension,AllInfoTypeExtensions)1239 TEST(CopyUserLayoutExtension, AllInfoTypeExtensions) {
1240   // `info_type` must be configured as a prerequisite.
1241   LoudnessInfo output_loudness = {.info_type =
1242                                       LoudnessInfo::kAnyLayoutExtension};
1243 
1244   // Configure user data to copy in.
1245   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1246   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1247       R"pb(
1248         info_type_size: 3 info_type_bytes: "abc"
1249       )pb",
1250       &user_loudness));
1251 
1252   // Configured expected data. The function only writes to the
1253   // `LayoutExtension`.
1254   const LayoutExtension expected_layout_extension = {
1255       .info_type_size = 3,
1256       .info_type_bytes = std::vector<uint8_t>({'a', 'b', 'c'})};
1257 
1258   EXPECT_THAT(MixPresentationGenerator::CopyUserLayoutExtension(
1259                   user_loudness, output_loudness),
1260               IsOk());
1261   EXPECT_EQ(output_loudness.layout_extension, expected_layout_extension);
1262 }
1263 
TEST(CopyUserLayoutExtension,OneInfoTypeExtension)1264 TEST(CopyUserLayoutExtension, OneInfoTypeExtension) {
1265   // `info_type` must be configured as a prerequisite.
1266   LoudnessInfo output_loudness = {.info_type = LoudnessInfo::kInfoTypeBitMask4};
1267 
1268   // Configure user data to copy in.
1269   iamf_tools_cli_proto::LoudnessInfo user_loudness;
1270   ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
1271       R"pb(
1272         info_type_size: 3 info_type_bytes: "abc"
1273       )pb",
1274       &user_loudness));
1275 
1276   // Configured expected data. The function only writes to the
1277   // `LayoutExtension`.
1278   const LayoutExtension expected_layout_extension = {
1279       .info_type_size = 3,
1280       .info_type_bytes = std::vector<uint8_t>({'a', 'b', 'c'})};
1281 
1282   EXPECT_THAT(MixPresentationGenerator::CopyUserLayoutExtension(
1283                   user_loudness, output_loudness),
1284               IsOk());
1285   EXPECT_EQ(output_loudness.layout_extension, expected_layout_extension);
1286 }
1287 
1288 }  // namespace
1289 }  // namespace iamf_tools
1290