• 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 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>(&param_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