• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear
5  * License and the Alliance for Open Media Patent License 1.0. If the BSD
6  * 3-Clause Clear License was not distributed with this source code in the
7  * LICENSE file, you can obtain it at
8  * www.aomedia.org/license/software-license/bsd-3-c-c. If the Alliance for
9  * Open Media Patent License 1.0 was not distributed with this source code
10  * in the PATENTS file, you can obtain it at www.aomedia.org/license/patent.
11  */
12 #include "iamf/cli/global_timing_module.h"
13 
14 #include <cstdint>
15 #include <optional>
16 
17 #include "absl/container/flat_hash_map.h"
18 #include "absl/status/status_matchers.h"
19 #include "absl/types/span.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include "iamf/cli/audio_element_with_data.h"
23 #include "iamf/cli/proto/parameter_block.pb.h"
24 #include "iamf/cli/proto/user_metadata.pb.h"
25 #include "iamf/cli/tests/cli_test_utils.h"
26 #include "iamf/obu/codec_config.h"
27 #include "iamf/obu/param_definition_variant.h"
28 #include "iamf/obu/param_definitions.h"
29 #include "iamf/obu/types.h"
30 
31 namespace iamf_tools {
32 namespace {
33 
34 using ::absl_testing::IsOk;
35 using ::testing::IsNull;
36 using ::testing::Not;
37 using ::testing::NotNull;
38 
39 constexpr DecodedUleb128 kSampleRate = 48000;
40 constexpr uint32_t kDuration = 960;
41 constexpr DecodedUleb128 kFirstAudioFrameId = 1000;
42 constexpr DecodedUleb128 kFirstParameterId = 0;
43 
44 // Normally the `ParamDefinitions` are stored in the descriptor OBUs. Tests can
45 // hold the raw definitions, for simplicity tests can hold the raw
46 // definitions and use this function to adapt to a map of pointers for the API.
47 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
GetParamDefinitionVariantMap(const absl::flat_hash_map<DecodedUleb128,MixGainParamDefinition> & param_definitions)48 GetParamDefinitionVariantMap(
49     const absl::flat_hash_map<DecodedUleb128, MixGainParamDefinition>&
50         param_definitions) {
51   absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
52       param_definition_variants;
53   for (const auto& [parameter_id, param_definition] : param_definitions) {
54     param_definition_variants[parameter_id] = param_definition;
55   }
56   return param_definition_variants;
57 }
58 
59 class GlobalTimingModuleTest : public ::testing::Test {
60  protected:
61   // Sets up a single audio element with the given substream IDs.
SetupObusForSubstreamIds(absl::Span<const DecodedUleb128> substream_ids)62   void SetupObusForSubstreamIds(
63       absl::Span<const DecodedUleb128> substream_ids) {
64     constexpr DecodedUleb128 kCodecConfigId = 0;
65     constexpr DecodedUleb128 kFirstAudioElementId = 0;
66     AddLpcmCodecConfigWithIdAndSampleRate(kCodecConfigId, kSampleRate,
67                                           codec_config_obus_);
68     AddAmbisonicsMonoAudioElementWithSubstreamIds(
69         kFirstAudioElementId, kCodecConfigId, substream_ids, codec_config_obus_,
70         audio_elements_);
71   }
72 
73   absl::flat_hash_map<uint32_t, CodecConfigObu> codec_config_obus_;
74   absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements_;
75 };
76 
TEST(Create,SucceedsForEmptyAudioElementsAndParamDefinitions)77 TEST(Create, SucceedsForEmptyAudioElementsAndParamDefinitions) {
78   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
79       kEmptyAudioElements;
80   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
81       kEmptyParamDefinitionVariants;
82 
83   // OK. To support "trivial IA Sequences" it is convenient to be able to
84   // support a null case.
85   EXPECT_THAT(GlobalTimingModule::Create(kEmptyAudioElements,
86                                          kEmptyParamDefinitionVariants),
87               NotNull());
88 }
89 
TEST_F(GlobalTimingModuleTest,CreateFailsForDuplicateSubstreamIds)90 TEST_F(GlobalTimingModuleTest, CreateFailsForDuplicateSubstreamIds) {
91   const DecodedUleb128 kDuplicateSubsteamId = kFirstAudioFrameId;
92   SetupObusForSubstreamIds({kDuplicateSubsteamId, kDuplicateSubsteamId});
93   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
94       kEmptyParamDefinitionVariants;
95 
96   EXPECT_THAT(GlobalTimingModule::Create(audio_elements_,
97                                          kEmptyParamDefinitionVariants),
98               IsNull());
99 }
100 
TEST(Create,FailsForParameterIdWithZeroRate)101 TEST(Create, FailsForParameterIdWithZeroRate) {
102   constexpr DecodedUleb128 kInvalidParameterRate = 0;
103   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
104       kEmptyAudioElements;
105   absl::flat_hash_map<DecodedUleb128, MixGainParamDefinition> param_definitions;
106   // The timing model does not care about the specific type of parameter. Use a
107   // generic one.
108   AddParamDefinitionWithMode0AndOneSubblock(
109       kFirstParameterId, kInvalidParameterRate, 64, param_definitions);
110 
111   EXPECT_THAT(
112       GlobalTimingModule::Create(
113           kEmptyAudioElements, GetParamDefinitionVariantMap(param_definitions)),
114       IsNull());
115 }
116 
TEST_F(GlobalTimingModuleTest,GetNextAudioFrameTimestampAdvancesTimestamps)117 TEST_F(GlobalTimingModuleTest, GetNextAudioFrameTimestampAdvancesTimestamps) {
118   SetupObusForSubstreamIds({kFirstAudioFrameId});
119   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
120       kEmptyParamDefinitionVariants;
121   auto global_timing_module = GlobalTimingModule::Create(
122       audio_elements_, kEmptyParamDefinitionVariants);
123   ASSERT_THAT(global_timing_module, NotNull());
124 
125   constexpr uint32_t kDuration = 128;
126   InternalTimestamp start_timestamp;
127   InternalTimestamp end_timestamp;
128   EXPECT_THAT(
129       global_timing_module->GetNextAudioFrameTimestamps(
130           kFirstAudioFrameId, kDuration, start_timestamp, end_timestamp),
131       IsOk());
132 
133   EXPECT_EQ(start_timestamp, 0);
134   EXPECT_EQ(end_timestamp, kDuration);
135 }
136 
TEST_F(GlobalTimingModuleTest,GetNextAudioFrameTimestampAdvancesTimestampsEachSubsteamIndependently)137 TEST_F(GlobalTimingModuleTest,
138        GetNextAudioFrameTimestampAdvancesTimestampsEachSubsteamIndependently) {
139   constexpr DecodedUleb128 kFirstSubstreamId = kFirstAudioFrameId;
140   constexpr DecodedUleb128 kSecondSubstreamId = 2000;
141   SetupObusForSubstreamIds({kFirstSubstreamId, kSecondSubstreamId});
142   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
143       kEmptyParamDefinitionVariants;
144   auto global_timing_module = GlobalTimingModule::Create(
145       audio_elements_, kEmptyParamDefinitionVariants);
146   ASSERT_THAT(global_timing_module, NotNull());
147 
148   constexpr uint32_t kDuration = 128;
149   InternalTimestamp start_timestamp;
150   InternalTimestamp end_timestamp;
151   EXPECT_THAT(
152       global_timing_module->GetNextAudioFrameTimestamps(
153           kFirstAudioFrameId, kDuration, start_timestamp, end_timestamp),
154       IsOk());
155   EXPECT_EQ(start_timestamp, 0);
156   EXPECT_EQ(end_timestamp, kDuration);
157 
158   // It's OK for another substream to tick at a different rate. It will advance
159   // independently.
160   constexpr uint32_t kLongerDuration = 256;
161   EXPECT_THAT(
162       global_timing_module->GetNextAudioFrameTimestamps(
163           kSecondSubstreamId, kLongerDuration, start_timestamp, end_timestamp),
164       IsOk());
165   EXPECT_EQ(start_timestamp, 0);
166   EXPECT_EQ(end_timestamp, kLongerDuration);
167 }
168 
TEST(GetNextAudioFrameTimestamp,FailsForUnknownSubstreamId)169 TEST(GetNextAudioFrameTimestamp, FailsForUnknownSubstreamId) {
170   constexpr DecodedUleb128 kUnknownSubstreamId = 1000;
171   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
172       kEmptyAudioElements;
173   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
174       kEmptyParamDefinitionVariants;
175   auto global_timing_module = GlobalTimingModule::Create(
176       kEmptyAudioElements, kEmptyParamDefinitionVariants);
177   ASSERT_THAT(global_timing_module, NotNull());
178 
179   InternalTimestamp start_timestamp;
180   InternalTimestamp end_timestamp;
181   EXPECT_THAT(
182       global_timing_module->GetNextAudioFrameTimestamps(
183           kUnknownSubstreamId, kDuration, start_timestamp, end_timestamp),
184       Not(IsOk()));
185 
186   // Despite the error, the timestamps should be set to the duration, which
187   // facilitates generating negative test vectors.
188   EXPECT_EQ(start_timestamp, 0);
189   EXPECT_EQ(end_timestamp, kDuration);
190 }
191 
TEST(GetNextParameterBlockTimestamps,AdvancesTimestamps)192 TEST(GetNextParameterBlockTimestamps, AdvancesTimestamps) {
193   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
194       kEmptyAudioElements;
195   absl::flat_hash_map<DecodedUleb128, MixGainParamDefinition> param_definitions;
196   // The timing model does not care about the specific type of parameter. Use a
197   // generic one.
198   AddParamDefinitionWithMode0AndOneSubblock(kFirstParameterId,
199                                             /*parameter_rate=*/kSampleRate, 64,
200                                             param_definitions);
201   auto global_timing_module = GlobalTimingModule::Create(
202       kEmptyAudioElements, GetParamDefinitionVariantMap(param_definitions));
203   ASSERT_THAT(global_timing_module, NotNull());
204 
205   constexpr uint32_t kDuration = 64;
206   InternalTimestamp start_timestamp;
207   InternalTimestamp end_timestamp;
208   EXPECT_THAT(
209       global_timing_module->GetNextParameterBlockTimestamps(
210           kFirstParameterId, 0, kDuration, start_timestamp, end_timestamp),
211       IsOk());
212   EXPECT_EQ(start_timestamp, 0);
213   EXPECT_EQ(end_timestamp, kDuration);
214 
215   EXPECT_THAT(global_timing_module->GetNextParameterBlockTimestamps(
216                   kFirstParameterId, end_timestamp, kDuration, start_timestamp,
217                   end_timestamp),
218               IsOk());
219   EXPECT_EQ(start_timestamp, 64);
220   EXPECT_EQ(end_timestamp, 128);
221 }
222 
TEST(GetNextParameterBlockTimestamps,FailsWhenInputTimestampDoesNotAgree)223 TEST(GetNextParameterBlockTimestamps, FailsWhenInputTimestampDoesNotAgree) {
224   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
225       kEmptyAudioElements;
226   absl::flat_hash_map<DecodedUleb128, MixGainParamDefinition> param_definitions;
227   // The timing model does not care about the specific type of parameter. Use a
228   // generic one.
229   AddParamDefinitionWithMode0AndOneSubblock(kFirstParameterId,
230                                             /*parameter_rate=*/kSampleRate, 64,
231                                             param_definitions);
232   auto global_timing_module = GlobalTimingModule::Create(
233       kEmptyAudioElements, GetParamDefinitionVariantMap(param_definitions));
234   ASSERT_THAT(global_timing_module, NotNull());
235 
236   constexpr uint32_t kDuration = 64;
237   constexpr InternalTimestamp kMismatchedInputStartTimestamp = 1;
238   InternalTimestamp start_timestamp;
239   InternalTimestamp end_timestamp;
240   EXPECT_THAT(global_timing_module->GetNextParameterBlockTimestamps(
241                   kFirstParameterId, kMismatchedInputStartTimestamp, kDuration,
242                   start_timestamp, end_timestamp),
243               Not(IsOk()));
244   // Despite, the error, the timestamps are set to the duration, which
245   // facilitates generating negative test vectors.
246   EXPECT_EQ(start_timestamp, 0);
247   EXPECT_EQ(end_timestamp, kDuration);
248 }
249 
TEST(GetNextParameterBlockTimestamps,FailsForUnknownParameterId)250 TEST(GetNextParameterBlockTimestamps, FailsForUnknownParameterId) {
251   constexpr DecodedUleb128 kStrayParameterBlockId = kFirstParameterId + 1;
252   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
253       kEmptyAudioElements;
254   absl::flat_hash_map<DecodedUleb128, MixGainParamDefinition> param_definitions;
255   // The timing model does not care about the specific type of parameter. Use a
256   // generic one.
257   AddParamDefinitionWithMode0AndOneSubblock(kFirstParameterId,
258                                             /*parameter_rate=*/kSampleRate, 64,
259                                             param_definitions);
260   auto global_timing_module = GlobalTimingModule::Create(
261       kEmptyAudioElements, GetParamDefinitionVariantMap(param_definitions));
262   ASSERT_THAT(global_timing_module, NotNull());
263 
264   InternalTimestamp start_timestamp;
265   InternalTimestamp end_timestamp;
266 
267   EXPECT_THAT(
268       global_timing_module->GetNextParameterBlockTimestamps(
269           kStrayParameterBlockId, 0, 64, start_timestamp, end_timestamp),
270       Not(IsOk()));
271 }
272 
TEST(GetGlobalAudioFrameTimestamp,ReturnsErrorWhenNoAudioFrames)273 TEST(GetGlobalAudioFrameTimestamp, ReturnsErrorWhenNoAudioFrames) {
274   const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
275       kEmptyAudioElements;
276   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
277       kEmptyParamDefinitionVariants;
278   auto global_timing_module = GlobalTimingModule::Create(
279       kEmptyAudioElements, kEmptyParamDefinitionVariants);
280   ASSERT_THAT(global_timing_module, NotNull());
281 
282   std::optional<InternalTimestamp> global_timestamp;
283   EXPECT_THAT(
284       global_timing_module->GetGlobalAudioFrameTimestamp(global_timestamp),
285       Not(IsOk()));
286 
287   EXPECT_EQ(global_timestamp, std::nullopt);
288 }
289 
TEST_F(GlobalTimingModuleTest,GetGlobalAudioFrameTimestampReturnsCommonTimestampWhenAudioFramesAreInSync)290 TEST_F(
291     GlobalTimingModuleTest,
292     GetGlobalAudioFrameTimestampReturnsCommonTimestampWhenAudioFramesAreInSync) {
293   constexpr DecodedUleb128 kFirstSubstreamId = kFirstAudioFrameId;
294   constexpr DecodedUleb128 kSecondSubstreamId = 2000;
295   SetupObusForSubstreamIds({kFirstSubstreamId, kSecondSubstreamId});
296   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
297       kEmptyParamDefinitionVariants;
298   auto global_timing_module = GlobalTimingModule::Create(
299       audio_elements_, kEmptyParamDefinitionVariants);
300   ASSERT_THAT(global_timing_module, NotNull());
301 
302   // Simulate a full temporal unit; two substreams are in sync.
303   constexpr uint32_t kDuration = 128;
304   InternalTimestamp ignored_start_timestamp;
305   InternalTimestamp ignored_end_timestamp;
306   EXPECT_THAT(global_timing_module->GetNextAudioFrameTimestamps(
307                   kFirstSubstreamId, kDuration, ignored_start_timestamp,
308                   ignored_end_timestamp),
309               IsOk());
310   EXPECT_THAT(global_timing_module->GetNextAudioFrameTimestamps(
311                   kSecondSubstreamId, kDuration, ignored_start_timestamp,
312                   ignored_end_timestamp),
313               IsOk());
314 
315   std::optional<InternalTimestamp> global_timestamp;
316   EXPECT_THAT(
317       global_timing_module->GetGlobalAudioFrameTimestamp(global_timestamp),
318       IsOk());
319 
320   EXPECT_EQ(global_timestamp, kDuration);
321 }
322 
TEST_F(GlobalTimingModuleTest,GetGlobalAudioFrameTimestampReturnsOkButSetsNulloptWhenAudioFramesAreOutOfSync)323 TEST_F(
324     GlobalTimingModuleTest,
325     GetGlobalAudioFrameTimestampReturnsOkButSetsNulloptWhenAudioFramesAreOutOfSync) {
326   constexpr DecodedUleb128 kFirstSubstreamId = kFirstAudioFrameId;
327   constexpr DecodedUleb128 kSecondSubstreamId = 2000;
328   SetupObusForSubstreamIds({kFirstSubstreamId, kSecondSubstreamId});
329   const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>
330       kEmptyParamDefinitionVariants;
331   auto global_timing_module = GlobalTimingModule::Create(
332       audio_elements_, kEmptyParamDefinitionVariants);
333   ASSERT_THAT(global_timing_module, NotNull());
334   // Simulate substreams which are desynchronized.
335   constexpr uint32_t kDuration = 128;
336   constexpr uint32_t kLongerDuration = 129;
337   InternalTimestamp ignored_start_timestamp;
338   InternalTimestamp ignored_end_timestamp;
339   EXPECT_THAT(global_timing_module->GetNextAudioFrameTimestamps(
340                   kFirstSubstreamId, kDuration, ignored_start_timestamp,
341                   ignored_end_timestamp),
342               IsOk());
343   EXPECT_THAT(global_timing_module->GetNextAudioFrameTimestamps(
344                   kSecondSubstreamId, kLongerDuration, ignored_start_timestamp,
345                   ignored_end_timestamp),
346               IsOk());
347 
348   // It is OK for them to be out of sync; it's possible that the caller is
349   // partially through a temporal unit. But that implies there is not currently
350   // a "global timestamp".
351   std::optional<InternalTimestamp> global_timestamp;
352   EXPECT_THAT(
353       global_timing_module->GetGlobalAudioFrameTimestamp(global_timestamp),
354       IsOk());
355 
356   EXPECT_EQ(global_timestamp, std::nullopt);
357 }
358 
359 }  // namespace
360 }  // namespace iamf_tools
361