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