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/cli_util.h"
13
14 #include <array>
15 #include <cstddef>
16 #include <cstdint>
17 #include <list>
18 #include <utility>
19 #include <variant>
20 #include <vector>
21
22 #include "absl/container/flat_hash_map.h"
23 #include "absl/container/flat_hash_set.h"
24 #include "absl/status/status.h"
25 #include "absl/status/status_matchers.h"
26 #include "absl/types/span.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "iamf/cli/audio_element_with_data.h"
30 #include "iamf/cli/audio_frame_with_data.h"
31 #include "iamf/cli/obu_with_data_generator.h"
32 #include "iamf/cli/proto/obu_header.pb.h"
33 #include "iamf/cli/proto/parameter_data.pb.h"
34 #include "iamf/cli/tests/cli_test_utils.h"
35 #include "iamf/obu/audio_element.h"
36 #include "iamf/obu/audio_frame.h"
37 #include "iamf/obu/codec_config.h"
38 #include "iamf/obu/mix_presentation.h"
39 #include "iamf/obu/obu_header.h"
40 #include "iamf/obu/param_definition_variant.h"
41 #include "iamf/obu/param_definitions.h"
42 #include "iamf/obu/types.h"
43
44 namespace iamf_tools {
45 namespace {
46
47 using ::absl_testing::IsOk;
48
49 constexpr DecodedUleb128 kCodecConfigId = 21;
50 constexpr DecodedUleb128 kAudioElementId = 100;
51 constexpr DecodedUleb128 kMixPresentationId = 100;
52 constexpr DecodedUleb128 kParameterId = 99999;
53 constexpr DecodedUleb128 kParameterRate = 48000;
54 constexpr DecodedUleb128 kFirstSubstreamId = 31;
55 constexpr DecodedUleb128 kSecondSubstreamId = 32;
56 constexpr std::array<DecodedUleb128, 1> kZerothOrderAmbisonicsSubstreamId{
57 kFirstSubstreamId};
58
TEST(WritePcmFrameToBuffer,ResizesOutputBuffer)59 TEST(WritePcmFrameToBuffer, ResizesOutputBuffer) {
60 const size_t kExpectedSize = 12; // 3 bytes per sample * 4 samples.
61 std::vector<std::vector<int32_t>> frame_to_write = {{0x7f000000, 0x7e000000},
62 {0x7f000000, 0x7e000000}};
63 const uint8_t kBitDepth = 24;
64 const bool kBigEndian = false;
65 std::vector<uint8_t> output_buffer;
66 EXPECT_THAT(WritePcmFrameToBuffer(frame_to_write, kBitDepth, kBigEndian,
67 output_buffer),
68 IsOk());
69
70 EXPECT_EQ(output_buffer.size(), kExpectedSize);
71 }
72
TEST(WritePcmFrameToBuffer,WritesBigEndian)73 TEST(WritePcmFrameToBuffer, WritesBigEndian) {
74 std::vector<std::vector<int32_t>> frame_to_write = {{0x7f001200, 0x7e003400},
75 {0x7f005600, 0x7e007800}};
76 const uint8_t kBitDepth = 24;
77 const bool kBigEndian = true;
78 std::vector<uint8_t> output_buffer;
79 EXPECT_THAT(WritePcmFrameToBuffer(frame_to_write, kBitDepth, kBigEndian,
80 output_buffer),
81 IsOk());
82
83 const std::vector<uint8_t> kExpectedBytes = {
84 0x7f, 0x00, 0x12, 0x7e, 0x00, 0x34, 0x7f, 0x00, 0x56, 0x7e, 0x00, 0x78};
85 EXPECT_EQ(output_buffer, kExpectedBytes);
86 }
87
TEST(WritePcmFrameToBuffer,WritesLittleEndian)88 TEST(WritePcmFrameToBuffer, WritesLittleEndian) {
89 std::vector<std::vector<int32_t>> frame_to_write = {{0x7f001200, 0x7e003400},
90 {0x7f005600, 0x7e007800}};
91 const uint8_t kBitDepth = 24;
92 const bool kBigEndian = false;
93 std::vector<uint8_t> output_buffer;
94 EXPECT_THAT(WritePcmFrameToBuffer(frame_to_write, kBitDepth, kBigEndian,
95 output_buffer),
96 IsOk());
97
98 const std::vector<uint8_t> kExpectedBytes = {
99 0x12, 0x00, 0x7f, 0x34, 0x00, 0x7e, 0x56, 0x00, 0x7f, 0x78, 0x00, 0x7e};
100 EXPECT_EQ(output_buffer, kExpectedBytes);
101 }
102
TEST(WritePcmFrameToBuffer,RequiresBitDepthIsMultipleOfEight)103 TEST(WritePcmFrameToBuffer, RequiresBitDepthIsMultipleOfEight) {
104 std::vector<std::vector<int32_t>> frame_to_write = {{0x7f001200, 0x7e003400},
105 {0x7f005600, 0x7e007800}};
106 const uint8_t kBitDepth = 23;
107 const bool kBigEndian = false;
108 std::vector<uint8_t> output_buffer;
109
110 EXPECT_FALSE(WritePcmFrameToBuffer(frame_to_write, kBitDepth, kBigEndian,
111 output_buffer)
112 .ok());
113 }
114
115 class GetCommonSampleRateAndBitDepthTest : public ::testing::Test {
116 public:
GetCommonSampleRateAndBitDepthTest()117 GetCommonSampleRateAndBitDepthTest()
118 : sample_rates_({48000}),
119 bit_depths_({16}),
120 expected_status_code_(absl::StatusCode::kOk),
121 expected_sample_rate_(48000),
122 expected_bit_depth_(16),
123 expected_requires_resampling_(false) {}
Test()124 void Test() {
125 uint32_t common_sample_rate;
126 uint8_t common_bit_depth;
127 bool requires_resampling;
128 EXPECT_EQ(GetCommonSampleRateAndBitDepth(
129 sample_rates_, bit_depths_, common_sample_rate,
130 common_bit_depth, requires_resampling)
131 .code(),
132 expected_status_code_);
133
134 if (expected_status_code_ == absl::StatusCode::kOk) {
135 EXPECT_EQ(common_sample_rate, expected_sample_rate_);
136 EXPECT_EQ(common_bit_depth, expected_bit_depth_);
137 EXPECT_EQ(requires_resampling, expected_requires_resampling_);
138 }
139 }
140
141 absl::flat_hash_set<uint32_t> sample_rates_;
142 absl::flat_hash_set<uint8_t> bit_depths_;
143 absl::StatusCode expected_status_code_;
144 uint32_t expected_sample_rate_;
145 uint8_t expected_bit_depth_;
146 bool expected_requires_resampling_;
147 };
148
TEST_F(GetCommonSampleRateAndBitDepthTest,DefaultUnique)149 TEST_F(GetCommonSampleRateAndBitDepthTest, DefaultUnique) { Test(); }
150
TEST_F(GetCommonSampleRateAndBitDepthTest,InvalidSampleRatesArg)151 TEST_F(GetCommonSampleRateAndBitDepthTest, InvalidSampleRatesArg) {
152 sample_rates_ = {};
153 expected_status_code_ = absl::StatusCode::kInvalidArgument;
154
155 Test();
156 }
157
TEST_F(GetCommonSampleRateAndBitDepthTest,InvalidBitDepthsArg)158 TEST_F(GetCommonSampleRateAndBitDepthTest, InvalidBitDepthsArg) {
159 bit_depths_ = {};
160 expected_status_code_ = absl::StatusCode::kInvalidArgument;
161
162 Test();
163 }
164
TEST_F(GetCommonSampleRateAndBitDepthTest,DifferentSampleRatesResampleTo48Khz)165 TEST_F(GetCommonSampleRateAndBitDepthTest,
166 DifferentSampleRatesResampleTo48Khz) {
167 sample_rates_ = {16000, 96000};
168 expected_sample_rate_ = 48000;
169 expected_requires_resampling_ = true;
170
171 Test();
172 }
173
TEST_F(GetCommonSampleRateAndBitDepthTest,DifferentBitDepthResampleTo16Bits)174 TEST_F(GetCommonSampleRateAndBitDepthTest, DifferentBitDepthResampleTo16Bits) {
175 bit_depths_ = {24, 32};
176 expected_bit_depth_ = 16;
177 expected_requires_resampling_ = true;
178
179 Test();
180 }
181
TEST_F(GetCommonSampleRateAndBitDepthTest,SampleRatesAndBitDepthsVary)182 TEST_F(GetCommonSampleRateAndBitDepthTest, SampleRatesAndBitDepthsVary) {
183 bit_depths_ = {24, 32};
184 expected_bit_depth_ = 16;
185
186 sample_rates_ = {16000, 96000};
187 expected_sample_rate_ = 48000;
188
189 expected_requires_resampling_ = true;
190
191 Test();
192 }
193
TEST_F(GetCommonSampleRateAndBitDepthTest,LargeCommonSampleRatesAndBitDepths)194 TEST_F(GetCommonSampleRateAndBitDepthTest, LargeCommonSampleRatesAndBitDepths) {
195 sample_rates_ = {192000};
196 expected_sample_rate_ = 192000;
197 bit_depths_ = {32};
198 expected_bit_depth_ = 32;
199
200 Test();
201 }
202
203 const DecodedUleb128 kFourSamplesPerFrame = 4;
204 const uint32_t kZeroSamplesToTrimAtEnd = 0;
205 const uint32_t kZeroSamplesToTrimAtStart = 0;
AddAudioFrameWithIdAndTrim(int32_t num_samples_per_frame,DecodedUleb128 audio_frame_id,uint32_t num_samples_to_trim_at_end,uint32_t num_samples_to_trim_at_start,std::list<AudioFrameWithData> & audio_frames)206 void AddAudioFrameWithIdAndTrim(int32_t num_samples_per_frame,
207 DecodedUleb128 audio_frame_id,
208 uint32_t num_samples_to_trim_at_end,
209 uint32_t num_samples_to_trim_at_start,
210 std::list<AudioFrameWithData>& audio_frames) {
211 const std::vector<uint8_t> kEmptyAudioFrameData({});
212
213 audio_frames.emplace_back(AudioFrameWithData{
214 .obu = AudioFrameObu(
215 ObuHeader{
216 .num_samples_to_trim_at_end = num_samples_to_trim_at_end,
217 .num_samples_to_trim_at_start = num_samples_to_trim_at_start},
218 audio_frame_id, kEmptyAudioFrameData),
219 .start_timestamp = 0,
220 .end_timestamp = num_samples_per_frame,
221 .audio_element_with_data = nullptr});
222 }
223
TEST(CollectAndValidateParamDefinitions,ReturnsOneUniqueParamDefinitionWhenTheyAreIdentical)224 TEST(CollectAndValidateParamDefinitions,
225 ReturnsOneUniqueParamDefinitionWhenTheyAreIdentical) {
226 // Initialize prerequisites.
227 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements = {};
228
229 // Create a mix presentation OBU. It will have a `element_mix_gain` and
230 // `output_mix_gain` which common settings.
231 std::list<MixPresentationObu> mix_presentation_obus;
232 AddMixPresentationObuWithAudioElementIds(
233 kMixPresentationId, {kAudioElementId}, kParameterId, kParameterRate,
234 mix_presentation_obus);
235 // Assert that the new mix presentation OBU has identical param definitions.
236 ASSERT_EQ(mix_presentation_obus.back()
237 .sub_mixes_[0]
238 .audio_elements[0]
239 .element_mix_gain,
240 mix_presentation_obus.back().sub_mixes_[0].output_mix_gain);
241
242 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant> result;
243 EXPECT_THAT(CollectAndValidateParamDefinitions(audio_elements,
244 mix_presentation_obus, result),
245 IsOk());
246 // Validate there is one unique param definition.
247 EXPECT_EQ(result.size(), 1);
248 }
249
TEST(CollectAndValidateParamDefinitions,IsInvalidWhenParamDefinitionsAreNotEquivalent)250 TEST(CollectAndValidateParamDefinitions,
251 IsInvalidWhenParamDefinitionsAreNotEquivalent) {
252 // Initialize prerequisites.
253 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements = {};
254
255 // Create a mix presentation OBU. It will have a `element_mix_gain` and
256 // `output_mix_gain` which common settings.
257 std::list<MixPresentationObu> mix_presentation_obus;
258 AddMixPresentationObuWithAudioElementIds(
259 kMixPresentationId, {kAudioElementId}, kParameterId, kParameterRate,
260 mix_presentation_obus);
261 auto& output_mix_gain =
262 mix_presentation_obus.back().sub_mixes_[0].output_mix_gain;
263 output_mix_gain.default_mix_gain_ = 1;
264 // Assert that the new mix presentation OBU has different param definitions.
265 ASSERT_NE(mix_presentation_obus.back()
266 .sub_mixes_[0]
267 .audio_elements[0]
268 .element_mix_gain,
269 output_mix_gain);
270
271 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant> result;
272 EXPECT_FALSE(CollectAndValidateParamDefinitions(audio_elements,
273 mix_presentation_obus, result)
274 .ok());
275 }
276
TEST(CollectAndValidateParamDefinitions,DoesNotCollectParamDefinitionsFromExtensionParamDefinitions)277 TEST(CollectAndValidateParamDefinitions,
278 DoesNotCollectParamDefinitionsFromExtensionParamDefinitions) {
279 // Initialize prerequisites.
280 absl::flat_hash_map<DecodedUleb128, CodecConfigObu> input_codec_configs;
281 AddOpusCodecConfigWithId(kCodecConfigId, input_codec_configs);
282 const std::list<MixPresentationObu> kNoMixPresentationObus = {};
283 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
284 AddAmbisonicsMonoAudioElementWithSubstreamIds(
285 kAudioElementId, kCodecConfigId, kZerothOrderAmbisonicsSubstreamId,
286 input_codec_configs, audio_elements);
287
288 // Add an extension param definition to the audio element. It is not possible
289 // to determine the ID to store it or to use further processing.
290 auto& audio_element = audio_elements.at(kAudioElementId);
291 audio_element.obu.InitializeParams(1);
292 audio_element.obu.audio_element_params_.emplace_back(
293 AudioElementParam{ExtendedParamDefinition(
294 ParamDefinition::kParameterDefinitionReservedStart)});
295
296 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant> result;
297 EXPECT_THAT(CollectAndValidateParamDefinitions(
298 audio_elements, kNoMixPresentationObus, result),
299 IsOk());
300 EXPECT_TRUE(result.empty());
301 }
302
TEST(CollectAndValidateParamDefinitions,ReconGainParamDefinition)303 TEST(CollectAndValidateParamDefinitions, ReconGainParamDefinition) {
304 absl::flat_hash_map<DecodedUleb128, CodecConfigObu> input_codec_configs;
305 AddOpusCodecConfigWithId(kCodecConfigId, input_codec_configs);
306 const std::list<MixPresentationObu> kNoMixPresentationObus = {};
307 absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
308 audio_elements_with_data;
309 AudioElementObu obu(ObuHeader(), kAudioElementId,
310 AudioElementObu::kAudioElementChannelBased, 0,
311 kCodecConfigId);
312 obu.audio_substream_ids_ = {kFirstSubstreamId, kSecondSubstreamId};
313 obu.num_substreams_ = 2;
314 obu.InitializeParams(1);
315 AddReconGainParamDefinition(kParameterId, kParameterRate, /*duration=*/1,
316 obu);
317
318 EXPECT_THAT(obu.InitializeScalableChannelLayout(2, 0), IsOk());
319
320 auto& two_layer_stereo_config =
321 std::get<ScalableChannelLayoutConfig>(obu.config_);
322 two_layer_stereo_config.channel_audio_layer_configs.clear();
323 const ChannelAudioLayerConfig mono_layer = {
324 .loudspeaker_layout =
325 ChannelAudioLayerConfig::LoudspeakerLayout::kLayoutMono,
326 .output_gain_is_present_flag = false,
327 .recon_gain_is_present_flag = true,
328 .substream_count = 1,
329 .coupled_substream_count = 0};
330 two_layer_stereo_config.channel_audio_layer_configs.push_back(mono_layer);
331 const ChannelAudioLayerConfig stereo_layer = {
332 .loudspeaker_layout =
333 ChannelAudioLayerConfig::LoudspeakerLayout::kLayoutStereo,
334 .output_gain_is_present_flag = false,
335 .recon_gain_is_present_flag = true,
336 .substream_count = 1,
337 .coupled_substream_count = 0};
338 two_layer_stereo_config.channel_audio_layer_configs.push_back(stereo_layer);
339 SubstreamIdLabelsMap substream_id_labels_map;
340 LabelGainMap label_gain_map;
341 std::vector<ChannelNumbers> channel_numbers;
342 ASSERT_THAT(ObuWithDataGenerator::FinalizeScalableChannelLayoutConfig(
343 obu.audio_substream_ids_, two_layer_stereo_config,
344 substream_id_labels_map, label_gain_map, channel_numbers),
345 IsOk());
346
347 auto iter = input_codec_configs.find(kCodecConfigId);
348 audio_elements_with_data.insert(
349 {kAudioElementId, AudioElementWithData{std::move(obu), &iter->second,
350 substream_id_labels_map,
351 label_gain_map, channel_numbers}});
352
353 absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant> result;
354 EXPECT_THAT(CollectAndValidateParamDefinitions(
355 audio_elements_with_data, kNoMixPresentationObus, result),
356 IsOk());
357 EXPECT_EQ(result.size(), 1);
358 auto param_definition_iter = result.find(kParameterId);
359 ASSERT_NE(param_definition_iter, result.end());
360 auto recon_gain_param_definition =
361 std::get_if<ReconGainParamDefinition>(¶m_definition_iter->second);
362 ASSERT_NE(recon_gain_param_definition, nullptr);
363
364 // Fields in `ReconGainParamDefinition`.
365 EXPECT_EQ(recon_gain_param_definition->parameter_id_, kParameterId);
366 EXPECT_EQ(recon_gain_param_definition->parameter_rate_, kParameterRate);
367 EXPECT_EQ(recon_gain_param_definition->param_definition_mode_, 0);
368 EXPECT_EQ(recon_gain_param_definition->duration_, 1);
369 EXPECT_EQ(recon_gain_param_definition->constant_subblock_duration_, 1);
370 EXPECT_EQ(recon_gain_param_definition->audio_element_id_, kAudioElementId);
371
372 // Auxiliary data.
373 EXPECT_EQ(recon_gain_param_definition->aux_data_.size(), 2);
374 EXPECT_EQ(
375 recon_gain_param_definition->aux_data_[0].recon_gain_is_present_flag,
376 true);
377 EXPECT_EQ(
378 recon_gain_param_definition->aux_data_[1].recon_gain_is_present_flag,
379 true);
380 constexpr ChannelNumbers kExpectedChannelNumbersMonoLayer = {.surround = 1};
381 constexpr ChannelNumbers kExpectedChannelNumbersStereoLayer = {.surround = 2};
382 EXPECT_EQ(recon_gain_param_definition->aux_data_[0].channel_numbers_for_layer,
383 kExpectedChannelNumbersMonoLayer);
384 EXPECT_EQ(recon_gain_param_definition->aux_data_[1].channel_numbers_for_layer,
385 kExpectedChannelNumbersStereoLayer);
386 }
387
TEST(IsStereoLayout,ReturnsTrueForStereoLayout)388 TEST(IsStereoLayout, ReturnsTrueForStereoLayout) {
389 Layout playback_layout = {
390 .layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
391 .specific_layout = LoudspeakersSsConventionLayout{
392 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
393 .reserved = 0}};
394 EXPECT_TRUE(IsStereoLayout(playback_layout));
395 }
396
TEST(IsStereoLayout,ReturnsFalseForNonStereoLayout)397 TEST(IsStereoLayout, ReturnsFalseForNonStereoLayout) {
398 Layout playback_layout = {.layout_type = Layout::kLayoutTypeBinaural};
399 EXPECT_FALSE(IsStereoLayout(playback_layout));
400 }
401
TEST(IsStereoLayout,ReturnsFalseForInvalidLayout)402 TEST(IsStereoLayout, ReturnsFalseForInvalidLayout) {
403 Layout playback_layout = {
404 .layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
405 .specific_layout = LoudspeakersReservedOrBinauralLayout{}};
406 EXPECT_FALSE(IsStereoLayout(playback_layout));
407 }
408
TEST(GetIndicesForLayout,SuccessWithStereoLayout)409 TEST(GetIndicesForLayout, SuccessWithStereoLayout) {
410 // Initialize prerequisites.
411 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements = {};
412 // Create a mix presentation OBU; by default, it's created with a stereo
413 // layout in the first submix.
414 std::list<MixPresentationObu> mix_presentation_obus;
415 AddMixPresentationObuWithAudioElementIds(
416 kMixPresentationId, {kAudioElementId}, kParameterId, kParameterRate,
417 mix_presentation_obus);
418 Layout playback_layout = {
419 .layout_type = Layout::kLayoutTypeLoudspeakersSsConvention,
420 .specific_layout = LoudspeakersSsConventionLayout{
421 .sound_system = LoudspeakersSsConventionLayout::kSoundSystemA_0_2_0,
422 .reserved = 0}};
423 // Set to non-default values to ensure they are returned correctly.
424 int submix_index = 2;
425 int layout_index = 2;
426 auto layout_info =
427 GetIndicesForLayout(mix_presentation_obus.back().sub_mixes_,
428 playback_layout, submix_index, layout_index);
429 EXPECT_THAT(layout_info, IsOk());
430 EXPECT_EQ(submix_index, 0);
431 EXPECT_EQ(layout_index, 0);
432 }
433
TEST(GetIndicesForLayout,FailsWithMismatchedLayout)434 TEST(GetIndicesForLayout, FailsWithMismatchedLayout) {
435 // Initialize prerequisites.
436 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements = {};
437 // Create a mix presentation OBU; by default, it's created with a stereo
438 // layout in the first submix.
439 std::list<MixPresentationObu> mix_presentation_obus;
440 AddMixPresentationObuWithAudioElementIds(
441 kMixPresentationId, {kAudioElementId}, kParameterId, kParameterRate,
442 mix_presentation_obus);
443 Layout playback_layout = {.layout_type = Layout::kLayoutTypeBinaural};
444 int submix_index;
445 int layout_index;
446 auto layout_info =
447 GetIndicesForLayout(mix_presentation_obus.back().sub_mixes_,
448 playback_layout, submix_index, layout_index);
449 EXPECT_THAT(layout_info, testing::Not(IsOk()));
450 }
451
452 } // namespace
453 } // namespace iamf_tools
454