Skip to content

Commit

Permalink
audio_frame_decoder: Add tests that the debugging wav file is creat…
Browse files Browse the repository at this point in the history
…ed and has samples trimmed from it.

  - Prefer `absl::string_view`.
  - Document how this component relates to the IAMF specification.

PiperOrigin-RevId: 628165797
  • Loading branch information
jwcullen committed Apr 30, 2024
1 parent ada61aa commit cbbfa1c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 13 deletions.
6 changes: 3 additions & 3 deletions iamf/cli/audio_frame_decoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
#include <filesystem>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/container/node_hash_map.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "iamf/cli/audio_element_with_data.h"
#include "iamf/cli/audio_frame_with_data.h"
#include "iamf/cli/cli_util.h"
Expand Down Expand Up @@ -68,7 +68,7 @@ absl::Status InitializeDecoder(const CodecConfigObu& codec_config,
}

absl::Status InitializeWavWriters(
const std::string& output_wav_directory, const std::string& file_prefix,
absl::string_view output_wav_directory, absl::string_view file_prefix,
const std::list<DecodedAudioFrame>& decoded_audio_frames,
absl::node_hash_map<uint32_t, WavWriter>& wav_writers) {
for (const auto& decoded_audio_frame : decoded_audio_frames) {
Expand Down Expand Up @@ -178,7 +178,7 @@ absl::Status WriteInterlacedSamplesToWav(
// Dumps the interlaced `decoded_frames` field of the input
// `decoded_audio_frames` to a WAV file per substream.
absl::Status DumpDecodedAudioFramesWav(
const std::string& output_wav_directory, const std::string& file_prefix,
absl::string_view output_wav_directory, absl::string_view file_prefix,
const std::list<DecodedAudioFrame>& decoded_audio_frames) {
// Initialize all `WavWriter`s.
absl::node_hash_map<uint32_t, WavWriter> wav_writers;
Expand Down
15 changes: 13 additions & 2 deletions iamf/cli/audio_frame_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <vector>

#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "iamf/cli/audio_element_with_data.h"
#include "iamf/cli/audio_frame_with_data.h"

Expand All @@ -39,15 +40,25 @@ struct DecodedAudioFrame {
const AudioElementWithData* audio_element_with_data;
};

/*\!brief Decodes Audio Frame OBUs based on the associated codec.
*
* This class is related to the "Codec Decoder" as used in the IAMF
* specification. "The Codec Decoder for each Audio Substream outputs the
* decoded channels."
*
* This class manages the underlying codec decoders for all substreams. Codec
* decoders may be stateful; this class manages a one-to-one mapping between
* codec decoders and substream.
*/
class AudioFrameDecoder {
public:
/*\!brief Constructor.
*
* \param output_wav_directory Directory to write debugging wav files to.
* \param file_prefix File name prefix for debugging files.
*/
AudioFrameDecoder(const std::string& output_wav_directory,
const std::string& file_prefix)
AudioFrameDecoder(absl::string_view output_wav_directory,
absl::string_view file_prefix)
: output_wav_directory_(output_wav_directory),
file_prefix_(file_prefix) {}

Expand Down
3 changes: 3 additions & 0 deletions iamf/cli/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ cc_test(
"//iamf/cli:audio_element_with_data",
"//iamf/cli:audio_frame_decoder",
"//iamf/cli:audio_frame_with_data",
"//iamf/cli:wav_reader",
"//iamf/obu:audio_frame",
"//iamf/obu:codec_config",
"//iamf/obu:leb128",
"//iamf/obu:obu_header",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_googletest//:gtest_main",
],
)
Expand Down
75 changes: 67 additions & 8 deletions iamf/cli/tests/audio_frame_decoder_test.cc
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#include "iamf/cli/audio_frame_decoder.h"

#include <cstdint>
#include <filesystem>
#include <list>
#include <string>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "gtest/gtest.h"
#include "iamf/cli/audio_element_with_data.h"
#include "iamf/cli/audio_frame_with_data.h"
#include "iamf/cli/tests/cli_test_utils.h"
#include "iamf/cli/wav_reader.h"
#include "iamf/obu/audio_frame.h"
#include "iamf/obu/codec_config.h"
#include "iamf/obu/leb128.h"
Expand All @@ -22,9 +25,13 @@ constexpr DecodedUleb128 kCodecConfigId = 44;
constexpr uint32_t kSampleRate = 16000;
constexpr DecodedUleb128 kAudioElementId = 13;
constexpr DecodedUleb128 kSubstreamId = 0;
const int kNumChannels = 1;
const int kNumSamplesPerFrame = 8;
const int kBytesPerSample = 2;
constexpr absl::string_view kWavFilePrefix = "test";

TEST(AudioFrameDecoderTest, NoAudioFrames) {
AudioFrameDecoder decoder(::testing::TempDir(), "test");
AudioFrameDecoder decoder(::testing::TempDir(), kWavFilePrefix);

std::list<DecodedAudioFrame> decoded_audio_frames;
EXPECT_TRUE(decoder.Decode({}, decoded_audio_frames).ok());
Expand All @@ -43,19 +50,22 @@ std::list<AudioFrameWithData> PrepareEncodedAudioFrames(

std::list<AudioFrameWithData> encoded_audio_frames;
encoded_audio_frames.push_back({
.obu = AudioFrameObu(ObuHeader(), kSubstreamId,
/*audio_frame=*/std::vector<uint8_t>(16, 0)),
.obu = AudioFrameObu(
ObuHeader(), kSubstreamId,
/*audio_frame=*/
std::vector<uint8_t>(kNumSamplesPerFrame * kBytesPerSample, 0)),
.start_timestamp = 0,
.end_timestamp = 8,
.raw_samples = {std::vector<int32_t>(8, 0)},
.end_timestamp = kNumSamplesPerFrame,
.raw_samples = std::vector<std::vector<int32_t>>(
kNumSamplesPerFrame, std::vector<int32_t>(kNumChannels, 0)),
.audio_element_with_data = &audio_elements.at(kAudioElementId),
});

return encoded_audio_frames;
}

TEST(AudioFrameDecoderTest, DecodeLpcmFrame) {
AudioFrameDecoder decoder(::testing::TempDir(), "test");
AudioFrameDecoder decoder(::testing::TempDir(), kWavFilePrefix);

// Encoded frames.
absl::flat_hash_map<uint32_t, CodecConfigObu> codec_config_obus;
Expand All @@ -72,7 +82,7 @@ TEST(AudioFrameDecoderTest, DecodeLpcmFrame) {
const auto& decoded_audio_frame = decoded_audio_frames.back();
EXPECT_EQ(decoded_audio_frame.substream_id, kSubstreamId);
EXPECT_EQ(decoded_audio_frame.start_timestamp, 0);
EXPECT_EQ(decoded_audio_frame.end_timestamp, 8);
EXPECT_EQ(decoded_audio_frame.end_timestamp, kNumSamplesPerFrame);
EXPECT_EQ(decoded_audio_frame.audio_element_with_data,
&audio_elements.at(kAudioElementId));

Expand All @@ -81,6 +91,55 @@ TEST(AudioFrameDecoderTest, DecodeLpcmFrame) {
encoded_audio_frames.back().raw_samples);
}

std::filesystem::path GetFirstExpectedWavFile(uint32_t substream_id) {
return std::filesystem::path(::testing::TempDir()) /
absl::StrCat(kWavFilePrefix, "_decoded_substream_", substream_id,
".wav");
}

void CleanupExpectedFileForSubstream(uint32_t substream_id) {
std::filesystem::remove(GetFirstExpectedWavFile(substream_id));
}

void DecodeEightSampleAudioFrame(uint32_t num_samples_to_trim_at_end = 0,
uint32_t num_samples_to_trim_at_start = 0) {
CleanupExpectedFileForSubstream(kSubstreamId);
AudioFrameDecoder decoder(::testing::TempDir(), kWavFilePrefix);
// Encoded frames.
absl::flat_hash_map<uint32_t, CodecConfigObu> codec_config_obus;
absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements;
std::list<AudioFrameWithData> encoded_audio_frames =
PrepareEncodedAudioFrames(codec_config_obus, audio_elements);
encoded_audio_frames.front().obu.header_.num_samples_to_trim_at_end =
num_samples_to_trim_at_end;
encoded_audio_frames.front().obu.header_.num_samples_to_trim_at_start =
num_samples_to_trim_at_start;
// Decode.
std::list<DecodedAudioFrame> decoded_audio_frames;
EXPECT_TRUE(decoder.Decode(encoded_audio_frames, decoded_audio_frames).ok());
}

TEST(AudioFrameDecoderTest, WritesDebuggingWavFileWithExpectedNumberOfSamples) {
DecodeEightSampleAudioFrame();

EXPECT_TRUE(std::filesystem::exists(GetFirstExpectedWavFile(kSubstreamId)));
WavReader reader(GetFirstExpectedWavFile(kSubstreamId).string(),
kNumSamplesPerFrame);
EXPECT_EQ(reader.remaining_samples(), kNumSamplesPerFrame);
}

TEST(AudioFrameDecoderTest, DebuggingWavFileHasSamplesTrimmed) {
const uint32_t kNumSamplesToTrimAtEnd = 5;
const uint32_t kNumSamplesToTrimAtStart = 2;
DecodeEightSampleAudioFrame(kNumSamplesToTrimAtEnd, kNumSamplesToTrimAtStart);
const uint32_t kExpectedNumSamples = 1;
EXPECT_TRUE(std::filesystem::exists(GetFirstExpectedWavFile(kSubstreamId)));
WavReader reader(GetFirstExpectedWavFile(kSubstreamId).string(),
kNumSamplesPerFrame);

EXPECT_EQ(reader.remaining_samples(), kExpectedNumSamples);
}

// TODO(b/308073716): Add tests for more kinds of decoders.

} // namespace
Expand Down

0 comments on commit cbbfa1c

Please sign in to comment.