1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 #include <stdint.h>
7 
8 #include <algorithm>
9 #include <memory>
10 
11 #include "base/bind.h"
12 #include "base/environment.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/path_service.h"
17 #include "base/run_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/synchronization/lock.h"
20 #include "base/test/task_environment.h"
21 #include "base/test/test_timeouts.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "media/audio/audio_device_description.h"
26 #include "media/audio/audio_device_info_accessor_for_tests.h"
27 #include "media/audio/audio_io.h"
28 #include "media/audio/audio_manager.h"
29 #include "media/audio/audio_unittest_util.h"
30 #include "media/audio/test_audio_thread.h"
31 #include "media/base/seekable_buffer.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 
35 namespace media {
36 
37 namespace {
38 
39 // Limits the number of delay measurements we can store in an array and
40 // then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
41 static const size_t kMaxDelayMeasurements = 1000;
42 
43 // Name of the output text file. The output file will be stored in the
44 // directory containing media_unittests.exe.
45 // Example: \src\build\Debug\audio_delay_values_ms.txt.
46 // See comments for the WASAPIAudioInputOutputFullDuplex test for more details
47 // about the file format.
48 static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
49 
50 // Contains delay values which are reported during the full-duplex test.
51 // Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
52 struct AudioDelayState {
AudioDelayStatemedia::__anone6bd58790111::AudioDelayState53   AudioDelayState()
54       : delta_time_ms(0),
55         buffer_delay_ms(0),
56         input_delay_ms(0),
57         output_delay_ms(0) {
58   }
59 
60   // Time in milliseconds since last delay report. Typical value is ~10 [ms].
61   int delta_time_ms;
62 
63   // Size of internal sync buffer. Typical value is ~0 [ms].
64   int buffer_delay_ms;
65 
66   // Reported capture/input delay. Typical value is ~10 [ms].
67   int input_delay_ms;
68 
69   // Reported render/output delay. Typical value is ~40 [ms].
70   int output_delay_ms;
71 };
72 
OnLogMessage(const std::string & message)73 void OnLogMessage(const std::string& message) {}
74 
75 // Test fixture class.
76 class AudioLowLatencyInputOutputTest : public testing::Test {
77  protected:
AudioLowLatencyInputOutputTest()78   AudioLowLatencyInputOutputTest() {
79     audio_manager_ =
80         AudioManager::CreateForTesting(std::make_unique<TestAudioThread>());
81   }
82 
~AudioLowLatencyInputOutputTest()83   ~AudioLowLatencyInputOutputTest() override { audio_manager_->Shutdown(); }
84 
audio_manager()85   AudioManager* audio_manager() { return audio_manager_.get(); }
task_runner()86   scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
87     return task_environment_.GetMainThreadTaskRunner();
88   }
89 
90  private:
91   base::test::TaskEnvironment task_environment_{
92       base::test::TaskEnvironment::MainThreadType::UI};
93   std::unique_ptr<AudioManager> audio_manager_;
94 
95   DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
96 };
97 
98 // This audio source/sink implementation should be used for manual tests
99 // only since delay measurements are stored on an output text file.
100 // All incoming/recorded audio packets are stored in an intermediate media
101 // buffer which the renderer reads from when it needs audio for playout.
102 // The total effect is that recorded audio is played out in loop back using
103 // a sync buffer as temporary storage.
104 class FullDuplexAudioSinkSource
105     : public AudioInputStream::AudioInputCallback,
106       public AudioOutputStream::AudioSourceCallback {
107  public:
FullDuplexAudioSinkSource(int sample_rate,int samples_per_packet,int channels)108   FullDuplexAudioSinkSource(int sample_rate,
109                             int samples_per_packet,
110                             int channels)
111     : sample_rate_(sample_rate),
112       samples_per_packet_(samples_per_packet),
113       channels_(channels),
114       input_elements_to_write_(0),
115       output_elements_to_write_(0),
116       previous_write_time_(base::TimeTicks::Now()) {
117     // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
118     frame_size_ = (16 / 8) * channels_;
119 
120     // Start with the smallest possible buffer size. It will be increased
121     // dynamically during the test if required.
122     buffer_.reset(
123         new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
124 
125     frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
126     delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
127   }
128 
~FullDuplexAudioSinkSource()129   ~FullDuplexAudioSinkSource() override {
130     // Get complete file path to output file in the directory containing
131     // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
132     base::FilePath file_name;
133     EXPECT_TRUE(base::PathService::Get(base::DIR_EXE, &file_name));
134     file_name = file_name.AppendASCII(kDelayValuesFileName);
135 
136     FILE* text_file = base::OpenFile(file_name, "wt");
137     DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
138     VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
139 
140     // Write the array which contains time-stamps, buffer size and
141     // audio delays values to a text file.
142     size_t elements_written = 0;
143     while (elements_written <
144         std::min(input_elements_to_write_, output_elements_to_write_)) {
145       const AudioDelayState state = delay_states_[elements_written];
146       fprintf(text_file, "%d %d %d %d\n",
147               state.delta_time_ms,
148               state.buffer_delay_ms,
149               state.input_delay_ms,
150               state.output_delay_ms);
151       ++elements_written;
152     }
153 
154     base::CloseFile(text_file);
155   }
156 
157   // AudioInputStream::AudioInputCallback.
OnError()158   void OnError() override {}
OnData(const AudioBus * src,base::TimeTicks capture_time,double volume)159   void OnData(const AudioBus* src,
160               base::TimeTicks capture_time,
161               double volume) override {
162     base::AutoLock lock(lock_);
163 
164     // Update three components in the AudioDelayState for this recorded
165     // audio packet.
166     const base::TimeTicks now_time = base::TimeTicks::Now();
167     const int diff = (now_time - previous_write_time_).InMilliseconds();
168     previous_write_time_ = now_time;
169     if (input_elements_to_write_ < kMaxDelayMeasurements) {
170       delay_states_[input_elements_to_write_].delta_time_ms = diff;
171       delay_states_[input_elements_to_write_].buffer_delay_ms =
172           BytesToMilliseconds(buffer_->forward_bytes());
173       delay_states_[input_elements_to_write_].input_delay_ms =
174           (base::TimeTicks::Now() - capture_time).InMilliseconds();
175       ++input_elements_to_write_;
176     }
177 
178     // TODO(henrika): fix this and use AudioFifo instead.
179     // Store the captured audio packet in a seekable media buffer.
180     // if (!buffer_->Append(src, size)) {
181     // An attempt to write outside the buffer limits has been made.
182     // Double the buffer capacity to ensure that we have a buffer large
183     // enough to handle the current sample test scenario.
184     //   buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
185     //   buffer_->Clear();
186     // }
187   }
188 
189   // AudioOutputStream::AudioSourceCallback.
OnError(ErrorType type)190   void OnError(ErrorType type) override {}
OnMoreData(base::TimeDelta delay,base::TimeTicks,int,AudioBus * dest)191   int OnMoreData(base::TimeDelta delay,
192                  base::TimeTicks /* delay_timestamp */,
193                  int /* prior_frames_skipped */,
194                  AudioBus* dest) override {
195     base::AutoLock lock(lock_);
196 
197     // Update one component in the AudioDelayState for the packet
198     // which is about to be played out.
199     if (output_elements_to_write_ < kMaxDelayMeasurements) {
200       delay_states_[output_elements_to_write_].output_delay_ms =
201           delay.InMilliseconds();
202       ++output_elements_to_write_;
203     }
204 
205     int size;
206     const uint8_t* source;
207     // Read the data from the seekable media buffer which contains
208     // captured data at the same size and sample rate as the output side.
209     if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
210       EXPECT_EQ(channels_, dest->channels());
211       size = std::min(dest->frames() * frame_size_, size);
212       EXPECT_EQ(static_cast<size_t>(size) % sizeof(*dest->channel(0)), 0U);
213       dest->FromInterleaved(source, size / frame_size_,
214                             frame_size_ / channels_);
215       buffer_->Seek(size);
216       return size / frame_size_;
217     }
218 
219     return 0;
220   }
221 
222  protected:
223   // Converts from bytes to milliseconds taking the sample rate and size
224   // of an audio frame into account.
BytesToMilliseconds(uint32_t delay_bytes) const225   int BytesToMilliseconds(uint32_t delay_bytes) const {
226     return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
227   }
228 
229  private:
230   base::Lock lock_;
231   std::unique_ptr<media::SeekableBuffer> buffer_;
232   int sample_rate_;
233   int samples_per_packet_;
234   int channels_;
235   int frame_size_;
236   double frames_to_ms_;
237   std::unique_ptr<AudioDelayState[]> delay_states_;
238   size_t input_elements_to_write_;
239   size_t output_elements_to_write_;
240   base::TimeTicks previous_write_time_;
241 };
242 
243 class AudioInputStreamTraits {
244  public:
245   typedef AudioInputStream StreamType;
246 
GetDefaultAudioStreamParameters(AudioManager * audio_manager)247   static AudioParameters GetDefaultAudioStreamParameters(
248       AudioManager* audio_manager) {
249     return AudioDeviceInfoAccessorForTests(audio_manager)
250         .GetInputStreamParameters(AudioDeviceDescription::kDefaultDeviceId);
251   }
252 
CreateStream(AudioManager * audio_manager,const AudioParameters & params)253   static StreamType* CreateStream(AudioManager* audio_manager,
254       const AudioParameters& params) {
255     return audio_manager->MakeAudioInputStream(
256         params, AudioDeviceDescription::kDefaultDeviceId,
257         base::BindRepeating(&OnLogMessage));
258   }
259 };
260 
261 class AudioOutputStreamTraits {
262  public:
263   typedef AudioOutputStream StreamType;
264 
GetDefaultAudioStreamParameters(AudioManager * audio_manager)265   static AudioParameters GetDefaultAudioStreamParameters(
266       AudioManager* audio_manager) {
267     return AudioDeviceInfoAccessorForTests(audio_manager)
268         .GetDefaultOutputStreamParameters();
269   }
270 
CreateStream(AudioManager * audio_manager,const AudioParameters & params)271   static StreamType* CreateStream(AudioManager* audio_manager,
272       const AudioParameters& params) {
273     return audio_manager->MakeAudioOutputStream(
274         params, std::string(), base::BindRepeating(&OnLogMessage));
275   }
276 };
277 
278 // Traits template holding a trait of StreamType. It encapsulates
279 // AudioInputStream and AudioOutputStream stream types.
280 template <typename StreamTraits>
281 class StreamWrapper {
282  public:
283   typedef typename StreamTraits::StreamType StreamType;
284 
StreamWrapper(AudioManager * audio_manager)285   explicit StreamWrapper(AudioManager* audio_manager)
286       : audio_manager_(audio_manager),
287         format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
288 #if defined(OS_ANDROID)
289         channel_layout_(CHANNEL_LAYOUT_MONO)
290 #else
291         channel_layout_(CHANNEL_LAYOUT_STEREO)
292 #endif
293   {
294     // Use the preferred sample rate.
295     const AudioParameters& params =
296         StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
297     sample_rate_ = params.sample_rate();
298 
299     // Use the preferred buffer size. Note that the input side uses the same
300     // size as the output side in this implementation.
301     samples_per_packet_ = params.frames_per_buffer();
302   }
303 
304   virtual ~StreamWrapper() = default;
305 
306   // Creates an Audio[Input|Output]Stream stream object using default
307   // parameters.
Create()308   StreamType* Create() {
309     return CreateStream();
310   }
311 
channels() const312   int channels() const {
313     return ChannelLayoutToChannelCount(channel_layout_);
314   }
sample_rate() const315   int sample_rate() const { return sample_rate_; }
samples_per_packet() const316   int samples_per_packet() const { return samples_per_packet_; }
317 
318  private:
CreateStream()319   StreamType* CreateStream() {
320     StreamType* stream = StreamTraits::CreateStream(
321         audio_manager_, AudioParameters(format_, channel_layout_, sample_rate_,
322                                         samples_per_packet_));
323     EXPECT_TRUE(stream);
324     return stream;
325   }
326 
327   AudioManager* audio_manager_;
328   AudioParameters::Format format_;
329   ChannelLayout channel_layout_;
330   int sample_rate_;
331   int samples_per_packet_;
332 };
333 
334 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
335 typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
336 
337 // This test is intended for manual tests and should only be enabled
338 // when it is required to make a real-time test of audio in full duplex and
339 // at the same time create a text file which contains measured delay values.
340 // The file can later be analyzed off line using e.g. MATLAB.
341 // MATLAB example:
342 //   D=load('audio_delay_values_ms.txt');
343 //   x=cumsum(D(:,1));
344 //   plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
345 //   axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
346 //   legend('buffer delay','input delay','output delay','total delay');
347 //   xlabel('time [msec]')
348 //   ylabel('delay [msec]')
349 //   title('Full-duplex audio delay measurement');
TEST_F(AudioLowLatencyInputOutputTest,DISABLED_FullDuplexDelayMeasurement)350 TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
351   AudioDeviceInfoAccessorForTests device_info_accessor(audio_manager());
352   ABORT_AUDIO_TEST_IF_NOT(device_info_accessor.HasAudioInputDevices() &&
353                           device_info_accessor.HasAudioOutputDevices());
354 
355   AudioInputStreamWrapper aisw(audio_manager());
356   AudioInputStream* ais = aisw.Create();
357   EXPECT_TRUE(ais);
358 
359   AudioOutputStreamWrapper aosw(audio_manager());
360   AudioOutputStream* aos = aosw.Create();
361   EXPECT_TRUE(aos);
362 
363   // This test only supports identical parameters in both directions.
364   // TODO(henrika): it is possible to cut delay here by using different
365   // buffer sizes for input and output.
366   if (aisw.sample_rate() != aosw.sample_rate() ||
367       aisw.samples_per_packet() != aosw.samples_per_packet() ||
368       aisw.channels() != aosw.channels()) {
369     LOG(ERROR) << "This test requires symmetric input and output parameters. "
370         "Ensure that sample rate and number of channels are identical in "
371         "both directions";
372     aos->Close();
373     ais->Close();
374     return;
375   }
376 
377   EXPECT_TRUE(ais->Open());
378   EXPECT_TRUE(aos->Open());
379 
380   FullDuplexAudioSinkSource full_duplex(
381       aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
382 
383   VLOG(0) << ">> You should now be able to hear yourself in loopback...";
384   DVLOG(0) << "   sample_rate       : " << aisw.sample_rate();
385   DVLOG(0) << "   samples_per_packet: " << aisw.samples_per_packet();
386   DVLOG(0) << "   channels          : " << aisw.channels();
387 
388   ais->Start(&full_duplex);
389   aos->Start(&full_duplex);
390 
391   // Wait for approximately 10 seconds. The user will hear their own voice
392   // in loop back during this time. At the same time, delay recordings are
393   // performed and stored in the output text file.
394   base::RunLoop run_loop;
395   task_runner()->PostDelayedTask(
396       FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
397   run_loop.Run();
398 
399   aos->Stop();
400   ais->Stop();
401 
402   // All Close() operations that run on the mocked audio thread,
403   // should be synchronous and not post additional close tasks to
404   // mocked the audio thread. Hence, there is no need to call
405   // message_loop()->RunUntilIdle() after the Close() methods.
406   aos->Close();
407   ais->Close();
408 }
409 
410 }  // namespace
411 
412 }  // namespace media
413