Add ability to play audio in circle for TestAudioDevice wav file capturer
Also use this ability in PC smoke test.
Bug: webrtc:10138
Change-Id: I83d526344f203082a19377d9642c9e453454f7ad
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/133163
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27649}
diff --git a/common_audio/wav_file.cc b/common_audio/wav_file.cc
index 1b8bcbd..77b46e7 100644
--- a/common_audio/wav_file.cc
+++ b/common_audio/wav_file.cc
@@ -74,12 +74,20 @@
num_samples_remaining_ = num_samples_;
RTC_CHECK_EQ(kWavFormat, format);
RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
+ RTC_CHECK_EQ(0, fgetpos(file_handle_, &data_start_pos_))
+ << "Failed to get WAV data position from file";
}
WavReader::~WavReader() {
Close();
}
+void WavReader::Reset() {
+ RTC_CHECK_EQ(0, fsetpos(file_handle_, &data_start_pos_))
+ << "Failed to set position in the file to WAV data start position";
+ num_samples_remaining_ = num_samples_;
+}
+
int WavReader::sample_rate() const {
return sample_rate_;
}
diff --git a/common_audio/wav_file.h b/common_audio/wav_file.h
index 7e790e0..d7aa2c1 100644
--- a/common_audio/wav_file.h
+++ b/common_audio/wav_file.h
@@ -77,6 +77,9 @@
// Close the WAV file.
~WavReader() override;
+ // Resets position to the beginning of the file.
+ void Reset();
+
// Returns the number of samples read. If this is less than requested,
// verifies that the end of the file was reached.
size_t ReadSamples(size_t num_samples, float* samples);
@@ -93,6 +96,7 @@
size_t num_samples_; // Total number of samples in the file.
size_t num_samples_remaining_;
FILE* file_handle_; // Input file, owned by this class.
+ fpos_t data_start_pos_; // Position in the file immediately after WAV header.
RTC_DISALLOW_COPY_AND_ASSIGN(WavReader);
};
diff --git a/common_audio/wav_file_unittest.cc b/common_audio/wav_file_unittest.cc
index b7e5d3f..fef87c8 100644
--- a/common_audio/wav_file_unittest.cc
+++ b/common_audio/wav_file_unittest.cc
@@ -24,9 +24,11 @@
#if defined(WEBRTC_MAC)
#define MAYBE_CPP DISABLED_CPP
#define MAYBE_CPPFileDescriptor DISABLED_CPPFileDescriptor
+#define MAYBE_CPPReset DISABLED_CPPReset
#else
#define MAYBE_CPP CPP
#define MAYBE_CPPFileDescriptor CPPFileDescriptor
+#define MAYBE_CPPReset CPPReset
#endif
namespace webrtc {
@@ -259,4 +261,77 @@
}
}
+// Write a tiny WAV file with the C++ interface then read-reset-read.
+TEST(WavReaderTest, MAYBE_CPPReset) {
+ const std::string outfile = test::OutputPath() + "wavtest4.wav";
+ static const size_t kNumSamples = 3;
+ {
+ WavWriter w(outfile, 14099, 1);
+ EXPECT_EQ(14099, w.sample_rate());
+ EXPECT_EQ(1u, w.num_channels());
+ EXPECT_EQ(0u, w.num_samples());
+ w.WriteSamples(kSamples, kNumSamples);
+ EXPECT_EQ(kNumSamples, w.num_samples());
+ }
+ // Write some extra "metadata" to the file that should be silently ignored
+ // by WavReader. We don't use WavWriter directly for this because it doesn't
+ // support metadata.
+ static const uint8_t kMetadata[] = {101, 202};
+ {
+ FILE* f = fopen(outfile.c_str(), "ab");
+ ASSERT_TRUE(f);
+ ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
+ fclose(f);
+ }
+ static const uint8_t kExpectedContents[] = {
+ // clang-format off
+ // clang formatting doesn't respect inline comments.
+ 'R', 'I', 'F', 'F',
+ 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
+ 'W', 'A', 'V', 'E',
+ 'f', 'm', 't', ' ',
+ 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
+ 1, 0, // format: PCM (1)
+ 1, 0, // channels: 1
+ 0x13, 0x37, 0, 0, // sample rate: 14099
+ 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
+ 2, 0, // block align: NumChannels * BytesPerSample
+ 16, 0, // bits per sample: 2 * 8
+ 'd', 'a', 't', 'a',
+ 6, 0, 0, 0, // size of payload: 6
+ 0, 0, // first sample: 0.0
+ 10, 0, // second sample: 10.0
+ 0xff, 0x7f, // third sample: 4e4 (saturated)
+ kMetadata[0], kMetadata[1],
+ // clang-format on
+ };
+ static const size_t kContentSize =
+ kWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
+ static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
+ EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
+ FILE* f = fopen(outfile.c_str(), "rb");
+ ASSERT_TRUE(f);
+ uint8_t contents[kContentSize];
+ ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
+ EXPECT_EQ(0, fclose(f));
+ EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
+
+ {
+ WavReader r(outfile);
+ EXPECT_EQ(14099, r.sample_rate());
+ EXPECT_EQ(1u, r.num_channels());
+ EXPECT_EQ(kNumSamples, r.num_samples());
+ static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
+ float samples[kNumSamples];
+ EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
+ EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
+ EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
+
+ r.Reset();
+ EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
+ EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
+ EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
+ }
+}
+
} // namespace webrtc
diff --git a/modules/audio_device/include/test_audio_device.cc b/modules/audio_device/include/test_audio_device.cc
index 8e439e1..5ff5387 100644
--- a/modules/audio_device/include/test_audio_device.cc
+++ b/modules/audio_device/include/test_audio_device.cc
@@ -269,17 +269,21 @@
public:
WavFileReader(std::string filename,
int sampling_frequency_in_hz,
- int num_channels)
+ int num_channels,
+ bool repeat)
: WavFileReader(absl::make_unique<WavReader>(filename),
sampling_frequency_in_hz,
- num_channels) {}
+ num_channels,
+ repeat) {}
WavFileReader(rtc::PlatformFile file,
int sampling_frequency_in_hz,
- int num_channels)
+ int num_channels,
+ bool repeat)
: WavFileReader(absl::make_unique<WavReader>(file),
sampling_frequency_in_hz,
- num_channels) {}
+ num_channels,
+ repeat) {}
int SamplingFrequency() const override { return sampling_frequency_in_hz_; }
@@ -290,7 +294,17 @@
TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) *
num_channels_,
[&](rtc::ArrayView<int16_t> data) {
- return wav_reader_->ReadSamples(data.size(), data.data());
+ size_t read = wav_reader_->ReadSamples(data.size(), data.data());
+ if (read < data.size() && repeat_) {
+ do {
+ wav_reader_->Reset();
+ size_t delta = wav_reader_->ReadSamples(
+ data.size() - read, data.subview(read).data());
+ RTC_CHECK_GT(delta, 0) << "No new data read from file";
+ read += delta;
+ } while (read < data.size());
+ }
+ return read;
});
return buffer->size() > 0;
}
@@ -298,17 +312,20 @@
private:
WavFileReader(std::unique_ptr<WavReader> wav_reader,
int sampling_frequency_in_hz,
- int num_channels)
+ int num_channels,
+ bool repeat)
: sampling_frequency_in_hz_(sampling_frequency_in_hz),
num_channels_(num_channels),
- wav_reader_(std::move(wav_reader)) {
+ wav_reader_(std::move(wav_reader)),
+ repeat_(repeat) {
RTC_CHECK_EQ(wav_reader_->sample_rate(), sampling_frequency_in_hz);
RTC_CHECK_EQ(wav_reader_->num_channels(), num_channels);
}
- int sampling_frequency_in_hz_;
+ const int sampling_frequency_in_hz_;
const int num_channels_;
std::unique_ptr<WavReader> wav_reader_;
+ const bool repeat_;
};
class WavFileWriter final : public TestAudioDeviceModule::Renderer {
@@ -495,16 +512,16 @@
int sampling_frequency_in_hz,
int num_channels) {
return absl::make_unique<WavFileReader>(filename, sampling_frequency_in_hz,
- num_channels);
+ num_channels, false);
}
std::unique_ptr<TestAudioDeviceModule::Capturer>
-TestAudioDeviceModule::CreateWavFileReader(std::string filename) {
+TestAudioDeviceModule::CreateWavFileReader(std::string filename, bool repeat) {
WavReader reader(filename);
int sampling_frequency_in_hz = reader.sample_rate();
int num_channels = rtc::checked_cast<int>(reader.num_channels());
return absl::make_unique<WavFileReader>(filename, sampling_frequency_in_hz,
- num_channels);
+ num_channels, repeat);
}
std::unique_ptr<TestAudioDeviceModule::Renderer>
@@ -528,16 +545,17 @@
int sampling_frequency_in_hz,
int num_channels) {
return absl::make_unique<WavFileReader>(file, sampling_frequency_in_hz,
- num_channels);
+ num_channels, false);
}
std::unique_ptr<TestAudioDeviceModule::Capturer>
-TestAudioDeviceModule::CreateWavFileReader(rtc::PlatformFile file) {
+TestAudioDeviceModule::CreateWavFileReader(rtc::PlatformFile file,
+ bool repeat) {
WavReader reader(file);
int sampling_frequency_in_hz = reader.sample_rate();
int num_channels = rtc::checked_cast<int>(reader.num_channels());
return absl::make_unique<WavFileReader>(file, sampling_frequency_in_hz,
- num_channels);
+ num_channels, repeat);
}
std::unique_ptr<TestAudioDeviceModule::Renderer>
diff --git a/modules/audio_device/include/test_audio_device.h b/modules/audio_device/include/test_audio_device.h
index c7ef768..586996e 100644
--- a/modules/audio_device/include/test_audio_device.h
+++ b/modules/audio_device/include/test_audio_device.h
@@ -113,7 +113,10 @@
// Returns a Capturer instance that gets its data from a file.
// Automatically detects sample rate and num of channels.
- static std::unique_ptr<Capturer> CreateWavFileReader(std::string filename);
+ // |repeat| - if true, the file will be replayed from the start when we reach
+ // the end of file.
+ static std::unique_ptr<Capturer> CreateWavFileReader(std::string filename,
+ bool repeat = false);
// Returns a Renderer instance that writes its data to a file.
static std::unique_ptr<Renderer> CreateWavFileWriter(
@@ -140,7 +143,10 @@
// Returns a Capturer instance that gets its data from a file.
// Automatically detects sample rate and num of channels.
- static std::unique_ptr<Capturer> CreateWavFileReader(rtc::PlatformFile file);
+ // |repeat| - if true, the file will be replayed from the start when we reach
+ // the end of file.
+ static std::unique_ptr<Capturer> CreateWavFileReader(rtc::PlatformFile file,
+ bool repeat = false);
// Returns a Renderer instance that writes its data to a file.
static std::unique_ptr<Renderer> CreateWavFileWriter(
diff --git a/modules/audio_device/include/test_audio_device_unittest.cc b/modules/audio_device/include/test_audio_device_unittest.cc
index db3260e..8038b95 100644
--- a/modules/audio_device/include/test_audio_device_unittest.cc
+++ b/modules/audio_device/include/test_audio_device_unittest.cc
@@ -173,6 +173,46 @@
RunTest(kInputSamples, kExpectedSamples, 8);
}
+TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) {
+ static const std::vector<int16_t> kInputSamples = {75, 1234, 243, -1231,
+ -22222, 0, 3, 88};
+ static const rtc::BufferT<int16_t> kExpectedSamples(kInputSamples.data(),
+ kInputSamples.size());
+
+ const std::string output_filename = test::OutputPath() +
+ "WavFileReaderTest_RepeatedTrue_" +
+ std::to_string(std::rand()) + ".wav";
+
+ static const size_t kSamplesPerFrame = 8;
+ static const int kSampleRate = kSamplesPerFrame * 100;
+ EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate),
+ kSamplesPerFrame);
+
+ // Create wav file to read.
+ {
+ std::unique_ptr<TestAudioDeviceModule::Renderer> writer =
+ TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800);
+
+ for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) {
+ EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>(
+ &kInputSamples[i],
+ std::min(kSamplesPerFrame, kInputSamples.size() - i))));
+ }
+ }
+
+ {
+ std::unique_ptr<TestAudioDeviceModule::Capturer> reader =
+ TestAudioDeviceModule::CreateWavFileReader(output_filename, true);
+ rtc::BufferT<int16_t> buffer(kExpectedSamples.size());
+ EXPECT_TRUE(reader->Capture(&buffer));
+ EXPECT_EQ(kExpectedSamples, buffer);
+ EXPECT_TRUE(reader->Capture(&buffer));
+ EXPECT_EQ(kExpectedSamples, buffer);
+ }
+
+ remove(output_filename.c_str());
+}
+
TEST(PulsedNoiseCapturerTest, SetMaxAmplitude) {
const int16_t kAmplitude = 50;
std::unique_ptr<TestAudioDeviceModule::PulsedNoiseCapturer> capturer =
diff --git a/test/pc/e2e/peer_connection_e2e_smoke_test.cc b/test/pc/e2e/peer_connection_e2e_smoke_test.cc
index 37388a4..f32e972 100644
--- a/test/pc/e2e/peer_connection_e2e_smoke_test.cc
+++ b/test/pc/e2e/peer_connection_e2e_smoke_test.cc
@@ -85,6 +85,9 @@
alice->AddVideoConfig(std::move(video_config));
AudioConfig audio_config;
audio_config.stream_label = "alice-audio";
+ audio_config.mode = AudioConfig::Mode::kFile;
+ audio_config.input_file_name = test::ResourcePath(
+ "pc_quality_smoke_test_alice_source", "wav");
alice->SetAudioConfig(std::move(audio_config));
});
@@ -98,6 +101,9 @@
bob->AddVideoConfig(std::move(video_config));
AudioConfig audio_config;
audio_config.stream_label = "bob-audio";
+ audio_config.mode = AudioConfig::Mode::kFile;
+ audio_config.input_file_name = test::ResourcePath(
+ "pc_quality_smoke_test_bob_source", "wav");
bob->SetAudioConfig(std::move(audio_config));
});
diff --git a/test/pc/e2e/test_peer.cc b/test/pc/e2e/test_peer.cc
index d5e4379..33c1036 100644
--- a/test/pc/e2e/test_peer.cc
+++ b/test/pc/e2e/test_peer.cc
@@ -112,7 +112,7 @@
if (audio_config.mode == AudioConfig::Mode::kFile) {
RTC_DCHECK(audio_config.input_file_name);
return TestAudioDeviceModule::CreateWavFileReader(
- audio_config.input_file_name.value());
+ audio_config.input_file_name.value(), /*repeat=*/true);
}
RTC_NOTREACHED() << "Unknown audio_config->mode";
return nullptr;