• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 #include "iamf/obu/mix_presentation.h"
13 
14 #include <cstdint>
15 #include <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