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/cli/parameter_block_partitioner.h"
13
14 #include <cstddef>
15 #include <cstdint>
16 #include <vector>
17
18 #include "absl/status/status.h"
19 #include "absl/status/status_matchers.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include "iamf/cli/proto/parameter_block.pb.h"
23 #include "iamf/cli/proto/parameter_data.pb.h"
24 #include "src/google/protobuf/text_format.h"
25
26 namespace iamf_tools {
27 namespace {
28
29 using ::absl_testing::IsOk;
30
31 using iamf_tools_cli_proto::MixGainParameterData;
32 using iamf_tools_cli_proto::ParameterBlockObuMetadata;
33
CreateStepMixGainParameterData(const int32_t start_point_value)34 MixGainParameterData CreateStepMixGainParameterData(
35 const int32_t start_point_value) {
36 MixGainParameterData mix_gain_parameter_data;
37 mix_gain_parameter_data.set_animation_type(
38 iamf_tools_cli_proto::ANIMATE_STEP);
39 mix_gain_parameter_data.mutable_param_data()
40 ->mutable_step()
41 ->set_start_point_value(start_point_value);
42 return mix_gain_parameter_data;
43 }
44
CreateLinearMixGainParameterData(const int32_t start_point_value,const int32_t end_point_value)45 MixGainParameterData CreateLinearMixGainParameterData(
46 const int32_t start_point_value, const int32_t end_point_value) {
47 MixGainParameterData mix_gain_parameter_data;
48 mix_gain_parameter_data.set_animation_type(
49 iamf_tools_cli_proto::ANIMATE_LINEAR);
50 auto* linear = mix_gain_parameter_data.mutable_param_data()->mutable_linear();
51 linear->set_start_point_value(start_point_value);
52 linear->set_end_point_value(end_point_value);
53 return mix_gain_parameter_data;
54 }
55
CreateBezierMixGainParameterData(const int32_t start_point_value,const int32_t end_point_value,const int32_t control_point_value,const uint32_t control_point_relative_time)56 MixGainParameterData CreateBezierMixGainParameterData(
57 const int32_t start_point_value, const int32_t end_point_value,
58 const int32_t control_point_value,
59 const uint32_t control_point_relative_time) {
60 MixGainParameterData mix_gain_parameter_data;
61 mix_gain_parameter_data.set_animation_type(
62 iamf_tools_cli_proto::ANIMATE_BEZIER);
63 auto* bezier = mix_gain_parameter_data.mutable_param_data()->mutable_bezier();
64 bezier->set_start_point_value(start_point_value);
65 bezier->set_end_point_value(end_point_value);
66 bezier->set_control_point_value(control_point_value);
67 bezier->set_control_point_relative_time(control_point_relative_time);
68 return mix_gain_parameter_data;
69 }
70
71 /*!\brief Creates a minimal parameter block OBU metadata.
72 *
73 * \param subblock_durations Input subblock durations.
74 * \param mix_gain_parameter_data Input mix gain parameter datas or an empty
75 * vector to assume all gains as step with a value of 0.
76 * \param full_parameter_block Output parameter block OBU metadata.
77 * \return `absl::OkStatus()` on success. A specific status on failure.
78 */
CreateMinimalParameterBlockObuMetadata(const std::vector<uint32_t> & subblock_durations,const std::vector<MixGainParameterData> & mix_gain_parameter_data,ParameterBlockObuMetadata & full_parameter_block)79 absl::Status CreateMinimalParameterBlockObuMetadata(
80 const std::vector<uint32_t>& subblock_durations,
81 const std::vector<MixGainParameterData>& mix_gain_parameter_data,
82 ParameterBlockObuMetadata& full_parameter_block) {
83 if (subblock_durations.empty()) {
84 return absl::InvalidArgumentError("Subblock durations cannot be empty.");
85 }
86 std::vector<MixGainParameterData> mix_gains;
87 if (mix_gain_parameter_data.empty()) {
88 // Fill with steps with a value of 0 if the input argument is not present.
89 mix_gains.resize(subblock_durations.size(),
90 CreateStepMixGainParameterData(0));
91 } else {
92 mix_gains = mix_gain_parameter_data;
93 }
94
95 // Calculate the duration from the input subblocks.
96 uint32_t duration = 0;
97 uint32_t constant_subblock_duration =
98 ParameterBlockPartitioner::FindConstantSubblockDuration(
99 subblock_durations);
100
101 // Add the duration of the remaining blocks.
102 for (auto& subblock_duration : subblock_durations) {
103 duration += subblock_duration;
104 }
105 full_parameter_block.set_duration(duration);
106 full_parameter_block.set_num_subblocks(subblock_durations.size());
107 full_parameter_block.set_constant_subblock_duration(
108 constant_subblock_duration);
109 for (int i = 0; i < subblock_durations.size(); i++) {
110 // Configure the subblocks with the input durations and mix gains.
111 auto* subblock = full_parameter_block.add_subblocks();
112 subblock->set_subblock_duration(subblock_durations[i]);
113 *subblock->mutable_mix_gain_parameter_data() = mix_gains[i];
114 }
115 full_parameter_block.set_start_timestamp(0);
116
117 return absl::OkStatus();
118 }
119
120 // TODO(b/277731089): Test `PartitionParameterBlock()` and
121 // `PartitionFrameAligned()` more thoroughly.
122
123 struct PartitionParameterBlocksTestCase {
124 std::vector<uint32_t> input_subblock_durations;
125 std::vector<MixGainParameterData> input_mix_gains;
126 int32_t partition_start;
127 int32_t partition_end;
128 std::vector<uint32_t> expected_partition_durations;
129 std::vector<MixGainParameterData> expected_output_mix_gains;
130 uint32_t constant_subblock_duration;
131 bool status_ok;
132 };
133
134 using PartitionParameterBlocks =
135 ::testing::TestWithParam<PartitionParameterBlocksTestCase>;
136
TEST_P(PartitionParameterBlocks,PartitionParameterBlock)137 TEST_P(PartitionParameterBlocks, PartitionParameterBlock) {
138 const PartitionParameterBlocksTestCase& test_case = GetParam();
139
140 // Create the parameter block to partition.
141 ParameterBlockObuMetadata full_parameter_block;
142 EXPECT_THAT(CreateMinimalParameterBlockObuMetadata(
143 test_case.input_subblock_durations, test_case.input_mix_gains,
144 full_parameter_block),
145 IsOk());
146
147 // Partition the parameter block.
148 ParameterBlockObuMetadata partitioned_parameter_block;
149
150 EXPECT_EQ(ParameterBlockPartitioner::PartitionParameterBlock(
151 full_parameter_block, test_case.partition_start,
152 test_case.partition_end, partitioned_parameter_block)
153 .ok(),
154 test_case.status_ok);
155
156 if (test_case.status_ok) {
157 // Validate the parameter block has as many subblocks in the partition as
158 // expected.
159 EXPECT_EQ(partitioned_parameter_block.num_subblocks(),
160 test_case.expected_partition_durations.size());
161
162 EXPECT_EQ(partitioned_parameter_block.constant_subblock_duration(),
163 test_case.constant_subblock_duration);
164 if (test_case.constant_subblock_duration == 0) {
165 // If the subblocks are included validate the all match the expected
166 // subblock.
167 for (int i = 0; i < test_case.expected_partition_durations.size(); ++i) {
168 EXPECT_EQ(partitioned_parameter_block.subblocks(i).subblock_duration(),
169 test_case.expected_partition_durations[i]);
170 }
171 }
172
173 // Compare the expected mix gains if present.
174 for (int i = 0; i < test_case.expected_output_mix_gains.size(); ++i) {
175 const auto& actual_mix_gain =
176 partitioned_parameter_block.subblocks(i).mix_gain_parameter_data();
177 const auto& expected_mix_gain = test_case.expected_output_mix_gains[i];
178 const auto& actual_param_data = actual_mix_gain.param_data();
179 const auto& expected_param_data = expected_mix_gain.param_data();
180 EXPECT_EQ(actual_mix_gain.animation_type(),
181 expected_mix_gain.animation_type());
182 switch (actual_mix_gain.animation_type()) {
183 using enum iamf_tools_cli_proto::AnimationType;
184 case ANIMATE_STEP:
185 EXPECT_EQ(actual_param_data.step().start_point_value(),
186 expected_param_data.step().start_point_value());
187 break;
188 case ANIMATE_LINEAR:
189 EXPECT_EQ(actual_param_data.linear().start_point_value(),
190 expected_param_data.linear().start_point_value());
191 EXPECT_EQ(actual_param_data.linear().end_point_value(),
192 expected_param_data.linear().end_point_value());
193 break;
194 case ANIMATE_BEZIER:
195 EXPECT_EQ(actual_param_data.bezier().start_point_value(),
196 expected_param_data.bezier().start_point_value());
197 EXPECT_EQ(actual_param_data.bezier().end_point_value(),
198 expected_param_data.bezier().end_point_value());
199 EXPECT_EQ(actual_param_data.bezier().control_point_value(),
200 expected_param_data.bezier().control_point_value());
201 EXPECT_EQ(actual_param_data.bezier().control_point_relative_time(),
202 expected_param_data.bezier().control_point_relative_time());
203 break;
204 default:
205 FAIL() << "Invalid animation type";
206 }
207 }
208 }
209 }
210
211 INSTANTIATE_TEST_SUITE_P(OneSubblock, PartitionParameterBlocks,
212 testing::ValuesIn<PartitionParameterBlocksTestCase>({
213 {{8000}, {}, 0, 1, {1}, {}, 1, true},
214 {{8000}, {}, 0, 128, {128}, {}, 128, true},
215 {{8000}, {}, 0, 8000, {8000}, {}, 8000, true},
216 }));
217
218 INSTANTIATE_TEST_SUITE_P(
219 TwoSubblocksConstantSubblockDurationNonzero, PartitionParameterBlocks,
220 testing::ValuesIn<PartitionParameterBlocksTestCase>({
221 {{4000, 4000}, {}, 0, 3999, {3999}, {}, 3999, true},
222 {{4000, 4000}, {}, 3950, 4050, {50, 50}, {}, 50, true},
223 {{4000, 4000}, {}, 3950, 4025, {50, 25}, {}, 50, true},
224 }));
225
226 INSTANTIATE_TEST_SUITE_P(
227 TwoSubblocksConstantSubblockDuration0, PartitionParameterBlocks,
228 testing::ValuesIn<PartitionParameterBlocksTestCase>({
229 {{4000, 4000}, {}, 3975, 4050, {25, 50}, {}, 0, true},
230 }));
231
232 INSTANTIATE_TEST_SUITE_P(
233 ManySubblocks, PartitionParameterBlocks,
234 testing::ValuesIn<PartitionParameterBlocksTestCase>({
235 {{1, 2, 3, 10, 10, 10}, {}, 0, 35, {1, 2, 3, 10, 10, 9}, {}, 0, true},
236 {{1, 2, 3, 10, 10, 10}, {}, 2, 35, {1, 3, 10, 10, 9}, {}, 0, true},
237 }));
238
239 INSTANTIATE_TEST_SUITE_P(ErrorZeroDuration, PartitionParameterBlocks,
240 testing::ValuesIn<PartitionParameterBlocksTestCase>({
241 {{4000, 4000}, {}, 0, 0, {}, {}, 0, false},
242 }));
243
244 INSTANTIATE_TEST_SUITE_P(ErrorNegativeDuration, PartitionParameterBlocks,
245 testing::ValuesIn<PartitionParameterBlocksTestCase>({
246 {{4000, 4000}, {}, 10000, 0, {}, {}, 0, false},
247 }));
248
249 INSTANTIATE_TEST_SUITE_P(ErrorNotFullyCovered, PartitionParameterBlocks,
250 testing::ValuesIn<PartitionParameterBlocksTestCase>({
251 {{4000, 4000}, {}, 4000, 8001, {}, {}, 0, false},
252 }));
253
254 INSTANTIATE_TEST_SUITE_P(Step, PartitionParameterBlocks,
255 testing::ValuesIn<PartitionParameterBlocksTestCase>({
256 {{4000, 4000},
257 {CreateStepMixGainParameterData(10),
258 CreateStepMixGainParameterData(20)},
259 0,
260 3999,
261 {3999},
262 {CreateStepMixGainParameterData(10)},
263 3999,
264 true},
265 {{4000, 4000},
266 {CreateStepMixGainParameterData(10),
267 CreateStepMixGainParameterData(20)},
268 2000,
269 6000,
270 {2000, 2000},
271 {CreateStepMixGainParameterData(10),
272 CreateStepMixGainParameterData(20)},
273 2000,
274 true},
275 }));
276
277 INSTANTIATE_TEST_SUITE_P(Linear, PartitionParameterBlocks,
278 testing::ValuesIn<PartitionParameterBlocksTestCase>({
279 {{4000, 4000},
280 {CreateLinearMixGainParameterData(0, 100),
281 CreateLinearMixGainParameterData(100, 1000)},
282 1000,
283 3000,
284 {2000},
285 {CreateLinearMixGainParameterData(25, 75)},
286 2000,
287 true},
288 }));
289
290 INSTANTIATE_TEST_SUITE_P(LinearTwoSubblocks, PartitionParameterBlocks,
291 testing::ValuesIn<PartitionParameterBlocksTestCase>({
292 {{4000, 4000},
293 {CreateLinearMixGainParameterData(0, 100),
294 CreateLinearMixGainParameterData(100, 1000)},
295 1000,
296 6000,
297 {3000, 2000},
298 {CreateLinearMixGainParameterData(25, 100),
299 CreateLinearMixGainParameterData(100, 550)},
300 3000,
301 true},
302 }));
303
304 INSTANTIATE_TEST_SUITE_P(
305 BezierAligned, PartitionParameterBlocks,
306 testing::ValuesIn<PartitionParameterBlocksTestCase>({
307 {{4000},
308 {CreateBezierMixGainParameterData(0, 100, 64, 100)},
309 0,
310 4000,
311 {4000},
312 {CreateBezierMixGainParameterData(0, 100, 64, 100)},
313 4000,
314 true},
315 }));
316
TEST(PartitionParameterBlock,InvalidForUnknownOrMissingParameterData)317 TEST(PartitionParameterBlock, InvalidForUnknownOrMissingParameterData) {
318 ParameterBlockObuMetadata full_parameter_block;
319 google::protobuf::TextFormat::ParseFromString(
320 R"pb(
321 parameter_id: 100
322 start_timestamp: 0
323 duration: 4000
324 num_subblocks: 1
325 constant_subblock_duration: 4000
326 subblocks {
327 # Parameter data is missing.
328 }
329 )pb",
330 &full_parameter_block);
331
332 ParameterBlockObuMetadata unused_parameter_block;
333 EXPECT_FALSE(ParameterBlockPartitioner::PartitionParameterBlock(
334 full_parameter_block, /*partitioned_start_time=*/0,
335 /*partitioned_end_time=*/4000, unused_parameter_block)
336 .ok());
337 }
338
ExpectHasOneSubblockWithDMixPMode(const ParameterBlockObuMetadata & parameter_block_metadata,const iamf_tools_cli_proto::DMixPMode & expected_dmixp_mode)339 void ExpectHasOneSubblockWithDMixPMode(
340 const ParameterBlockObuMetadata& parameter_block_metadata,
341 const iamf_tools_cli_proto::DMixPMode& expected_dmixp_mode) {
342 EXPECT_EQ(parameter_block_metadata.subblocks().size(), 1);
343 ASSERT_TRUE(
344 parameter_block_metadata.subblocks(0).has_demixing_info_parameter_data());
345 EXPECT_EQ(parameter_block_metadata.subblocks(0)
346 .demixing_info_parameter_data()
347 .dmixp_mode(),
348 expected_dmixp_mode);
349 }
350
TEST(PartitionParameterBlock,IsEquivalentWhenSubblockBoundaryIsNotCrossedForDemixing)351 TEST(PartitionParameterBlock,
352 IsEquivalentWhenSubblockBoundaryIsNotCrossedForDemixing) {
353 ParameterBlockObuMetadata full_parameter_block;
354 google::protobuf::TextFormat::ParseFromString(
355 R"pb(
356 parameter_id: 100
357 start_timestamp: 0
358 duration: 12000
359 num_subblocks: 3
360 constant_subblock_duration: 4000
361 # t = [0, 4000).
362 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_1 } }
363 # t = [4000, 8000).
364 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_3 } }
365 # t = [8000, 12000).
366 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_2 } }
367 )pb",
368 &full_parameter_block);
369
370 // OK if it spans the whole (semi-open) range.
371 ParameterBlockObuMetadata partition_from_first_subblock;
372 EXPECT_THAT(ParameterBlockPartitioner::PartitionParameterBlock(
373 full_parameter_block, /*partitioned_start_time=*/0,
374 /*partitioned_end_time=*/4000, partition_from_first_subblock),
375 IsOk());
376 ExpectHasOneSubblockWithDMixPMode(partition_from_first_subblock,
377 iamf_tools_cli_proto::DMIXP_MODE_1);
378 // OK if the new duration is shorter than the original subblock duration.
379 ParameterBlockObuMetadata partition_from_third_subblock;
380 EXPECT_THAT(ParameterBlockPartitioner::PartitionParameterBlock(
381 full_parameter_block, /*partitioned_start_time=*/9000,
382 /*partitioned_end_time=*/9001, partition_from_third_subblock),
383 IsOk());
384 ExpectHasOneSubblockWithDMixPMode(partition_from_third_subblock,
385 iamf_tools_cli_proto::DMIXP_MODE_2);
386 }
387
TEST(PartitionParameterBlock,InvalidWhenSubblockBoundaryIsCrossedForDemixing)388 TEST(PartitionParameterBlock, InvalidWhenSubblockBoundaryIsCrossedForDemixing) {
389 ParameterBlockObuMetadata full_parameter_block;
390 google::protobuf::TextFormat::ParseFromString(
391 R"pb(
392 parameter_id: 100
393 start_timestamp: 0
394 duration: 12000
395 num_subblocks: 3
396 constant_subblock_duration: 4000
397 # t = [0, 4000).
398 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_1 } }
399 # t = [4000, 8000).
400 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_3 } }
401 # t = [8000, 12000).
402 subblocks { demixing_info_parameter_data { dmixp_mode: DMIXP_MODE_2 } }
403 )pb",
404 &full_parameter_block);
405
406 ParameterBlockObuMetadata unused_partitioned_parameter_block;
407 EXPECT_FALSE(ParameterBlockPartitioner::PartitionParameterBlock(
408 full_parameter_block, /*partitioned_start_time=*/3950,
409 /*partitioned_end_time=*/4500,
410 unused_partitioned_parameter_block)
411 .ok());
412 EXPECT_FALSE(ParameterBlockPartitioner::PartitionParameterBlock(
413 full_parameter_block, /*partitioned_start_time=*/3999,
414 /*partitioned_end_time=*/4001,
415 unused_partitioned_parameter_block)
416 .ok());
417 }
418
TEST(PartitionParameterBlock,IsEquivalentWhenSubblockBoundaryIsNotCrossedForReconGain)419 TEST(PartitionParameterBlock,
420 IsEquivalentWhenSubblockBoundaryIsNotCrossedForReconGain) {
421 const int32_t kStartDuration = 0;
422 const int32_t kEndDuration = 4000;
423 const int32_t kExpectedDuration = kEndDuration - kStartDuration;
424 ParameterBlockObuMetadata full_parameter_block;
425 google::protobuf::TextFormat::ParseFromString(
426 R"pb(
427 parameter_id: 100
428 start_timestamp: 0
429 duration: 8000
430 num_subblocks: 1
431 constant_subblock_duration: 8000
432 subblocks {
433 recon_gain_info_parameter_data {
434 recon_gains_for_layer {}
435 recon_gains_for_layer { recon_gain { key: 2 value: 200 } }
436 }
437 }
438 )pb",
439 &full_parameter_block);
440 const size_t kNumLayers = 2;
441 const size_t kNumReconGainsForSecondLayer = 1;
442 const size_t kSecondLayerReconGainValueForKey2 = 200;
443
444 ParameterBlockObuMetadata partitioned_parameter_block;
445 EXPECT_THAT(ParameterBlockPartitioner::PartitionParameterBlock(
446 full_parameter_block, kStartDuration, kEndDuration,
447 partitioned_parameter_block),
448 IsOk());
449
450 EXPECT_EQ(partitioned_parameter_block.duration(), kExpectedDuration);
451 EXPECT_EQ(partitioned_parameter_block.subblocks().size(), 1);
452 ASSERT_TRUE(partitioned_parameter_block.subblocks(0)
453 .has_recon_gain_info_parameter_data());
454 const auto& recon_gain_info_parameter_data =
455 partitioned_parameter_block.subblocks(0).recon_gain_info_parameter_data();
456 EXPECT_EQ(recon_gain_info_parameter_data.recon_gains_for_layer().size(),
457 kNumLayers);
458 EXPECT_TRUE(recon_gain_info_parameter_data.recon_gains_for_layer(0)
459 .recon_gain()
460 .empty());
461 const auto& second_layer_recon_gains =
462 recon_gain_info_parameter_data.recon_gains_for_layer(1);
463 EXPECT_EQ(second_layer_recon_gains.recon_gain().size(),
464 kNumReconGainsForSecondLayer);
465 EXPECT_EQ(second_layer_recon_gains.recon_gain().at(2),
466 kSecondLayerReconGainValueForKey2);
467 }
468
TEST(PartitionParameterBlock,InvalidWhenSubblockBoundaryIsCrossedForReconGain)469 TEST(PartitionParameterBlock,
470 InvalidWhenSubblockBoundaryIsCrossedForReconGain) {
471 ParameterBlockObuMetadata full_parameter_block;
472 google::protobuf::TextFormat::ParseFromString(
473 R"pb(
474 parameter_id: 100
475 start_timestamp: 0
476 duration: 8000
477 num_subblocks: 2
478 constant_subblock_duration: 4000
479 # t = [0, 4000).
480 subblocks {
481 recon_gain_info_parameter_data {
482 recon_gains_for_layer {}
483 recon_gains_for_layer { recon_gain { key: 2 value: 200 } }
484 }
485 }
486 # t = [4000, 8000).
487 subblocks {
488 recon_gain_info_parameter_data {
489 recon_gains_for_layer {}
490 recon_gains_for_layer { recon_gain { key: 2 value: 100 } }
491 }
492 }
493 )pb",
494 &full_parameter_block);
495
496 ParameterBlockObuMetadata partitioned_parameter_block;
497 EXPECT_FALSE(ParameterBlockPartitioner::PartitionParameterBlock(
498 full_parameter_block, /*partitioned_start_time=*/3999,
499 /*partitioned_end_time=*/4001, partitioned_parameter_block)
500 .ok());
501 }
502
503 struct FindConstantSubblockDurationTestCase {
504 std::vector<uint32_t> input_subblock_durations;
505 uint32_t expected_constant_subblock_duration;
506 };
507
508 using FindConstantSubblockDurationTest =
509 ::testing::TestWithParam<FindConstantSubblockDurationTestCase>;
510
TEST_P(FindConstantSubblockDurationTest,PartitionParameterBlock)511 TEST_P(FindConstantSubblockDurationTest, PartitionParameterBlock) {
512 const FindConstantSubblockDurationTestCase& test_case = GetParam();
513
514 EXPECT_EQ(test_case.expected_constant_subblock_duration,
515 ParameterBlockPartitioner::FindConstantSubblockDuration(
516 test_case.input_subblock_durations));
517 }
518
519 INSTANTIATE_TEST_SUITE_P(
520 OneSubblock, FindConstantSubblockDurationTest,
521 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
522 {{1}, 1},
523 {{4000}, 4000},
524 {{UINT32_MAX}, UINT32_MAX},
525 }));
526
527 INSTANTIATE_TEST_SUITE_P(
528 TwoSubblocksFirstLonger, FindConstantSubblockDurationTest,
529 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
530 {{2, 1}, 2},
531 }));
532
533 INSTANTIATE_TEST_SUITE_P(
534 TwoSubblocksFirstShorter, FindConstantSubblockDurationTest,
535 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
536 {{1, 2}, 0},
537 }));
538
539 INSTANTIATE_TEST_SUITE_P(
540 ManySubblocksEqual, FindConstantSubblockDurationTest,
541 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
542 {{99, 99, 99, 99}, 99},
543 {{4, 4, 4, 4}, 4},
544 }));
545
546 INSTANTIATE_TEST_SUITE_P(
547 ManySubblocksLastShorter, FindConstantSubblockDurationTest,
548 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
549 {{99, 99, 99, 97}, 99},
550 {{4, 4, 4, 3}, 4},
551 }));
552
553 INSTANTIATE_TEST_SUITE_P(
554 ManySubblocksUnequal, FindConstantSubblockDurationTest,
555 testing::ValuesIn<FindConstantSubblockDurationTestCase>({
556 {{4, 4, 4, 5}, 0},
557 {{99, 100, 101, 102}, 0},
558 }));
559
560 } // namespace
561 } // namespace iamf_tools
562