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