• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024, 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 
13 #include "iamf/cli/adm_to_user_metadata/iamf/iamf.h"
14 
15 #include <cstdint>
16 #include <map>
17 #include <string>
18 #include <vector>
19 
20 #include "absl/base/no_destructor.h"
21 #include "absl/status/status_matchers.h"
22 #include "absl/strings/string_view.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "iamf/cli/adm_to_user_metadata/adm/adm_elements.h"
26 #include "iamf/cli/user_metadata_builder/iamf_input_layout.h"
27 
28 namespace iamf_tools {
29 namespace adm_to_user_metadata {
30 namespace {
31 
32 using ::absl_testing::IsOk;
33 
34 constexpr int32_t kFrameDurationMs = 10;
35 constexpr uint32_t kSamplesPerSec = 48000;
36 
37 constexpr absl::string_view kStereoAudioPackFormatId = "AP_00010002";
38 constexpr absl::string_view kStereoAudioObjectId = "Stereo Audio Object";
39 constexpr absl::string_view kThirdOrderAmbisonicsAudioObjectId =
40     "TOA Audio Object";
41 
GetStereoAudioObject(absl::string_view id=kStereoAudioObjectId)42 AudioObject GetStereoAudioObject(absl::string_view id = kStereoAudioObjectId) {
43   return AudioObject(
44       {.id = std::string(id),
45        .audio_pack_format_id_refs = {std::string(kStereoAudioPackFormatId)}});
46 }
47 
GetThirdOrderAmbisonicsAudioObject()48 const AudioObject& GetThirdOrderAmbisonicsAudioObject() {
49   constexpr absl::string_view kThirdOrderAmbisonicsAudioPackFormatId =
50       "AP_00040003";
51 
52   static const absl::NoDestructor<AudioObject> kThirdOrderAmbisonicsAudioObject(
53       {.id = std::string(kThirdOrderAmbisonicsAudioObjectId),
54        .audio_pack_format_id_refs = {
55            std::string(kThirdOrderAmbisonicsAudioPackFormatId)}});
56   return *kThirdOrderAmbisonicsAudioObject;
57 }
58 
GetObjectBasedAudioObject()59 const AudioObject& GetObjectBasedAudioObject() {
60   constexpr absl::string_view kObjectBasedAudioObjectId =
61       "Object Based Audio Object";
62   constexpr absl::string_view kObjectBasedAudioPackFormatId = "AP_00030001";
63 
64   static const absl::NoDestructor<AudioObject> kStereoAudioObject(
65       {.id = std::string(kObjectBasedAudioObjectId),
66        .audio_pack_format_id_refs = {
67            std::string(kObjectBasedAudioPackFormatId)}});
68   return *kStereoAudioObject;
69 }
70 
GetAdmWithStereoAndToaObjectsWithoutAudioProgramme()71 const ADM& GetAdmWithStereoAndToaObjectsWithoutAudioProgramme() {
72   static const absl::NoDestructor<ADM> kAdmWithStereoAndToaObjects(
73       {.audio_objects = {GetStereoAudioObject(),
74                          GetThirdOrderAmbisonicsAudioObject()}});
75   return *kAdmWithStereoAndToaObjects;
76 }
77 
GetAdmWithStereoAndToaObjectsAndTwoAudioProgrammes()78 const ADM& GetAdmWithStereoAndToaObjectsAndTwoAudioProgrammes() {
79   static const absl::NoDestructor<ADM> kAdmWithStereoAndToaObjects(
80       {.audio_programmes =
81            {{
82                 .id = "ProgrammeIdWithTwoAudioObjects",
83                 .audio_content_id_refs = {"AudioContentIdWithTwoObjects"},
84             },
85             {
86                 .id = "ProgrammeIdWithOneAudioObject",
87                 .audio_content_id_refs = {"AudioContentIdWithOneObject"},
88             }},
89        .audio_contents = {{
90                               .id = "AudioContentIdWithTwoObjects",
91                               .audio_object_id_ref =
92                                   {std::string(kStereoAudioObjectId),
93                                    std::string(
94                                        kThirdOrderAmbisonicsAudioObjectId)},
95                           },
96                           {
97                               .id = "AudioContentIdWithOneObject",
98                               .audio_object_id_ref = {std::string(
99                                   kThirdOrderAmbisonicsAudioObjectId)},
100                           }},
101        .audio_objects = {GetStereoAudioObject(),
102                          GetThirdOrderAmbisonicsAudioObject()}});
103   return *kAdmWithStereoAndToaObjects;
104 }
105 
106 const absl::string_view kStereoWithComplementaryGroupId =
107     "Stereo Object with Complementary Group";
GetAdmWithComplementaryGroups()108 const ADM& GetAdmWithComplementaryGroups() {
109   static const absl::NoDestructor<ADM> kAdmWithComplementaryGroups(
110       {.audio_programmes = {{
111            .id = "AudioProgramWithStereoWithComplementaryGroup",
112            .audio_content_id_refs = {"AudioContentIdWithComplemntaryGroup"},
113        }},
114        .audio_contents = {{
115            .id = "AudioContentIdWithComplemntaryGroup",
116            .audio_object_id_ref = {std::string(
117                kStereoWithComplementaryGroupId)},
118        }},
119        .audio_objects = {{.id = std::string(kStereoWithComplementaryGroupId),
120                           .audio_pack_format_id_refs =
121                               {
122                                   std::string(kStereoAudioPackFormatId),
123                               },
124                           .audio_comple_object_id_ref = {std::string(
125                               kThirdOrderAmbisonicsAudioObjectId)}},
126                          GetThirdOrderAmbisonicsAudioObject()}});
127   return *kAdmWithComplementaryGroups;
128 }
129 
TEST(Create,WithNoAudioObjectsSucceeds)130 TEST(Create, WithNoAudioObjectsSucceeds) {
131   EXPECT_THAT(IAMF::Create(
132                   /*adm=*/{}, kFrameDurationMs, kSamplesPerSec),
133               IsOk());
134 }
135 
TEST(Create,WithObjectBasedAudioObjectFails)136 TEST(Create, WithObjectBasedAudioObjectFails) {
137   EXPECT_FALSE(IAMF::Create(
138                    /*adm=*/{.audio_objects = {GetObjectBasedAudioObject()}},
139                    kFrameDurationMs, kSamplesPerSec)
140                    .ok());
141 }
142 
TEST(Create,PopulatesIamfInputLayoutsFromObjects)143 TEST(Create, PopulatesIamfInputLayoutsFromObjects) {
144   const auto iamf =
145       IAMF::Create(GetAdmWithStereoAndToaObjectsWithoutAudioProgramme(),
146                    kFrameDurationMs, kSamplesPerSec);
147   ASSERT_THAT(iamf, IsOk());
148 
149   const std::vector<IamfInputLayout> kExpectedInputLayouts = {
150       IamfInputLayout::kStereo, IamfInputLayout::kAmbisonicsOrder3};
151 
152   EXPECT_EQ(iamf->input_layouts_, kExpectedInputLayouts);
153 
154   EXPECT_TRUE(iamf->audio_object_to_audio_element_.empty());
155   EXPECT_TRUE(iamf->mix_presentation_id_to_audio_objects_and_metadata_.empty());
156 }
157 
TEST(Create,MapsAreEmptyWhenNoAudioProgramme)158 TEST(Create, MapsAreEmptyWhenNoAudioProgramme) {
159   const auto iamf =
160       IAMF::Create(GetAdmWithStereoAndToaObjectsWithoutAudioProgramme(),
161                    kFrameDurationMs, kSamplesPerSec);
162   ASSERT_THAT(iamf, IsOk());
163 
164   EXPECT_TRUE(iamf->audio_object_to_audio_element_.empty());
165   EXPECT_TRUE(iamf->mix_presentation_id_to_audio_objects_and_metadata_.empty());
166 }
167 
TEST(Create,PopulatesAudioObjectToAudioElement)168 TEST(Create, PopulatesAudioObjectToAudioElement) {
169   const auto iamf =
170       IAMF::Create(GetAdmWithStereoAndToaObjectsAndTwoAudioProgrammes(),
171                    kFrameDurationMs, kSamplesPerSec);
172   ASSERT_THAT(iamf, IsOk());
173 
174   EXPECT_EQ(iamf->audio_object_to_audio_element_.size(), 2);
175   EXPECT_EQ(iamf->audio_object_to_audio_element_.at(
176                 std::string(kStereoAudioObjectId)),
177             0);
178   EXPECT_EQ(iamf->audio_object_to_audio_element_.at(
179                 std::string(kThirdOrderAmbisonicsAudioObjectId)),
180             1);
181 }
182 
TEST(Create,PopulatesAudioProgrammeToAudioObjectsMap)183 TEST(Create, PopulatesAudioProgrammeToAudioObjectsMap) {
184   constexpr int32_t kExpectedFirstAudioProgramIndex = 0;
185   constexpr int32_t kExpectedFirstAudioProgramNumAudioObjects = 2;
186   constexpr int32_t kExpectedSecondAudioProgramIndex = 1;
187   constexpr int32_t kExpectedSecondAudioProgramNumAudioObjects = 1;
188 
189   const auto iamf =
190       IAMF::Create(GetAdmWithStereoAndToaObjectsAndTwoAudioProgrammes(),
191                    kFrameDurationMs, kSamplesPerSec);
192   ASSERT_THAT(iamf, IsOk());
193 
194   EXPECT_EQ(iamf->mix_presentation_id_to_audio_objects_and_metadata_.size(), 2);
195   const auto& first_mix_presentation_data =
196       iamf->mix_presentation_id_to_audio_objects_and_metadata_.at(0);
197   EXPECT_EQ(first_mix_presentation_data.original_audio_programme_index,
198             kExpectedFirstAudioProgramIndex);
199   EXPECT_EQ(first_mix_presentation_data.audio_objects.size(),
200             kExpectedFirstAudioProgramNumAudioObjects);
201   EXPECT_EQ(first_mix_presentation_data.audio_objects.at(0).id,
202             std::string(kStereoAudioObjectId));
203   EXPECT_EQ(first_mix_presentation_data.audio_objects.at(1).id,
204             std::string(kThirdOrderAmbisonicsAudioObjectId));
205 
206   const auto& second_mix_presentation_data =
207       iamf->mix_presentation_id_to_audio_objects_and_metadata_.at(1);
208   EXPECT_EQ(second_mix_presentation_data.original_audio_programme_index,
209             kExpectedSecondAudioProgramIndex);
210   EXPECT_EQ(second_mix_presentation_data.audio_objects.size(),
211             kExpectedSecondAudioProgramNumAudioObjects);
212   EXPECT_EQ(second_mix_presentation_data.audio_objects.at(0).id,
213             std::string(kThirdOrderAmbisonicsAudioObjectId));
214 }
215 
TEST(Create,IgnoresAudioProgrammeWithMoreThanTwoAudioObjects)216 TEST(Create, IgnoresAudioProgrammeWithMoreThanTwoAudioObjects) {
217   const absl::string_view kSecondStereoObjectId = "Another Stereo Audio Object";
218   auto adm = GetAdmWithStereoAndToaObjectsAndTwoAudioProgrammes();
219   adm.audio_programmes.push_back(
220       {.id = "AudioProgrammeWithTooManyAudioObjects",
221        .audio_content_id_refs = {"AudioContentIdWithThreeObjects"}});
222   adm.audio_contents.push_back(
223       {.id = "AudioContentIdWithThreeObjects",
224        .audio_object_id_ref = {std::string(kStereoAudioObjectId),
225                                std::string(kThirdOrderAmbisonicsAudioObjectId),
226                                std::string(kSecondStereoObjectId)}});
227   adm.audio_objects.push_back(GetStereoAudioObject(kSecondStereoObjectId));
228   const auto iamf = IAMF::Create(adm, kFrameDurationMs, kSamplesPerSec);
229   ASSERT_THAT(iamf, IsOk());
230 
231   EXPECT_EQ(iamf->mix_presentation_id_to_audio_objects_and_metadata_.size(), 2);
232 }
233 
TEST(Create,CreatesAudioProgrammesForComplementaryGroups)234 TEST(Create, CreatesAudioProgrammesForComplementaryGroups) {
235   const auto iamf = IAMF::Create(GetAdmWithComplementaryGroups(),
236                                  kFrameDurationMs, kSamplesPerSec);
237   ASSERT_THAT(iamf, IsOk());
238 
239   EXPECT_EQ(iamf->audio_object_to_audio_element_.size(), 2);
240 
241   EXPECT_EQ(iamf->mix_presentation_id_to_audio_objects_and_metadata_.size(), 2);
242   const auto& first_mix_presentation_data =
243       iamf->mix_presentation_id_to_audio_objects_and_metadata_.at(0);
244   EXPECT_EQ(first_mix_presentation_data.original_audio_programme_index, 0);
245   EXPECT_EQ(first_mix_presentation_data.audio_objects.size(), 1);
246   EXPECT_EQ(first_mix_presentation_data.audio_objects.at(0).id,
247             kStereoWithComplementaryGroupId);
248 
249   const auto& second_mix_presentation_data =
250       iamf->mix_presentation_id_to_audio_objects_and_metadata_.at(1);
251   EXPECT_EQ(second_mix_presentation_data.original_audio_programme_index, 0);
252   EXPECT_EQ(second_mix_presentation_data.audio_objects.size(), 1);
253   EXPECT_EQ(second_mix_presentation_data.audio_objects.at(0).id,
254             kThirdOrderAmbisonicsAudioObjectId);
255 }
256 
257 struct FrameDurationInfoTestCase {
258   int32_t frame_duration_ms;
259   int64_t samples_per_sec;
260   int32_t expected_num_samples_per_frame = 0;
261   bool is_valid = true;
262 };
263 
264 using FrameDurationInfo = ::testing::TestWithParam<FrameDurationInfoTestCase>;
265 
TEST_P(FrameDurationInfo,TestWithParam)266 TEST_P(FrameDurationInfo, TestWithParam) {
267   const auto& test_case = GetParam();
268   // Many fields are irrelevant to calculating the number of samples per frame.
269   auto iamf = IAMF::Create(
270       /*adm=*/{}, test_case.frame_duration_ms, test_case.samples_per_sec);
271   ASSERT_EQ(iamf.ok(), test_case.is_valid);
272 
273   if (test_case.is_valid) {
274     EXPECT_EQ(iamf->num_samples_per_frame_,
275               test_case.expected_num_samples_per_frame);
276   }
277 }
278 
279 INSTANTIATE_TEST_SUITE_P(CalculatesNumSamplesPerFrame, FrameDurationInfo,
280                          testing::ValuesIn<FrameDurationInfoTestCase>({
281                              {.frame_duration_ms = 10,
282                               .samples_per_sec = 48000,
283                               .expected_num_samples_per_frame = 480},
284                              {.frame_duration_ms = 20,
285                               .samples_per_sec = 48000,
286                               .expected_num_samples_per_frame = 960},
287                              {.frame_duration_ms = 20,
288                               .samples_per_sec = 96000,
289                               .expected_num_samples_per_frame = 1920},
290                              {.frame_duration_ms = 10,
291                               .samples_per_sec = 44100,
292                               .expected_num_samples_per_frame = 441},
293                          }));
294 
295 INSTANTIATE_TEST_SUITE_P(CalculatesNumSamplesPerFrameAndRoundsDown,
296                          FrameDurationInfo,
297                          testing::ValuesIn<FrameDurationInfoTestCase>({
298                              {.frame_duration_ms = 9,
299                               .samples_per_sec = 44100,
300                               .expected_num_samples_per_frame = 396},
301 
302                          }));
303 
304 INSTANTIATE_TEST_SUITE_P(
305     InvalidWheFrameDurationIsZero, FrameDurationInfo,
306     testing::ValuesIn<FrameDurationInfoTestCase>({
307         {.frame_duration_ms = 0, .samples_per_sec = 48000, .is_valid = false},
308     }));
309 
310 INSTANTIATE_TEST_SUITE_P(
311     InvalidWhenSamplesPerSecIsZero, FrameDurationInfo,
312     testing::ValuesIn<FrameDurationInfoTestCase>({
313         {.frame_duration_ms = 10, .samples_per_sec = 0, .is_valid = false},
314     }));
315 
316 }  // namespace
317 }  // namespace adm_to_user_metadata
318 }  // namespace iamf_tools
319