1 /*
2 * Copyright (c) 2024, 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/wav_sample_provider.h"
13
14 #include <array>
15 #include <cstdint>
16 #include <filesystem>
17 #include <string>
18
19 // [internal] Placeholder for get runfiles header.
20 #include "absl/container/flat_hash_map.h"
21 #include "absl/status/status_matchers.h"
22 #include "absl/strings/string_view.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "iamf/cli/audio_element_with_data.h"
26 #include "iamf/cli/channel_label.h"
27 #include "iamf/cli/demixing_module.h"
28 #include "iamf/cli/proto/audio_element.pb.h"
29 #include "iamf/cli/proto/audio_frame.pb.h"
30 #include "iamf/cli/proto/user_metadata.pb.h"
31 #include "iamf/cli/tests/cli_test_utils.h"
32 #include "iamf/cli/user_metadata_builder/iamf_input_layout.h"
33 #include "iamf/obu/codec_config.h"
34 #include "iamf/obu/types.h"
35 #include "src/google/protobuf/text_format.h"
36
37 namespace iamf_tools {
38 namespace {
39
40 using ::absl_testing::IsOk;
41 using enum ChannelLabel::Label;
42 using testing::Pointwise;
43
44 constexpr DecodedUleb128 kAudioElementId = 300;
45 constexpr DecodedUleb128 kSubstreamId = 0;
46 constexpr DecodedUleb128 kCodecConfigId = 200;
47 constexpr uint32_t kSampleRate = 48000;
48
49 constexpr bool kUseChannelMetadatas = true;
50 constexpr bool kUseDeprecatedChannelLabels = false;
51
52 constexpr auto kExpectedSamplesL2 = std::to_array<InternalSampleType>(
53 {1 << 16, 2 << 16, 3 << 16, 4 << 16, 5 << 16, 6 << 16, 7 << 16, 8 << 16});
54 constexpr auto kExpectedSamplesR2 =
55 std::to_array({65535 << 16, 65534 << 16, 65533 << 16, 65532 << 16,
56 65531 << 16, 65530 << 16, 65529 << 16, 65528 << 16});
57
58 using iamf_tools_cli_proto::AudioFrameObuMetadata;
59
FillChannelMetadata(AudioFrameObuMetadata & audio_frame_metadata)60 void FillChannelMetadata(AudioFrameObuMetadata& audio_frame_metadata) {
61 using enum iamf_tools_cli_proto::ChannelLabel;
62 auto* channel_metadata = audio_frame_metadata.add_channel_metadatas();
63 channel_metadata->set_channel_id(0);
64 channel_metadata->set_channel_label(CHANNEL_LABEL_L_2);
65 channel_metadata = audio_frame_metadata.add_channel_metadatas();
66 channel_metadata->set_channel_id(1);
67 channel_metadata->set_channel_label(CHANNEL_LABEL_R_2);
68 }
69
FillDeprecatedChannelLabels(AudioFrameObuMetadata & audio_frame_metadata)70 void FillDeprecatedChannelLabels(AudioFrameObuMetadata& audio_frame_metadata) {
71 audio_frame_metadata.add_channel_ids(0);
72 audio_frame_metadata.add_channel_labels("L2");
73 audio_frame_metadata.add_channel_ids(1);
74 audio_frame_metadata.add_channel_labels("R2");
75 }
76
FillStereoDataForAudioElementId(bool use_channel_metadatas,uint32_t audio_element_id,AudioFrameObuMetadata & audio_frame_metadata)77 void FillStereoDataForAudioElementId(
78 bool use_channel_metadatas, uint32_t audio_element_id,
79 AudioFrameObuMetadata& audio_frame_metadata) {
80 ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(
81 R"pb(
82 wav_filename: "stereo_8_samples_48khz_s16le.wav"
83 samples_to_trim_at_end: 0
84 samples_to_trim_at_start: 0
85 )pb",
86 &audio_frame_metadata));
87 audio_frame_metadata.set_audio_element_id(audio_element_id);
88 if (use_channel_metadatas) {
89 FillChannelMetadata(audio_frame_metadata);
90 } else {
91 FillDeprecatedChannelLabels(audio_frame_metadata);
92 }
93 }
94
InitializeTestData(bool use_channel_metadatas,const uint32_t sample_rate,iamf_tools_cli_proto::UserMetadata & user_metadata,absl::flat_hash_map<DecodedUleb128,AudioElementWithData> & audio_elements)95 void InitializeTestData(
96 bool use_channel_metadatas, const uint32_t sample_rate,
97 iamf_tools_cli_proto::UserMetadata& user_metadata,
98 absl::flat_hash_map<DecodedUleb128, AudioElementWithData>& audio_elements) {
99 FillStereoDataForAudioElementId(use_channel_metadatas, kAudioElementId,
100 *user_metadata.add_audio_frame_metadata());
101 static absl::flat_hash_map<uint32_t, CodecConfigObu> codec_config_obus;
102 codec_config_obus.clear();
103 AddLpcmCodecConfigWithIdAndSampleRate(kCodecConfigId, sample_rate,
104 codec_config_obus);
105 AddScalableAudioElementWithSubstreamIds(
106 IamfInputLayout::kStereo, kAudioElementId, kCodecConfigId, {kSubstreamId},
107 codec_config_obus, audio_elements);
108 }
109
GetInputWavDir()110 std::string GetInputWavDir() {
111 static const auto input_wav_dir =
112 (std::filesystem::current_path() / std::string("iamf/cli/testdata"))
113 .string();
114 return input_wav_dir;
115 }
116
TEST(Create,SucceedsForStereoInputWithChannelMetadatas)117 TEST(Create, SucceedsForStereoInputWithChannelMetadatas) {
118 iamf_tools_cli_proto::UserMetadata user_metadata;
119 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
120 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
121 audio_elements);
122
123 EXPECT_THAT(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
124 GetInputWavDir(), audio_elements),
125 IsOk());
126 }
127
TEST(Create,SucceedsForStereoInputWithDeprecatedChannelLabels)128 TEST(Create, SucceedsForStereoInputWithDeprecatedChannelLabels) {
129 iamf_tools_cli_proto::UserMetadata user_metadata;
130 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
131 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
132 audio_elements);
133
134 EXPECT_THAT(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
135 GetInputWavDir(), audio_elements),
136 IsOk());
137 }
138
TEST(Create,FailsWhenMixingChannelIdsAndChannelMetadatas)139 TEST(Create, FailsWhenMixingChannelIdsAndChannelMetadatas) {
140 iamf_tools_cli_proto::UserMetadata user_metadata;
141 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
142 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
143 audio_elements);
144 // Forbid any state where the new and old styles are mixed.
145 FillDeprecatedChannelLabels(*user_metadata.mutable_audio_frame_metadata(0));
146
147 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
148 GetInputWavDir(), audio_elements)
149 .ok());
150 }
151
TEST(Create,FailsWhenUserMetadataContainsDuplicateAudioElementIds)152 TEST(Create, FailsWhenUserMetadataContainsDuplicateAudioElementIds) {
153 iamf_tools_cli_proto::UserMetadata user_metadata;
154 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
155 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
156 audio_elements);
157 FillStereoDataForAudioElementId(kUseChannelMetadatas, kAudioElementId,
158 *user_metadata.add_audio_frame_metadata());
159
160 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
161 GetInputWavDir(), audio_elements)
162 .ok());
163 }
164
TEST(Create,FailsWhenMatchingAudioElementObuIsMissing)165 TEST(Create, FailsWhenMatchingAudioElementObuIsMissing) {
166 iamf_tools_cli_proto::UserMetadata user_metadata;
167 const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>
168 kNoAudioElements = {};
169 FillStereoDataForAudioElementId(kUseChannelMetadatas, kAudioElementId,
170 *user_metadata.add_audio_frame_metadata());
171
172 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
173 GetInputWavDir(), kNoAudioElements)
174 .ok());
175 }
176
TEST(Create,FailsWhenCodecConfigIsMissing)177 TEST(Create, FailsWhenCodecConfigIsMissing) {
178 iamf_tools_cli_proto::UserMetadata user_metadata;
179 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
180 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
181 audio_elements);
182
183 // Corrupt the audio element by clearing the codec config pointer.
184 ASSERT_TRUE(audio_elements.contains(kAudioElementId));
185 audio_elements.at(kAudioElementId).codec_config = nullptr;
186
187 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
188 GetInputWavDir(), audio_elements)
189 .ok());
190 }
191
TEST(Create,FailsForUnknownLabels)192 TEST(Create, FailsForUnknownLabels) {
193 constexpr absl::string_view kUnknownLabel = "unknown_label";
194 iamf_tools_cli_proto::UserMetadata user_metadata;
195 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
196 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
197 audio_elements);
198 user_metadata.mutable_audio_frame_metadata(0)->set_channel_labels(
199 0, kUnknownLabel);
200
201 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
202 GetInputWavDir(), audio_elements)
203 .ok());
204 }
205
TEST(Create,SucceedsForDuplicateDeprecatedChannelIds)206 TEST(Create, SucceedsForDuplicateDeprecatedChannelIds) {
207 constexpr uint32_t kDuplicateChannelId = 0;
208 iamf_tools_cli_proto::UserMetadata user_metadata;
209 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
210 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
211 audio_elements);
212 user_metadata.mutable_audio_frame_metadata(0)->set_channel_ids(
213 0, kDuplicateChannelId);
214 user_metadata.mutable_audio_frame_metadata(0)->set_channel_ids(
215 1, kDuplicateChannelId);
216
217 EXPECT_THAT(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
218 GetInputWavDir(), audio_elements),
219 IsOk());
220 }
221
TEST(Create,SucceedsForDuplicateChannelMetadatasChannelIds)222 TEST(Create, SucceedsForDuplicateChannelMetadatasChannelIds) {
223 constexpr uint32_t kDuplicateChannelId = 0;
224 iamf_tools_cli_proto::UserMetadata user_metadata;
225 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
226 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
227 audio_elements);
228 user_metadata.mutable_audio_frame_metadata(0)
229 ->mutable_channel_metadatas(0)
230 ->set_channel_id(kDuplicateChannelId);
231 user_metadata.mutable_audio_frame_metadata(0)
232 ->mutable_channel_metadatas(1)
233 ->set_channel_id(kDuplicateChannelId);
234
235 EXPECT_THAT(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
236 GetInputWavDir(), audio_elements),
237 IsOk());
238 }
239
TEST(Create,FailsForDuplicateDeprecatedChannelLabels)240 TEST(Create, FailsForDuplicateDeprecatedChannelLabels) {
241 constexpr absl::string_view kDuplicateLabel = "L2";
242 iamf_tools_cli_proto::UserMetadata user_metadata;
243 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
244 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
245 audio_elements);
246 user_metadata.mutable_audio_frame_metadata(0)->set_channel_labels(
247 0, kDuplicateLabel);
248 user_metadata.mutable_audio_frame_metadata(0)->set_channel_labels(
249 1, kDuplicateLabel);
250
251 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
252 GetInputWavDir(), audio_elements)
253 .ok());
254 }
255
TEST(Create,FailsForDuplicateChannelMetadatasChannelLabels)256 TEST(Create, FailsForDuplicateChannelMetadatasChannelLabels) {
257 constexpr auto kDuplicateLabel = iamf_tools_cli_proto::CHANNEL_LABEL_L_2;
258 iamf_tools_cli_proto::UserMetadata user_metadata;
259 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
260 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
261 audio_elements);
262 user_metadata.mutable_audio_frame_metadata(0)
263 ->mutable_channel_metadatas(0)
264 ->set_channel_label(kDuplicateLabel);
265 user_metadata.mutable_audio_frame_metadata(0)
266 ->mutable_channel_metadatas(1)
267 ->set_channel_label(kDuplicateLabel);
268
269 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
270 GetInputWavDir(), audio_elements)
271 .ok());
272 }
273
TEST(Create,FailsForDeprecatedChannelIdTooLarge)274 TEST(Create, FailsForDeprecatedChannelIdTooLarge) {
275 iamf_tools_cli_proto::UserMetadata user_metadata;
276 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
277 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
278 audio_elements);
279 // Channel IDs are indexed from zero; a stereo wav file must not have a
280 // channel ID greater than 1.
281 constexpr auto kFirstChannelIndex = 0;
282 constexpr uint32_t kChannelIdTooLargeForStereoWavFile = 2;
283 user_metadata.mutable_audio_frame_metadata(0)->mutable_channel_ids()->Set(
284 kFirstChannelIndex, kChannelIdTooLargeForStereoWavFile);
285
286 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
287 GetInputWavDir(), audio_elements)
288 .ok());
289 }
290
TEST(Create,FailsForChannelMetadataChannelIdTooLarge)291 TEST(Create, FailsForChannelMetadataChannelIdTooLarge) {
292 iamf_tools_cli_proto::UserMetadata user_metadata;
293 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
294 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
295 audio_elements);
296 // Channel IDs are indexed from zero; a stereo wav file must not have a
297 // channel ID greater than 1.
298 constexpr auto kFirstChannelIndex = 0;
299 constexpr uint32_t kChannelIdTooLargeForStereoWavFile = 2;
300 user_metadata.mutable_audio_frame_metadata(0)
301 ->mutable_channel_metadatas(kFirstChannelIndex)
302 ->set_channel_id(kChannelIdTooLargeForStereoWavFile);
303
304 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
305 GetInputWavDir(), audio_elements)
306 .ok());
307 }
308
TEST(Create,FailsForDifferentSizedDeprecatedChannelIdsAndChannelLabels)309 TEST(Create, FailsForDifferentSizedDeprecatedChannelIdsAndChannelLabels) {
310 iamf_tools_cli_proto::UserMetadata user_metadata;
311 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
312 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
313 audio_elements);
314
315 // Add one extra channel label, which does not have a corresponding
316 // channel ID, causing the `Create()` to fail.
317 user_metadata.mutable_audio_frame_metadata(0)->add_channel_labels("C");
318
319 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
320 GetInputWavDir(), audio_elements)
321 .ok());
322 }
323
TEST(Create,FailsForBitDepthLowerThanFile)324 TEST(Create, FailsForBitDepthLowerThanFile) {
325 iamf_tools_cli_proto::UserMetadata user_metadata;
326 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
327 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
328 audio_elements);
329
330 // Try to load a 24-bit WAV file with a codec config whose bit depth is 16.
331 // The `Initialize()` would refuse to lower the bit depth and fail.
332 user_metadata.mutable_audio_frame_metadata(0)->set_wav_filename(
333 "stereo_8_samples_48khz_s24le.wav");
334 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
335 GetInputWavDir(), audio_elements)
336 .ok());
337 }
338
TEST(Create,FailsForMismatchingSampleRates)339 TEST(Create, FailsForMismatchingSampleRates) {
340 iamf_tools_cli_proto::UserMetadata user_metadata;
341 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
342
343 // Set the sample rate of the codec config to a different one than the WAV
344 // file, causing the `Initialize()` to fail.
345 const uint32_t kWrongSampleRate = 16000;
346 InitializeTestData(kUseChannelMetadatas, kWrongSampleRate, user_metadata,
347 audio_elements);
348
349 EXPECT_FALSE(WavSampleProvider::Create(user_metadata.audio_frame_metadata(),
350 GetInputWavDir(), audio_elements)
351 .ok());
352 }
353
ReadOneFrameExpectFinished(WavSampleProvider & wav_sample_provider,LabelSamplesMap & labeled_samples)354 void ReadOneFrameExpectFinished(WavSampleProvider& wav_sample_provider,
355 LabelSamplesMap& labeled_samples) {
356 bool finished_reading = false;
357 EXPECT_THAT(wav_sample_provider.ReadFrames(kAudioElementId, labeled_samples,
358 finished_reading),
359 IsOk());
360 EXPECT_TRUE(finished_reading);
361 }
362
TEST(WavSampleProviderTest,ReadFrameSucceedsWithDeprecatedChannelLabels)363 TEST(WavSampleProviderTest, ReadFrameSucceedsWithDeprecatedChannelLabels) {
364 iamf_tools_cli_proto::UserMetadata user_metadata;
365 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
366 InitializeTestData(kUseDeprecatedChannelLabels, kSampleRate, user_metadata,
367 audio_elements);
368 auto wav_sample_provider = WavSampleProvider::Create(
369 user_metadata.audio_frame_metadata(), GetInputWavDir(), audio_elements);
370 ASSERT_THAT(wav_sample_provider, IsOk());
371
372 LabelSamplesMap labeled_samples;
373 ReadOneFrameExpectFinished(*wav_sample_provider, labeled_samples);
374
375 // Validate samples read from the WAV file.
376 EXPECT_THAT(
377 labeled_samples[kL2],
378 Pointwise(InternalSampleMatchesIntegralSample(), kExpectedSamplesL2));
379 EXPECT_THAT(
380 labeled_samples[kR2],
381 Pointwise(InternalSampleMatchesIntegralSample(), kExpectedSamplesR2));
382 }
383
TEST(WavSampleProviderTest,ReadFrameSucceedsWithChannelMetadatas)384 TEST(WavSampleProviderTest, ReadFrameSucceedsWithChannelMetadatas) {
385 iamf_tools_cli_proto::UserMetadata user_metadata;
386 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
387 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
388 audio_elements);
389
390 auto wav_sample_provider = WavSampleProvider::Create(
391 user_metadata.audio_frame_metadata(), GetInputWavDir(), audio_elements);
392 ASSERT_THAT(wav_sample_provider, IsOk());
393
394 LabelSamplesMap labeled_samples;
395 ReadOneFrameExpectFinished(*wav_sample_provider, labeled_samples);
396
397 // Validate samples read from the WAV file.
398 EXPECT_THAT(
399 labeled_samples[kL2],
400 Pointwise(InternalSampleMatchesIntegralSample(), kExpectedSamplesL2));
401 EXPECT_THAT(
402 labeled_samples[kR2],
403 Pointwise(InternalSampleMatchesIntegralSample(), kExpectedSamplesR2));
404 }
405
TEST(WavSampleProviderTest,ReadFrameFailsWithWrongAudioElementId)406 TEST(WavSampleProviderTest, ReadFrameFailsWithWrongAudioElementId) {
407 iamf_tools_cli_proto::UserMetadata user_metadata;
408 absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
409 InitializeTestData(kUseChannelMetadatas, kSampleRate, user_metadata,
410 audio_elements);
411
412 auto wav_sample_provider = WavSampleProvider::Create(
413 user_metadata.audio_frame_metadata(), GetInputWavDir(), audio_elements);
414 ASSERT_THAT(wav_sample_provider, IsOk());
415
416 // Try to read frames using a wrong Audio Element ID.
417 const auto kWrongAudioElementId = kAudioElementId + 99;
418 LabelSamplesMap labeled_samples;
419 bool finished_reading = false;
420 EXPECT_FALSE(
421 wav_sample_provider
422 ->ReadFrames(kWrongAudioElementId, labeled_samples, finished_reading)
423 .ok());
424 }
425
426 } // namespace
427 } // namespace iamf_tools
428