1 /*
2 * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12 #include "iamf/obu/mix_presentation.h"
13
14 #include <cstdint>
15 #include <memory>
16 #include <optional>
17 #include <string>
18 #include <variant>
19 #include <vector>
20
21 #include "absl/status/status.h"
22 #include "absl/status/status_matchers.h"
23 #include "absl/types/span.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "iamf/common/leb_generator.h"
27 #include "iamf/common/read_bit_buffer.h"
28 #include "iamf/common/utils/bit_buffer_util.h"
29 #include "iamf/common/utils/tests/test_utils.h"
30 #include "iamf/common/write_bit_buffer.h"
31 #include "iamf/obu/obu_header.h"
32 #include "iamf/obu/param_definitions.h"
33 #include "iamf/obu/tests/obu_test_base.h"
34 #include "iamf/obu/types.h"
35
36 namespace iamf_tools {
37 namespace {
38
39 using ::absl_testing::IsOk;
40
41 // Bit shifts for the `layout_type` and `sound_system` fields which are stored
42 // in the same byte.
43 constexpr int kLayoutTypeBitShift = 6;
44 constexpr int kSoundSystemBitShift = 2;
45
46 class MixPresentationObuTest : public ObuTestBase, public testing::Test {
47 public:
48 // Used to populate a `MixPresentationSubMix`.
49 struct DynamicSubMixArguments {
50 // Outer vector has length `num_audio_elements`. Inner has length
51 // `num_subblocks`.
52 std::vector<std::vector<uint32_t>> element_mix_gain_subblocks;
53
54 // Length `num_subblocks`.
55 std::vector<uint32_t> output_mix_gain_subblocks;
56 };
57
MixPresentationObuTest()58 MixPresentationObuTest()
59 : ObuTestBase(
60 /*expected_header=*/{kObuIaMixPresentation << 3, 47},
61 /*expected_payload=*/
62 {
63 // Start Mix OBU.
64 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1',
65 '\0', 1,
66 // Start Submix 1
67 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
68 // Start RenderingConfig.
69 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
70 // End RenderingConfig.
71 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
72 // Start Layout 1 (of Submix 1).
73 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
74 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
75 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
76 // End Mix OBU.
77 }),
78 mix_presentation_id_(10),
79 count_label_(1),
80 annotations_language_({"en-us"}),
81 localized_presentation_annotations_({"Mix 1"}),
82 sub_mixes_(
83 {{.audio_elements = {{
84 .audio_element_id = 11,
85 .localized_element_annotations = {"Submix 1"},
86 .rendering_config =
87 {.headphones_rendering_mode =
88 RenderingConfig::kHeadphonesRenderingModeStereo,
89 .reserved = 0,
90 .rendering_config_extension_size = 0,
91 .rendering_config_extension_bytes = {}},
92 }},
93 .layouts = {{.loudness_layout =
94 {.layout_type =
95 Layout::kLayoutTypeLoudspeakersSsConvention,
96 .specific_layout =
97 LoudspeakersSsConventionLayout{
98 .sound_system =
99 LoudspeakersSsConventionLayout::
100 kSoundSystemA_0_2_0,
101 .reserved = 0}},
102 .loudness = {.info_type = LoudnessInfo::kTruePeak,
103 .integrated_loudness = 18,
104 .digital_peak = 19,
105 .true_peak = 20}}}}}),
106 dynamic_sub_mix_args_({{.element_mix_gain_subblocks = {{}},
107 .output_mix_gain_subblocks = {}}}),
108 mix_presentation_tags_(std::nullopt) {
109 MixGainParamDefinition element_mix_gain;
110 element_mix_gain.parameter_id_ = 12;
111 element_mix_gain.parameter_rate_ = 13;
112 element_mix_gain.param_definition_mode_ = true;
113 element_mix_gain.reserved_ = 0;
114 element_mix_gain.default_mix_gain_ = 14;
115 sub_mixes_[0].audio_elements[0].element_mix_gain = element_mix_gain;
116
117 MixGainParamDefinition output_mix_gain;
118 output_mix_gain.parameter_id_ = 15;
119 output_mix_gain.parameter_rate_ = 16;
120 output_mix_gain.param_definition_mode_ = true;
121 output_mix_gain.reserved_ = 0;
122 output_mix_gain.default_mix_gain_ = 17;
123 sub_mixes_[0].output_mix_gain = output_mix_gain;
124 }
125
126 ~MixPresentationObuTest() override = default;
127
128 protected:
InitExpectOk()129 void InitExpectOk() override { InitMixPresentationObu(); }
130
WriteObuExpectOk(WriteBitBuffer & wb)131 void WriteObuExpectOk(WriteBitBuffer& wb) override {
132 EXPECT_THAT(obu_->ValidateAndWriteObu(wb), IsOk());
133 }
134
135 std::unique_ptr<MixPresentationObu> obu_;
136
137 DecodedUleb128 mix_presentation_id_;
138 DecodedUleb128 count_label_;
139 std::vector<std::string> annotations_language_;
140 // Length `count_label`.
141 std::vector<std::string> localized_presentation_annotations_;
142
143 // Length `num_sub_mixes`.
144 std::vector<MixPresentationSubMix> sub_mixes_;
145
146 // Length `num_sub_mixes`.
147 std::vector<DynamicSubMixArguments> dynamic_sub_mix_args_;
148
149 std::optional<MixPresentationTags> mix_presentation_tags_;
150
151 private:
152 // Copies over data into the output argument `obu->sub_mix[i]`.
InitSubMixDynamicMemory(int i)153 void InitSubMixDynamicMemory(int i) {
154 // The sub-mix to initialize.
155 auto& sub_mix = sub_mixes_[i];
156 const auto& sub_mix_args = dynamic_sub_mix_args_[i];
157
158 // Create and initialize memory for the subblock durations within each
159 // audio element.
160 ASSERT_EQ(sub_mix_args.element_mix_gain_subblocks.size(),
161 sub_mix.audio_elements.size());
162 for (int j = 0; j < sub_mix.audio_elements.size(); ++j) {
163 auto& audio_element = sub_mix.audio_elements[j];
164 const auto& audio_element_args =
165 sub_mix_args.element_mix_gain_subblocks[j];
166
167 audio_element.element_mix_gain.InitializeSubblockDurations(
168 static_cast<DecodedUleb128>(audio_element_args.size()));
169 ASSERT_EQ(audio_element_args.size(),
170 audio_element.element_mix_gain.GetNumSubblocks());
171 for (int k = 0; k < audio_element_args.size(); ++k) {
172 EXPECT_THAT(audio_element.element_mix_gain.SetSubblockDuration(
173 k, audio_element_args[k]),
174 IsOk());
175 }
176 }
177
178 // Create and initialize memory for the subblock durations within the
179 // output mix config.
180
181 sub_mix.output_mix_gain.InitializeSubblockDurations(
182 static_cast<DecodedUleb128>(
183 sub_mix_args.output_mix_gain_subblocks.size()));
184 ASSERT_EQ(sub_mix_args.output_mix_gain_subblocks.size(),
185 sub_mix.output_mix_gain.GetNumSubblocks());
186 for (int j = 0; j < sub_mix_args.output_mix_gain_subblocks.size(); ++j) {
187 EXPECT_THAT(sub_mix.output_mix_gain.SetSubblockDuration(
188 j, sub_mix_args.output_mix_gain_subblocks[j]),
189 IsOk());
190 }
191 }
192
193 // Copies over data into the output argument `obu`.
InitMixPresentationObu()194 void InitMixPresentationObu() {
195 // Allocate and initialize some dynamic memory within `sub_mixes`.
196 ASSERT_EQ(dynamic_sub_mix_args_.size(), sub_mixes_.size());
197 for (int i = 0; i < sub_mixes_.size(); ++i) {
198 InitSubMixDynamicMemory(i);
199 }
200
201 // Construct and transfer ownership of the memory to the OBU.
202 obu_ = std::make_unique<MixPresentationObu>(
203 header_, mix_presentation_id_, count_label_, annotations_language_,
204 localized_presentation_annotations_, sub_mixes_);
205 }
206 };
207
TEST_F(MixPresentationObuTest,ConstructorSetsObuTyoe)208 TEST_F(MixPresentationObuTest, ConstructorSetsObuTyoe) {
209 InitExpectOk();
210
211 EXPECT_EQ(obu_->header_.obu_type, kObuIaMixPresentation);
212 }
213
TEST_F(MixPresentationObuTest,DefaultSingleStereo)214 TEST_F(MixPresentationObuTest, DefaultSingleStereo) { InitAndTestWrite(); }
215
TEST_F(MixPresentationObuTest,RedundantCopy)216 TEST_F(MixPresentationObuTest, RedundantCopy) {
217 header_.obu_redundant_copy = true;
218
219 expected_header_ = {kObuIaMixPresentation << 3 | kObuRedundantCopyBitMask,
220 47},
221
222 InitAndTestWrite();
223 }
224
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithIllegalTrimmingStatusFlag)225 TEST_F(MixPresentationObuTest,
226 ValidateAndWriteFailsWithIllegalTrimmingStatusFlag) {
227 header_.obu_trimming_status_flag = true;
228
229 InitExpectOk();
230 WriteBitBuffer unused_wb(0);
231 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
232 }
233
TEST_F(MixPresentationObuTest,ExtensionHeader)234 TEST_F(MixPresentationObuTest, ExtensionHeader) {
235 header_.obu_extension_flag = true;
236 header_.extension_header_size = 5;
237 header_.extension_header_bytes = {'e', 'x', 't', 'r', 'a'};
238
239 expected_header_ = {kObuIaMixPresentation << 3 | kObuExtensionFlagBitMask,
240 // `obu_size`.
241 53,
242 // `extension_header_size`.
243 5,
244 // `extension_header_bytes`.
245 'e', 'x', 't', 'r', 'a'};
246
247 InitAndTestWrite();
248 }
249
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInvalidNumSubMixes)250 TEST_F(MixPresentationObuTest, ValidateAndWriteFailsWithInvalidNumSubMixes) {
251 sub_mixes_.clear();
252 dynamic_sub_mix_args_.clear();
253
254 InitExpectOk();
255 WriteBitBuffer unused_wb(0);
256 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
257 }
258
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInconsistentCountLabelAndAnnotationsLanguage)259 TEST_F(MixPresentationObuTest,
260 ValidateAndWriteFailsWithInconsistentCountLabelAndAnnotationsLanguage) {
261 count_label_ = 1;
262 annotations_language_.clear();
263
264 InitExpectOk();
265 WriteBitBuffer unused_wb(0);
266
267 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
268 }
269
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInconsistentCountLabelAndLocalizedPresentationAnnotations)270 TEST_F(
271 MixPresentationObuTest,
272 ValidateAndWriteFailsWithInconsistentCountLabelAndLocalizedPresentationAnnotations) {
273 count_label_ = 1;
274 localized_presentation_annotations_.clear();
275
276 InitExpectOk();
277 WriteBitBuffer unused_wb(0);
278
279 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
280 }
281
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInconsistentCountLabelAndLocalizedElementAnnotations)282 TEST_F(
283 MixPresentationObuTest,
284 ValidateAndWriteFailsWithInconsistentCountLabelAndLocalizedElementAnnotations) {
285 count_label_ = 1;
286 sub_mixes_[0].audio_elements[0].localized_element_annotations.clear();
287
288 InitExpectOk();
289 WriteBitBuffer unused_wb(0);
290
291 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
292 }
293
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInvalidNonUniqueAudioElementIds)294 TEST_F(MixPresentationObuTest,
295 ValidateAndWriteFailsWithInvalidNonUniqueAudioElementIds) {
296 ASSERT_EQ(sub_mixes_.size(), 1);
297 ASSERT_EQ(sub_mixes_[0].audio_elements.size(), 1);
298 // Add an extra audio element to sub-mix.
299 sub_mixes_[0].audio_elements.push_back(sub_mixes_[0].audio_elements[0]);
300 dynamic_sub_mix_args_[0].element_mix_gain_subblocks = {{}, {}};
301
302 // It is forbidden to have duplicate audio element IDs within a mix
303 // presentation OBU.
304 ASSERT_EQ(sub_mixes_[0].audio_elements[0].audio_element_id,
305 sub_mixes_[0].audio_elements[1].audio_element_id);
306
307 InitExpectOk();
308 WriteBitBuffer unused_wb(0);
309 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
310 }
311
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInvalidWhenSubMixHasNoAudioElements)312 TEST_F(MixPresentationObuTest,
313 ValidateAndWriteFailsWithInvalidWhenSubMixHasNoAudioElements) {
314 ASSERT_EQ(sub_mixes_.size(), 1);
315 // Reconfigure the sub-mix to have no audio elements and no `element_mix`
316 // gains which are typically 1:1 with the audio elements.
317 sub_mixes_[0].audio_elements.clear();
318 dynamic_sub_mix_args_[0].element_mix_gain_subblocks.clear();
319
320 InitExpectOk();
321 WriteBitBuffer unused_wb(0);
322 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
323 }
324
TEST_F(MixPresentationObuTest,TwoAnchorElements)325 TEST_F(MixPresentationObuTest, TwoAnchorElements) {
326 sub_mixes_[0].layouts[0].loudness.info_type = LoudnessInfo::kAnchoredLoudness;
327 sub_mixes_[0].layouts[0].loudness.anchored_loudness = {
328 {{.anchor_element = AnchoredLoudnessElement::kAnchorElementAlbum,
329 .anchored_loudness = 20},
330 {.anchor_element = AnchoredLoudnessElement::kAnchorElementDialogue,
331 .anchored_loudness = 21}}};
332
333 expected_header_ = {kObuIaMixPresentation << 3, 52};
334 expected_payload_ = {
335 // Start Mix OBU.
336 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
337 // Start Submix 1.
338 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
339 // Start RenderingConfig.
340 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
341 // End RenderingConfig.
342 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
343 // Start Layout 1 (of Submix 1).
344 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
345 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
346 LoudnessInfo::kAnchoredLoudness, 0, 18, 0, 19,
347 // Start anchored loudness.
348 2, AnchoredLoudnessElement::kAnchorElementAlbum, 0, 20,
349 AnchoredLoudnessElement::kAnchorElementDialogue, 0, 21,
350 // End anchored loudness.
351 // End Layout 1 (of Submix 1).
352 // End Submix 1.
353 // End Mix OBU.
354 };
355
356 InitAndTestWrite();
357 }
358
TEST_F(MixPresentationObuTest,AnchoredAndTruePeak)359 TEST_F(MixPresentationObuTest, AnchoredAndTruePeak) {
360 sub_mixes_[0].layouts[0].loudness.info_type =
361 LoudnessInfo::kAnchoredLoudness | LoudnessInfo::kTruePeak;
362 sub_mixes_[0].layouts[0].loudness.true_peak = 22;
363 sub_mixes_[0].layouts[0].loudness.anchored_loudness = {
364 {{.anchor_element = AnchoredLoudnessElement::kAnchorElementAlbum,
365 .anchored_loudness = 20}}};
366
367 expected_header_ = {kObuIaMixPresentation << 3, 51};
368 expected_payload_ = {
369 // Start Mix OBU.
370 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
371 // Start Submix 1
372 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
373 // Start RenderingConfig.
374 RenderingConfig::RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
375 // End RenderingConfig
376 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
377 // Start Layout 1 (of Submix 1).
378 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
379 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
380 LoudnessInfo::kAnchoredLoudness | LoudnessInfo::kTruePeak, 0, 18, 0, 19,
381 // Start true peak.
382 0, 22,
383 // End true peak.
384 // Start anchored loudness.
385 1, AnchoredLoudnessElement::kAnchorElementAlbum, 0, 20,
386 // End anchored loudness.
387 // End Layout 1 (of Submix 1).
388 // End Submix 1.
389 // End Mix OBU.
390 };
391
392 InitAndTestWrite();
393 }
394
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInvalidNonUniqueAnchorElement)395 TEST_F(MixPresentationObuTest,
396 ValidateAndWriteFailsWithInvalidNonUniqueAnchorElement) {
397 sub_mixes_[0].layouts[0].loudness.info_type = LoudnessInfo::kAnchoredLoudness;
398 sub_mixes_[0].layouts[0].loudness.anchored_loudness = {
399 {{.anchor_element = AnchoredLoudnessElement::kAnchorElementAlbum,
400 .anchored_loudness = 20},
401 {.anchor_element = AnchoredLoudnessElement::kAnchorElementAlbum,
402 .anchored_loudness = 21}}};
403
404 InitExpectOk();
405 WriteBitBuffer unused_wb(0);
406 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
407 }
408
TEST_F(MixPresentationObuTest,ExtensionLayoutZero)409 TEST_F(MixPresentationObuTest, ExtensionLayoutZero) {
410 sub_mixes_[0].layouts[0].loudness.info_type = 0x04;
411 sub_mixes_[0].layouts[0].loudness.layout_extension = {.info_type_size = 0,
412 .info_type_bytes{}};
413
414 expected_header_ = {kObuIaMixPresentation << 3, 46};
415 expected_payload_ = {
416 // Start Mix OBU.
417 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
418 // Start Submix 1
419 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
420 // Start RenderingConfig.
421 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
422 // End RenderingConfig.
423 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
424 // Start Layout 1 (of Submix 1).
425 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
426 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
427 0x04, 0, 18, 0, 19, 0,
428 // End Mix OBU.
429 };
430
431 InitAndTestWrite();
432 }
433
TEST_F(MixPresentationObuTest,NonMinimalLebGeneratorAffectsAllLeb128s)434 TEST_F(MixPresentationObuTest, NonMinimalLebGeneratorAffectsAllLeb128s) {
435 // Initialize a test has several `DecodedUleb128` explicitly in the bitstream.
436 sub_mixes_[0].layouts[0].loudness.info_type = 0x04;
437 sub_mixes_[0].layouts[0].loudness.layout_extension = {.info_type_size = 0,
438 .info_type_bytes{}};
439
440 sub_mixes_[0].audio_elements[0].rendering_config = {
441 .headphones_rendering_mode =
442 RenderingConfig::kHeadphonesRenderingModeStereo,
443 .reserved = 0,
444 .rendering_config_extension_size = 2,
445 .rendering_config_extension_bytes = {'e', 'x'}};
446
447 leb_generator_ =
448 LebGenerator::Create(LebGenerator::GenerationMode::kFixedSize, 2);
449
450 expected_header_ = {kObuIaMixPresentation << 3,
451 // `obu_size` is affected by the `LebGenerator`.
452 0x80 | 60, 0x00};
453 expected_payload_ = {
454 // Start Mix OBU.
455 // `mix_presentation_id` is affected by the `LebGenerator`.
456 0x80 | 10, 0x00,
457 // `count_label` is affected by the `LebGenerator`.
458 0x80 | 1, 0x00,
459 // `language_label` and `mix_presentation_annotations`.
460 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0',
461 // `num_sub_mixes` is affected by the `LebGenerator`.
462 0x80 | 1, 0x00,
463 // Start Submix 1
464 // `num_audio_elements` is affected by the `LebGenerator`.
465 0x80 | 1, 0x00,
466 // `audio_element_id` is affected by the `LebGenerator`.
467 0x80 | 11, 0x00, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
468 // Start RenderingConfig.
469 RenderingConfig::kHeadphonesRenderingModeStereo << 6,
470 // `rendering_config_extension_size` is affected by the `LebGenerator`.
471 0x80 | 2, 0x00, 'e', 'x',
472 // End RenderingConfig.
473 // `element_mix_gain.parameter_id` is affected by the `LebGenerator`.
474 0x80 | 12, 0x00,
475 // `element_mix_gain.parameter_rate` is affected by the `LebGenerator`.
476 0x80 | 13, 0x00, 0x80, 0, 14,
477 // `output_mix_gain.parameter_id` is affected by the `LebGenerator`.
478 0x80 | 15, 0x00,
479 // `output_mix_gain.parameter_rate` is affected by the `LebGenerator`.
480 0x80 | 16, 0x00, 0x80, 0, 17,
481 // `num_layouts` is affected by the `LebGenerator`.
482 0x80 | 1, 0x00,
483 // Start Layout 1 (of Submix 1).
484 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
485 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
486 0x04, 0, 18, 0, 19,
487 // `info_type_size` is affected by the `LebGenerator`.
488 0x80 | 0, 0x00
489 // End Mix OBU.
490 };
491
492 InitAndTestWrite();
493 }
494
TEST_F(MixPresentationObuTest,ExtensionLayoutNonZero)495 TEST_F(MixPresentationObuTest, ExtensionLayoutNonZero) {
496 sub_mixes_[0].layouts[0].loudness.info_type = 0x04;
497 sub_mixes_[0].layouts[0].loudness.layout_extension = {
498 .info_type_size = 5, .info_type_bytes{'e', 'x', 't', 'r', 'a'}};
499
500 expected_header_ = {kObuIaMixPresentation << 3, 51};
501 expected_payload_ = {
502 // Start Mix OBU.
503 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
504 // Start Submix 1
505 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
506 // Start RenderingConfig.
507 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
508 // End RenderingConfig.
509 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
510 // Start Layout 1 (of Submix 1).
511 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
512 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
513 0x04, 0, 18, 0, 19, 5, 'e', 'x', 't', 'r', 'a',
514 // End Mix OBU.
515 };
516
517 InitAndTestWrite();
518 }
519
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithIllegalIamfStringOver128Bytes)520 TEST_F(MixPresentationObuTest,
521 ValidateAndWriteFailsWithIllegalIamfStringOver128Bytes) {
522 // Create a string that has no null terminator in the first 128 bytes.
523 const std::string kIllegalIamfString(kIamfMaxStringSize, 'a');
524 localized_presentation_annotations_[0] = kIllegalIamfString;
525
526 InitExpectOk();
527 WriteBitBuffer unused_wb(0);
528 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
529 }
530
TEST_F(MixPresentationObuTest,MultipleLabels)531 TEST_F(MixPresentationObuTest, MultipleLabels) {
532 count_label_ = 2;
533 annotations_language_ = {"en-us", "en-gb"};
534 localized_presentation_annotations_ = {"Mix 1", "Mix 1"};
535 sub_mixes_[0].audio_elements[0].localized_element_annotations = {
536 "Submix 1", "GB Submix 1"};
537
538 expected_header_ = {kObuIaMixPresentation << 3, 71};
539 expected_payload_ = {
540 // Start Mix OBU.
541 10, 2, 'e', 'n', '-', 'u', 's', '\0', 'e', 'n', '-', 'g', 'b', '\0', 'M',
542 'i', 'x', ' ', '1', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
543 // Start Submix 1
544 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0', 'G', 'B', ' ', 'S',
545 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
546 // Start RenderingConfig.
547 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
548 // End RenderingConfig
549 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
550 // Start Layout 1 (of Submix 1).
551 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
552 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
553 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
554 // End Mix OBU.
555 };
556
557 InitAndTestWrite();
558 }
559
TEST_F(MixPresentationObuTest,ValidateAndWriteSucceedsWhenAnnotationsLanguagesAreUnique)560 TEST_F(MixPresentationObuTest,
561 ValidateAndWriteSucceedsWhenAnnotationsLanguagesAreUnique) {
562 const std::vector<std::string> kAnnotationsLanaguesWithDifferentRegions = {
563 {"en-us"}, {"en-gb"}};
564 annotations_language_ = kAnnotationsLanaguesWithDifferentRegions;
565
566 count_label_ = 2;
567 localized_presentation_annotations_ = {"0", "1"};
568 sub_mixes_[0].audio_elements[0].localized_element_annotations = {"0", "1"};
569
570 InitExpectOk();
571 WriteBitBuffer unused_wb(0);
572 EXPECT_THAT(obu_->ValidateAndWriteObu(unused_wb), IsOk());
573 }
574
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWhenAnnotationsLanguagesAreNotUnique)575 TEST_F(MixPresentationObuTest,
576 ValidateAndWriteFailsWhenAnnotationsLanguagesAreNotUnique) {
577 const std::vector<std::string> kInvalidAnnotationsLanguagesWithDuplicate = {
578 {"en-us"}, {"en-us"}};
579 annotations_language_ = kInvalidAnnotationsLanguagesWithDuplicate;
580
581 // Configure plausible values for the related fields.
582 count_label_ = 2;
583 localized_presentation_annotations_ = {"0", "1"};
584 sub_mixes_[0].audio_elements[0].localized_element_annotations = {"0", "1"};
585
586 InitExpectOk();
587 WriteBitBuffer unused_wb(0);
588 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
589 }
590
TEST_F(MixPresentationObuTest,BinauralRenderingConfig)591 TEST_F(MixPresentationObuTest, BinauralRenderingConfig) {
592 sub_mixes_[0].audio_elements[0].rendering_config = {
593 .headphones_rendering_mode =
594 RenderingConfig::kHeadphonesRenderingModeStereo,
595 .reserved = 0,
596 .rendering_config_extension_size = 0,
597 .rendering_config_extension_bytes = {}};
598
599 expected_header_ = {kObuIaMixPresentation << 3, 47};
600 expected_payload_ = {
601 // Start Mix OBU.
602 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
603 // Start Submix 1.
604 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
605 // Start RenderingConfig.
606 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
607 // End RenderingConfig.
608 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
609 // Start Layout 1 (of Submix 1).
610 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
611 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
612 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
613 // End Mix OBU.
614 };
615
616 InitAndTestWrite();
617 }
618
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithOverflowBinauralRenderingConfigReservedOverSixBits)619 TEST_F(
620 MixPresentationObuTest,
621 ValidateAndWriteFailsWithOverflowBinauralRenderingConfigReservedOverSixBits) {
622 sub_mixes_[0].audio_elements[0].rendering_config = {
623 .headphones_rendering_mode =
624 RenderingConfig::kHeadphonesRenderingModeStereo,
625 .reserved = (1 << 6),
626 .rendering_config_extension_size = 0,
627 .rendering_config_extension_bytes = {}};
628
629 InitExpectOk();
630 WriteBitBuffer unused_wb(0);
631 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
632 }
633
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithOverflowSsLayoutReservedOverTwoBits)634 TEST_F(MixPresentationObuTest,
635 ValidateAndWriteFailsWithOverflowSsLayoutReservedOverTwoBits) {
636 std::get<LoudspeakersSsConventionLayout>(
637 sub_mixes_[0].layouts[0].loudness_layout.specific_layout)
638 .reserved = (1 << 2);
639
640 InitExpectOk();
641 WriteBitBuffer unused_wb(0);
642 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
643 }
644
TEST_F(MixPresentationObuTest,RenderingConfigExtension)645 TEST_F(MixPresentationObuTest, RenderingConfigExtension) {
646 sub_mixes_[0].audio_elements[0].rendering_config = {
647 .headphones_rendering_mode =
648 RenderingConfig::kHeadphonesRenderingModeStereo,
649 .reserved = 0,
650 .rendering_config_extension_size = 2,
651 .rendering_config_extension_bytes = {'e', 'x'}};
652
653 expected_header_ = {kObuIaMixPresentation << 3, 49};
654 expected_payload_ = {
655 // Start Mix OBU.
656 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
657 // Start Submix 1
658 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
659 // Start RenderingConfig.
660 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 2, 'e', 'x',
661 // End RenderingConfig.
662 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
663 // Start Layout 1 (of Submix 1).
664 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
665 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
666 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
667 // End Mix OBU.
668 };
669
670 InitAndTestWrite();
671 }
672
TEST_F(MixPresentationObuTest,MultipleSubmixesAndLayouts)673 TEST_F(MixPresentationObuTest, MultipleSubmixesAndLayouts) {
674 sub_mixes_.push_back(
675 {.audio_elements = {{
676 .audio_element_id = 21,
677 .localized_element_annotations = {"Submix 2"},
678 .rendering_config =
679 {.headphones_rendering_mode =
680 RenderingConfig::kHeadphonesRenderingModeBinaural,
681 .reserved = 0,
682 .rendering_config_extension_size = 0,
683 .rendering_config_extension_bytes = {}},
684 }},
685 .layouts = {
686 {.loudness_layout = {.layout_type = Layout::kLayoutTypeReserved0,
687 .specific_layout =
688 LoudspeakersReservedOrBinauralLayout{
689 .reserved = 0}},
690 .loudness = {.info_type = LoudnessInfo::kTruePeak,
691 .integrated_loudness = 28,
692 .digital_peak = 29,
693 .true_peak = 30}},
694
695 {.loudness_layout =
696 {.layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
697 .specific_layout =
698 LoudspeakersSsConventionLayout{
699 .sound_system = LoudspeakersSsConventionLayout::
700 kSoundSystemA_0_2_0,
701 .reserved = 0}},
702 .loudness = {.info_type = 0,
703 .integrated_loudness = 31,
704 .digital_peak = 32,
705 .true_peak = 0}},
706
707 {.loudness_layout = {.layout_type = Layout::kLayoutTypeBinaural,
708 .specific_layout =
709 LoudspeakersReservedOrBinauralLayout{
710 .reserved = 0}},
711 .loudness = {.info_type = LoudnessInfo::kTruePeak,
712 .integrated_loudness = 34,
713 .digital_peak = 35,
714 .true_peak = 36}},
715 }});
716 MixGainParamDefinition element_mix_gain;
717 element_mix_gain.parameter_id_ = 22;
718 element_mix_gain.parameter_rate_ = 23;
719 element_mix_gain.param_definition_mode_ = true;
720 element_mix_gain.reserved_ = 0;
721 element_mix_gain.default_mix_gain_ = 24;
722 sub_mixes_.back().audio_elements[0].element_mix_gain = element_mix_gain;
723
724 MixGainParamDefinition output_mix_gain;
725 output_mix_gain.parameter_id_ = 25;
726 output_mix_gain.parameter_rate_ = 26;
727 output_mix_gain.param_definition_mode_ = true;
728 output_mix_gain.reserved_ = 0;
729 output_mix_gain.default_mix_gain_ = 27;
730 sub_mixes_.back().output_mix_gain = output_mix_gain;
731
732 dynamic_sub_mix_args_.push_back(
733 {.element_mix_gain_subblocks = {{}}, .output_mix_gain_subblocks = {}});
734
735 expected_header_ = {kObuIaMixPresentation << 3, 93};
736 expected_payload_ = {
737 // Start Mix OBU.
738 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 2,
739 // Start Submix 1.
740 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
741 // Start RenderingConfig.
742 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
743 // End RenderingConfig.
744 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
745 // Start Layout 1 (of Submix 1).
746 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
747 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
748 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
749 // Start Submix 2.
750 1, 21, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '2', '\0',
751 // Start RenderingConfig.
752 RenderingConfig::kHeadphonesRenderingModeBinaural << 6, 0,
753 // End RenderingConfig.
754 22, 23, 0x80, 0, 24, 25, 26, 0x80, 0, 27, 3,
755 // Start Layout1 (Submix 2).
756 Layout::kLayoutTypeReserved0 << 6, LoudnessInfo::kTruePeak, 0, 28, 0, 29,
757 0, 30,
758 // Start Layout2 (Submix 2).
759 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
760 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
761 0, 0, 31, 0, 32,
762 // Start Layout3 (Submix 2).
763 (Layout::kLayoutTypeBinaural << 6), LoudnessInfo::kTruePeak, 0, 34, 0, 35,
764 0, 36
765 // End Mix OBU.
766 };
767
768 InitAndTestWrite();
769 }
770
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithInvalidMissingStero)771 TEST_F(MixPresentationObuTest, ValidateAndWriteFailsWithInvalidMissingStero) {
772 sub_mixes_[0].layouts[0].loudness_layout = {
773 .layout_type = Layout::kLayoutTypeBinaural,
774 .specific_layout = LoudspeakersReservedOrBinauralLayout{.reserved = 0}};
775
776 InitExpectOk();
777 WriteBitBuffer unused_wb(0);
778 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
779 }
780
TEST_F(MixPresentationObuTest,WritesMixPresentionTags)781 TEST_F(MixPresentationObuTest, WritesMixPresentionTags) {
782 expected_header_ = {kObuIaMixPresentation << 3, 58};
783 expected_payload_ = {
784 // Start Mix OBU.
785 10, 1, 'e', 'n', '-', 'u', 's', '\0', 'M', 'i', 'x', ' ', '1', '\0', 1,
786 // Start Submix 1
787 1, 11, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
788 // Start RenderingConfig.
789 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
790 // End RenderingConfig.
791 12, 13, 0x80, 0, 14, 15, 16, 0x80, 0, 17, 1,
792 // Start Layout 1 (of Submix 1).
793 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
794 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
795 LoudnessInfo::kTruePeak, 0, 18, 0, 19, 0, 20,
796 // Start Mix Presentation Tags.
797 // `num_tags`.
798 1,
799 // `tag_name[0]`.
800 't', 'a', 'g', '\0',
801 // `tag_value[0]`.
802 'v', 'a', 'l', 'u', 'e', '\0',
803 // End Mix OBU.
804 };
805 InitExpectOk();
806 obu_->mix_presentation_tags_ =
807 MixPresentationTags{.tags = {{"tag", "value"}}};
808
809 WriteBitBuffer wb(1024);
810 EXPECT_THAT(obu_->ValidateAndWriteObu(wb), IsOk());
811
812 ValidateObuWriteResults(wb, expected_header_, expected_payload_);
813 }
814
815 class GetNumChannelsFromLayoutTest : public testing::Test {
816 public:
GetNumChannelsFromLayoutTest()817 GetNumChannelsFromLayoutTest()
818 : layout_({.layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
819 .specific_layout = LoudspeakersSsConventionLayout{
820 .sound_system =
821 LoudspeakersSsConventionLayout::kSoundSystem12_0_1_0,
822 .reserved = 0}}) {}
823
824 protected:
825 Layout layout_;
826 };
827
TEST_F(GetNumChannelsFromLayoutTest,SoundSystemMono)828 TEST_F(GetNumChannelsFromLayoutTest, SoundSystemMono) {
829 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
830 .sound_system = LoudspeakersSsConventionLayout::kSoundSystem12_0_1_0;
831 const int32_t kExpectedMonoChannels = 1;
832
833 int32_t num_channels;
834 EXPECT_THAT(
835 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
836 IsOk());
837 EXPECT_EQ(num_channels, kExpectedMonoChannels);
838 }
839
TEST_F(GetNumChannelsFromLayoutTest,SoundSystemStereo)840 TEST_F(GetNumChannelsFromLayoutTest, SoundSystemStereo) {
841 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
842 .sound_system = LoudspeakersSsConventionLayout::
843 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0;
844 const int32_t kExpectedStereoChannels = 2;
845
846 int32_t num_channels;
847 EXPECT_THAT(
848 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
849 IsOk());
850 EXPECT_EQ(num_channels, kExpectedStereoChannels);
851 }
852
TEST_F(GetNumChannelsFromLayoutTest,SoundSystem5_1)853 TEST_F(GetNumChannelsFromLayoutTest, SoundSystem5_1) {
854 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
855 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemB_0_5_0;
856 const int32_t kExpected5_1Channels = 6;
857
858 int32_t num_channels;
859 EXPECT_THAT(
860 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
861 IsOk());
862 EXPECT_EQ(num_channels, kExpected5_1Channels);
863 }
864
TEST_F(GetNumChannelsFromLayoutTest,SoundSystem7_1_4)865 TEST_F(GetNumChannelsFromLayoutTest, SoundSystem7_1_4) {
866 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
867 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemJ_4_7_0;
868 const int32_t kExpected7_1_4Channels = 12;
869
870 int32_t num_channels;
871 EXPECT_THAT(
872 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
873 IsOk());
874 EXPECT_EQ(num_channels, kExpected7_1_4Channels);
875 }
876
TEST_F(GetNumChannelsFromLayoutTest,SoundSystem9_1_6)877 TEST_F(GetNumChannelsFromLayoutTest, SoundSystem9_1_6) {
878 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
879 .sound_system = LoudspeakersSsConventionLayout::kSoundSystem13_6_9_0;
880 const int32_t kExpected9_1_6Channels = 16;
881
882 int32_t num_channels;
883 EXPECT_THAT(
884 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
885 IsOk());
886 EXPECT_EQ(num_channels, kExpected9_1_6Channels);
887 }
888
TEST_F(GetNumChannelsFromLayoutTest,LayoutTypeBinaural)889 TEST_F(GetNumChannelsFromLayoutTest, LayoutTypeBinaural) {
890 layout_ = {
891 .layout_type = Layout::kLayoutTypeBinaural,
892 .specific_layout = LoudspeakersReservedOrBinauralLayout{.reserved = 0}};
893 const int32_t kExpectedBinauralChannels = 2;
894
895 int32_t num_channels;
896 EXPECT_THAT(
897 MixPresentationObu::GetNumChannelsFromLayout(layout_, num_channels),
898 IsOk());
899 EXPECT_EQ(num_channels, kExpectedBinauralChannels);
900 }
901
TEST_F(GetNumChannelsFromLayoutTest,UnsupportedReservedLayoutType)902 TEST_F(GetNumChannelsFromLayoutTest, UnsupportedReservedLayoutType) {
903 layout_ = {
904 .layout_type = Layout::kLayoutTypeReserved0,
905 .specific_layout = LoudspeakersReservedOrBinauralLayout{.reserved = 0}};
906
907 int32_t unused_num_channels;
908 EXPECT_FALSE(
909 MixPresentationObu::GetNumChannelsFromLayout(layout_, unused_num_channels)
910 .ok());
911 }
912
TEST_F(GetNumChannelsFromLayoutTest,UnsupportedReservedSoundSystem)913 TEST_F(GetNumChannelsFromLayoutTest, UnsupportedReservedSoundSystem) {
914 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
915 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemBeginReserved;
916
917 int32_t unused_num_channels;
918 EXPECT_FALSE(
919 MixPresentationObu::GetNumChannelsFromLayout(layout_, unused_num_channels)
920 .ok());
921 }
922
TEST_F(MixPresentationObuTest,ValidateAndWriteFailsWithErrorBeyondLayoutType)923 TEST_F(MixPresentationObuTest, ValidateAndWriteFailsWithErrorBeyondLayoutType) {
924 // `Layout::LayoutType` is 2-bit enum in IAMF. It is invalid for the value to
925 // be out of range.
926 const auto kBeyondLayoutType = static_cast<Layout::LayoutType>(4);
927 // Since a stereo layout must be present, add a new layout and configure
928 // `num_layouts` correctly.
929 ASSERT_FALSE(sub_mixes_.empty());
930 sub_mixes_[0].layouts.push_back(
931 MixPresentationLayout{Layout{.layout_type = kBeyondLayoutType}});
932
933 InitExpectOk();
934 WriteBitBuffer unused_wb(0);
935 EXPECT_FALSE(obu_->ValidateAndWriteObu(unused_wb).ok());
936 }
937
TEST_F(GetNumChannelsFromLayoutTest,ErrorBeyondReservedSoundSystem)938 TEST_F(GetNumChannelsFromLayoutTest, ErrorBeyondReservedSoundSystem) {
939 // `LoudspeakersSsConventionLayout::SoundSystem` is a 4-bit enum in the spec.
940 // It is invalid for the value to be out of this range.
941 const auto kBeyondSoundSystemReserved =
942 static_cast<LoudspeakersSsConventionLayout::SoundSystem>(16);
943 std::get<LoudspeakersSsConventionLayout>(layout_.specific_layout)
944 .sound_system = kBeyondSoundSystemReserved;
945
946 int32_t unused_num_channels;
947 EXPECT_FALSE(
948 MixPresentationObu::GetNumChannelsFromLayout(layout_, unused_num_channels)
949 .ok());
950 }
951
952 // --- Begin CreateFromBuffer tests ---
TEST(CreateFromBufferTest,RejectEmptyBitstream)953 TEST(CreateFromBufferTest, RejectEmptyBitstream) {
954 std::vector<uint8_t> source;
955 const int64_t payload_size = source.size();
956 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
957 1024, absl::MakeConstSpan(source));
958 ObuHeader header;
959 EXPECT_FALSE(
960 MixPresentationObu::CreateFromBuffer(header, payload_size, *buffer).ok());
961 }
962
TEST(CreateFromBuffer,InvalidWithNoSubMixes)963 TEST(CreateFromBuffer, InvalidWithNoSubMixes) {
964 std::vector<uint8_t> source = {
965 // Start Mix OBU.
966 // mix_presentation_id
967 10,
968 // count_label
969 1,
970 // annotations_language[0]
971 'e', 'n', '-', 'u', 's', '\0',
972 // localized_presentation_annotations[0]
973 'M', 'i', 'x', ' ', '1', '\0',
974 // num_sub_mixes
975 0,
976 // End Mix OBU.
977 };
978 const int64_t payload_size = source.size();
979 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
980 1024, absl::MakeConstSpan(source));
981 ObuHeader header;
982 EXPECT_FALSE(
983 MixPresentationObu::CreateFromBuffer(header, payload_size, *buffer).ok());
984 }
985
TEST(CreateFromBuffer,ReadsOneSubMix)986 TEST(CreateFromBuffer, ReadsOneSubMix) {
987 const std::vector<std::string> kAnnotationsLanguage = {"en-us"};
988 const std::vector<std::string> kLocalizedPresentationAnnotations = {"Mix 1"};
989 const std::vector<std::string> kAudioElementLocalizedElementAnnotations = {
990 "Submix 1"};
991
992 std::vector<uint8_t> source = {
993 // Start Mix OBU.
994 // mix_presentation_id
995 10,
996 // count_label
997 1,
998 // annotations_language[0]
999 'e', 'n', '-', 'u', 's', '\0',
1000 // localized_presentation_annotations[0]
1001 'M', 'i', 'x', ' ', '1', '\0',
1002 // num_sub_mixes
1003 1,
1004 // Start Submix.
1005 1, 21,
1006 // localized_element_annotations[0]
1007 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
1008 // Start RenderingConfig.
1009 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
1010 // End RenderingConfig.
1011 22, 23, 0x80, 0, 24, 25, 26, 0x80, 0, 27,
1012 // num_layouts
1013 2,
1014 // Start Layout1.
1015 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1016 (LoudspeakersSsConventionLayout::kSoundSystemB_0_5_0 << 2),
1017 0, 0, 31, 0, 32,
1018 // Start Layout2.
1019 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1020 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
1021 0, 0, 31, 0, 32,
1022 // End SubMix.
1023 // End Mix OBU.
1024 };
1025 const int64_t payload_size = source.size();
1026 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1027 1024, absl::MakeConstSpan(source));
1028 ObuHeader header;
1029 auto obu =
1030 MixPresentationObu::CreateFromBuffer(header, payload_size, *buffer);
1031 ASSERT_THAT(obu, IsOk());
1032 EXPECT_EQ(obu->header_.obu_type, kObuIaMixPresentation);
1033 EXPECT_EQ(obu->GetMixPresentationId(), 10);
1034 EXPECT_EQ(obu->GetAnnotationsLanguage(), kAnnotationsLanguage);
1035 EXPECT_EQ(obu->GetLocalizedPresentationAnnotations(),
1036 kLocalizedPresentationAnnotations);
1037 EXPECT_EQ(obu->GetNumSubMixes(), 1);
1038 ASSERT_FALSE(obu->sub_mixes_[0].audio_elements.empty());
1039 EXPECT_EQ(obu->sub_mixes_[0].audio_elements[0].localized_element_annotations,
1040 kAudioElementLocalizedElementAnnotations);
1041 }
1042
TEST(CreateFromBufferTest,ReadsMixPresentationTagsIntoFooter)1043 TEST(CreateFromBufferTest, ReadsMixPresentationTagsIntoFooter) {
1044 const std::vector<uint8_t> kMixPresentationTags = {
1045 // Start MixPresentationTags.
1046 1,
1047 // Start Tag1.
1048 'A', 'B', 'C', '\0', '1', '2', '3', '\0',
1049 // End Tag1.
1050 };
1051 std::vector<uint8_t> source = {
1052 // Start Mix OBU.
1053 // mix_presentation_id
1054 10,
1055 // count_label
1056 0,
1057 // num_sub_mixes
1058 1,
1059 // Start Submix.
1060 1, 21,
1061 // Start RenderingConfig.
1062 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
1063 // End RenderingConfig.
1064 22, 23, 0x80, 0, 24, 25, 26, 0x80, 0, 27,
1065 // num_layouts
1066 1,
1067 // Start Layout0.
1068 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1069 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
1070 0, 0, 31, 0, 32,
1071 // End SubMix.
1072 };
1073 source.insert(source.end(), kMixPresentationTags.begin(),
1074 kMixPresentationTags.end());
1075 const int64_t payload_size = source.size();
1076 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1077 1024, absl::MakeConstSpan(source));
1078 ObuHeader header;
1079 auto obu =
1080 MixPresentationObu::CreateFromBuffer(header, payload_size, *buffer);
1081 ASSERT_THAT(obu, IsOk());
1082
1083 EXPECT_FALSE(obu->mix_presentation_tags_.has_value());
1084 EXPECT_EQ(obu->footer_, kMixPresentationTags);
1085 }
1086
TEST(CreateFromBufferTest,SucceedsWithDuplicateContentLanguageTags)1087 TEST(CreateFromBufferTest, SucceedsWithDuplicateContentLanguageTags) {
1088 const std::vector<uint8_t> kDuplicateContentLanguageTags = {
1089 // Start MixPresentationTags.
1090 2,
1091 // `tag_name[0]`.
1092 'c', 'o', 'n', 't', 'e', 'n', 't', '_', 'l', 'a', 'n', 'g', 'u', 'a', 'g',
1093 'e', '\0',
1094 // `tag_value[0]`.
1095 'e', 'n', '-', 'u', 's', '\0',
1096 // `tag_name[1]`.
1097 'c', 'o', 'n', 't', 'e', 'n', 't', '_', 'l', 'a', 'n', 'g', 'u', 'a', 'g',
1098 'e', '\0',
1099 // `tag_value[1]`.
1100 'e', 'n', '-', 'g', 'b', '\0'};
1101 std::vector<uint8_t> source = {
1102 // Start Mix OBU.
1103 // mix_presentation_id
1104 10,
1105 // count_label
1106 0,
1107 // num_sub_mixes
1108 1,
1109 // Start Submix.
1110 1, 21,
1111 // Start RenderingConfig.
1112 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
1113 // End RenderingConfig.
1114 22, 23, 0x80, 0, 24, 25, 26, 0x80, 0, 27,
1115 // num_layouts
1116 1,
1117 // Start Layout0.
1118 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1119 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
1120 0, 0, 31, 0, 32,
1121 // End SubMix.
1122 };
1123 source.insert(source.end(), kDuplicateContentLanguageTags.begin(),
1124 kDuplicateContentLanguageTags.end());
1125 const int64_t payload_size = source.size();
1126 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1127 1024, absl::MakeConstSpan(source));
1128 ObuHeader header;
1129 auto obu =
1130 MixPresentationObu::CreateFromBuffer(header, payload_size, *buffer);
1131 ASSERT_THAT(obu, IsOk());
1132
1133 EXPECT_FALSE(obu->mix_presentation_tags_.has_value());
1134 EXPECT_EQ(obu->footer_, kDuplicateContentLanguageTags);
1135 }
1136
TEST(ReadSubMixAudioElementTest,AllFieldsPresent)1137 TEST(ReadSubMixAudioElementTest, AllFieldsPresent) {
1138 std::vector<uint8_t> source = {
1139 // Start SubMixAudioElement.
1140 // audio_element_id
1141 11,
1142 // localized_element_annotations[0]
1143 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
1144 // Start RenderingConfig.
1145 RenderingConfig::kHeadphonesRenderingModeBinaural << 6, 0,
1146 // End RenderingConfig.
1147 // Start ElementMixGain
1148 // Parameter ID.
1149 0x00,
1150 // Parameter Rate.
1151 1,
1152 // Param Definition Mode (upper bit), next 7 bits reserved.
1153 0x80,
1154 // Default Mix Gain.
1155 0, 4
1156 // End ElementMixGain
1157 };
1158 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1159 1024, absl::MakeConstSpan(source));
1160 SubMixAudioElement audio_element;
1161 EXPECT_THAT(audio_element.ReadAndValidate(/*count_label=*/1, *buffer),
1162 IsOk());
1163
1164 // Set up expected values.
1165 SubMixAudioElement expected_submix_audio_element = SubMixAudioElement{
1166 .audio_element_id = 11,
1167 .localized_element_annotations = {"Submix 1"},
1168 .rendering_config =
1169 {.headphones_rendering_mode =
1170 RenderingConfig::kHeadphonesRenderingModeBinaural},
1171 .element_mix_gain = MixGainParamDefinition()};
1172 expected_submix_audio_element.element_mix_gain.parameter_id_ = 0;
1173 expected_submix_audio_element.element_mix_gain.parameter_rate_ = 1;
1174 expected_submix_audio_element.element_mix_gain.param_definition_mode_ = true;
1175 expected_submix_audio_element.element_mix_gain.reserved_ = 0;
1176 expected_submix_audio_element.element_mix_gain.default_mix_gain_ = 4;
1177 EXPECT_EQ(audio_element, expected_submix_audio_element);
1178 }
1179
1180 // TODO(b/339855295): Add more tests.
TEST(ReadMixPresentationLayoutTest,LoudSpeakerWithAnchoredLoudness)1181 TEST(ReadMixPresentationLayoutTest, LoudSpeakerWithAnchoredLoudness) {
1182 std::vector<uint8_t> source = {
1183 // Start Layout.
1184 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1185 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
1186 LoudnessInfo::kAnchoredLoudness, 0, 18, 0, 19,
1187 // Start anchored loudness.
1188 2, AnchoredLoudnessElement::kAnchorElementAlbum, 0, 20,
1189 AnchoredLoudnessElement::kAnchorElementDialogue, 0, 21,
1190 // End anchored loudness.
1191 // End Layout.
1192 };
1193 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1194 1024, absl::MakeConstSpan(source));
1195 MixPresentationLayout layout;
1196 EXPECT_THAT(layout.ReadAndValidate(*buffer), IsOk());
1197 EXPECT_EQ(layout.loudness_layout.layout_type,
1198 Layout::kLayoutTypeLoudspeakersSsConvention);
1199 EXPECT_EQ(std::get<LoudspeakersSsConventionLayout>(
1200 layout.loudness_layout.specific_layout),
1201 LoudspeakersSsConventionLayout(
1202 {.sound_system =
1203 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0}));
1204 EXPECT_EQ(layout.loudness.info_type, LoudnessInfo::kAnchoredLoudness);
1205 EXPECT_EQ(layout.loudness.anchored_loudness.anchor_elements.size(), 2);
1206 EXPECT_EQ(layout.loudness.anchored_loudness.anchor_elements[0].anchor_element,
1207 AnchoredLoudnessElement::kAnchorElementAlbum);
1208 EXPECT_EQ(
1209 layout.loudness.anchored_loudness.anchor_elements[0].anchored_loudness,
1210 20);
1211 EXPECT_EQ(layout.loudness.anchored_loudness.anchor_elements[1].anchor_element,
1212 AnchoredLoudnessElement::kAnchorElementDialogue);
1213 EXPECT_EQ(
1214 layout.loudness.anchored_loudness.anchor_elements[1].anchored_loudness,
1215 21);
1216 }
1217
TEST(LoudspeakersSsConventionLayoutRead,ReadsSsConventionLayout)1218 TEST(LoudspeakersSsConventionLayoutRead, ReadsSsConventionLayout) {
1219 // SS Convention layout is only 6-bits. Ensure the data to be read is in the
1220 // upper 6-bits of the buffer.
1221 const int kSsConventionBitShift = 2;
1222 constexpr auto kSoundSystem =
1223 LoudspeakersSsConventionLayout::kSoundSystem12_0_1_0;
1224 constexpr uint8_t kArbitraryTwoBitReservedField = 3;
1225 std::vector<uint8_t> source = {
1226 (kSoundSystem << kSoundSystemBitShift | kArbitraryTwoBitReservedField)
1227 << kSsConventionBitShift};
1228 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1229 1024, absl::MakeConstSpan(source));
1230 LoudspeakersSsConventionLayout ss_convention_layout;
1231
1232 EXPECT_THAT(ss_convention_layout.Read(*buffer), IsOk());
1233
1234 EXPECT_EQ(ss_convention_layout.sound_system, kSoundSystem);
1235 EXPECT_EQ(ss_convention_layout.reserved, kArbitraryTwoBitReservedField);
1236 }
1237
TEST(LoudspeakersReservedOrBinauralLayoutRead,ReadsReservedField)1238 TEST(LoudspeakersReservedOrBinauralLayoutRead, ReadsReservedField) {
1239 // Binaural layout is only 6-bits. Ensure the data to be read is in the
1240 // upper 6-bits of the buffer.
1241 const int kBinauralLayoutBitShift = 2;
1242 constexpr uint8_t kArbitrarySixBitReservedField = 63;
1243 std::vector<uint8_t> source = {kArbitrarySixBitReservedField
1244 << kBinauralLayoutBitShift};
1245 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1246 1024, absl::MakeConstSpan(source));
1247 LoudspeakersReservedOrBinauralLayout reserved_binaural_layout;
1248
1249 EXPECT_THAT(reserved_binaural_layout.Read(*buffer), IsOk());
1250
1251 EXPECT_EQ(reserved_binaural_layout.reserved, kArbitrarySixBitReservedField);
1252 }
1253
TEST(LayoutReadAndValidate,ReadsLoudspeakersSsConventionLayout)1254 TEST(LayoutReadAndValidate, ReadsLoudspeakersSsConventionLayout) {
1255 constexpr auto kSoundSystem =
1256 LoudspeakersSsConventionLayout::kSoundSystem12_0_1_0;
1257 constexpr uint8_t kArbitraryTwoBitReservedField = 3;
1258 std::vector<uint8_t> source = {
1259 (Layout::kLayoutTypeLoudspeakersSsConvention << kLayoutTypeBitShift) |
1260 (kSoundSystem << kSoundSystemBitShift | kArbitraryTwoBitReservedField)};
1261 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1262 1024, absl::MakeConstSpan(source));
1263 Layout loudness_layout;
1264
1265 EXPECT_THAT(loudness_layout.ReadAndValidate(*buffer), IsOk());
1266
1267 EXPECT_EQ(loudness_layout.layout_type,
1268 Layout::kLayoutTypeLoudspeakersSsConvention);
1269 ASSERT_TRUE(std::holds_alternative<LoudspeakersSsConventionLayout>(
1270 loudness_layout.specific_layout));
1271 const auto& ss_convention_layout =
1272 std::get<LoudspeakersSsConventionLayout>(loudness_layout.specific_layout);
1273 EXPECT_EQ(ss_convention_layout.sound_system, kSoundSystem);
1274 EXPECT_EQ(ss_convention_layout.reserved, kArbitraryTwoBitReservedField);
1275 }
1276
TEST(LayoutReadAndValidate,ReadsReservedLayout)1277 TEST(LayoutReadAndValidate, ReadsReservedLayout) {
1278 const auto kReservedLayout = Layout::kLayoutTypeReserved0;
1279 constexpr uint8_t kArbitrarySixBitReservedField = 63;
1280 std::vector<uint8_t> source = {(kReservedLayout << kLayoutTypeBitShift) |
1281 (kArbitrarySixBitReservedField)};
1282 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1283 1024, absl::MakeConstSpan(source));
1284 Layout loudness_layout;
1285
1286 EXPECT_THAT(loudness_layout.ReadAndValidate(*buffer), IsOk());
1287
1288 EXPECT_EQ(loudness_layout.layout_type, kReservedLayout);
1289 ASSERT_TRUE(std::holds_alternative<LoudspeakersReservedOrBinauralLayout>(
1290 loudness_layout.specific_layout));
1291 EXPECT_EQ(std::get<LoudspeakersReservedOrBinauralLayout>(
1292 loudness_layout.specific_layout)
1293 .reserved,
1294 kArbitrarySixBitReservedField);
1295 }
1296
TEST(LayoutReadAndValidate,ReadsBinauralLayout)1297 TEST(LayoutReadAndValidate, ReadsBinauralLayout) {
1298 const auto kBinauralLayout = Layout::kLayoutTypeBinaural;
1299 constexpr uint8_t kArbitrarySixBitReservedField = 33;
1300 std::vector<uint8_t> source = {(kBinauralLayout << kLayoutTypeBitShift) |
1301 (kArbitrarySixBitReservedField)};
1302 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1303 1024, absl::MakeConstSpan(source));
1304 Layout loudness_layout;
1305
1306 EXPECT_THAT(loudness_layout.ReadAndValidate(*buffer), IsOk());
1307
1308 EXPECT_EQ(loudness_layout.layout_type, kBinauralLayout);
1309 ASSERT_TRUE(std::holds_alternative<LoudspeakersReservedOrBinauralLayout>(
1310 loudness_layout.specific_layout));
1311 EXPECT_EQ(std::get<LoudspeakersReservedOrBinauralLayout>(
1312 loudness_layout.specific_layout)
1313 .reserved,
1314 kArbitrarySixBitReservedField);
1315 }
1316
TEST(ReadMixPresentationSubMixTest,AudioElementAndMultipleLayouts)1317 TEST(ReadMixPresentationSubMixTest, AudioElementAndMultipleLayouts) {
1318 std::vector<uint8_t> source = {
1319 // Start Submix.
1320 1, 21, 'S', 'u', 'b', 'm', 'i', 'x', ' ', '1', '\0',
1321 // Start RenderingConfig.
1322 RenderingConfig::kHeadphonesRenderingModeStereo << 6, 0,
1323 // End RenderingConfig.
1324 22, 23, 0x80, 0, 24, 25, 26, 0x80, 0, 27,
1325 // num_layouts
1326 2,
1327 // Start Layout1.
1328 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1329 (LoudspeakersSsConventionLayout::kSoundSystemB_0_5_0 << 2),
1330 0, 0, 31, 0, 32,
1331 // Start Layout2.
1332 (Layout::kLayoutTypeLoudspeakersSsConvention << 6) |
1333 (LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0 << 2),
1334 0, 0, 31, 0, 32,
1335 // End SubMix.
1336 };
1337 auto buffer = MemoryBasedReadBitBuffer::CreateFromSpan(
1338 1024, absl::MakeConstSpan(source));
1339 MixPresentationSubMix sub_mix;
1340 EXPECT_THAT(sub_mix.ReadAndValidate(/*count_label=*/1, *buffer), IsOk());
1341 EXPECT_EQ(sub_mix.audio_elements.size(), 1);
1342 EXPECT_EQ(sub_mix.layouts[0].loudness_layout.layout_type,
1343 Layout::kLayoutTypeLoudspeakersSsConvention);
1344 EXPECT_EQ(std::get<LoudspeakersSsConventionLayout>(
1345 sub_mix.layouts[0].loudness_layout.specific_layout),
1346 LoudspeakersSsConventionLayout(
1347 {.sound_system =
1348 LoudspeakersSsConventionLayout::kSoundSystemB_0_5_0}));
1349 EXPECT_EQ(sub_mix.layouts[1].loudness_layout.layout_type,
1350 Layout::kLayoutTypeLoudspeakersSsConvention);
1351 EXPECT_EQ(std::get<LoudspeakersSsConventionLayout>(
1352 sub_mix.layouts[1].loudness_layout.specific_layout),
1353 LoudspeakersSsConventionLayout(
1354 {.sound_system =
1355 LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0}));
1356 }
1357
TEST(MixPresentationTagsWriteAndValidate,WritesWithZeroTags)1358 TEST(MixPresentationTagsWriteAndValidate, WritesWithZeroTags) {
1359 constexpr uint8_t kZeroNumTags = 0;
1360 const MixPresentationTags kMixPresentationTagsWithZeroTags = {.tags = {}};
1361 const std::vector<uint8_t> kExpectedBuffer = {
1362 // `num_tags`.
1363 kZeroNumTags,
1364 };
1365 WriteBitBuffer wb(1024);
1366
1367 EXPECT_THAT(kMixPresentationTagsWithZeroTags.ValidateAndWrite(wb), IsOk());
1368
1369 EXPECT_EQ(wb.bit_buffer(), kExpectedBuffer);
1370 }
1371
TEST(MixPresentationTagsWriteAndValidate,WritesContentLanguageTag)1372 TEST(MixPresentationTagsWriteAndValidate, WritesContentLanguageTag) {
1373 constexpr uint8_t kOneTag = 1;
1374 const MixPresentationTags kMixPresentationTagsWithContentLanguageTag = {
1375 .tags = {{"content_language", "eng"}}};
1376 const std::vector<uint8_t> kExpectedBuffer = {
1377 // `num_tags`.
1378 kOneTag,
1379 // `tag_name[0]`.
1380 'c', 'o', 'n', 't', 'e', 'n', 't', '_', 'l', 'a', 'n', 'g', 'u', 'a', 'g',
1381 'e', '\0',
1382 // `tag_value[0]`.
1383 'e', 'n', 'g', '\0'};
1384 WriteBitBuffer wb(1024);
1385
1386 EXPECT_THAT(kMixPresentationTagsWithContentLanguageTag.ValidateAndWrite(wb),
1387 IsOk());
1388
1389 EXPECT_EQ(wb.bit_buffer(), kExpectedBuffer);
1390 }
1391
TEST(MixPresentationTagsWriteAndValidate,InvalidWhenContentLanguageTagNotThreeCharacters)1392 TEST(MixPresentationTagsWriteAndValidate,
1393 InvalidWhenContentLanguageTagNotThreeCharacters) {
1394 const MixPresentationTags kMixPresentationTagsWithContentLanguageTag = {
1395 .tags = {{"content_language", "en-us"}}};
1396
1397 WriteBitBuffer wb(0);
1398
1399 EXPECT_FALSE(
1400 kMixPresentationTagsWithContentLanguageTag.ValidateAndWrite(wb).ok());
1401 }
1402
TEST(MixPresentationTagsWriteAndValidate,WritesArbitraryTags)1403 TEST(MixPresentationTagsWriteAndValidate, WritesArbitraryTags) {
1404 constexpr uint8_t kNumTags = 1;
1405 const MixPresentationTags kMixPresentationTagsWithArbitraryTag = {
1406 .tags = {{"ABC", "123"}}};
1407 const std::vector<uint8_t> kExpectedBuffer = {// `num_tags`.
1408 kNumTags,
1409 // `tag_name[0]`.
1410 'A', 'B', 'C', '\0',
1411 // `tag_value[1]`.
1412 '1', '2', '3', '\0'};
1413 WriteBitBuffer wb(1024);
1414
1415 EXPECT_THAT(kMixPresentationTagsWithArbitraryTag.ValidateAndWrite(wb),
1416 IsOk());
1417
1418 EXPECT_EQ(wb.bit_buffer(), kExpectedBuffer);
1419 }
1420
TEST(MixPresentationTagsWriteAndValidate,WritesDuplicateArbitraryTags)1421 TEST(MixPresentationTagsWriteAndValidate, WritesDuplicateArbitraryTags) {
1422 constexpr uint8_t kTwoTags = 2;
1423 const MixPresentationTags kMixPresentationTagsWithArbitraryTag = {
1424 .tags = {{"tag", "value"}, {"tag", "value"}}};
1425 const std::vector<uint8_t> kExpectedBuffer = {// `num_tags`.
1426 kTwoTags,
1427 // `tag_name[0]`.
1428 't', 'a', 'g', '\0',
1429 // `tag_value[0]`.
1430 'v', 'a', 'l', 'u', 'e', '\0',
1431 // `tag_name[1]`.
1432 't', 'a', 'g', '\0',
1433 // `tag_value[1]`.
1434 'v', 'a', 'l', 'u', 'e', '\0'};
1435 WriteBitBuffer wb(1024);
1436
1437 EXPECT_THAT(kMixPresentationTagsWithArbitraryTag.ValidateAndWrite(wb),
1438 IsOk());
1439
1440 EXPECT_EQ(wb.bit_buffer(), kExpectedBuffer);
1441 }
1442
TEST(MixPresentationTagsWriteAndValidate,InvalidForDuplicateContentIdTag)1443 TEST(MixPresentationTagsWriteAndValidate, InvalidForDuplicateContentIdTag) {
1444 const MixPresentationTags
1445 kMixPresentationTagsWithDuplicateContentLanguageTag = {
1446 .tags = {{"content_language", "eng"}, {"content_language", "kor"}}};
1447
1448 WriteBitBuffer wb(1024);
1449
1450 EXPECT_FALSE(
1451 kMixPresentationTagsWithDuplicateContentLanguageTag.ValidateAndWrite(wb)
1452 .ok());
1453 }
1454
1455 } // namespace
1456 } // namespace iamf_tools
1457