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;