Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / media / audio / win / audio_unified_win_unittest.cc
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 "base/basictypes.h"
6 #include "base/command_line.h"
7 #include "base/file_util.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/path_service.h"
10 #include "base/test/test_timeouts.h"
11 #include "base/time/time.h"
12 #include "base/win/scoped_com_initializer.h"
13 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_manager.h"
15 #include "media/audio/win/audio_unified_win.h"
16 #include "media/audio/win/core_audio_util_win.h"
17 #include "media/base/channel_mixer.h"
18 #include "media/base/media_switches.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using ::testing::_;
23 using ::testing::AtLeast;
24 using ::testing::Between;
25 using ::testing::DoAll;
26 using ::testing::NotNull;
27 using ::testing::Return;
28 using base::win::ScopedCOMInitializer;
29
30 namespace media {
31
32 static const size_t kMaxDeltaSamples = 1000;
33 static const char kDeltaTimeMsFileName[] = "unified_delta_times_ms.txt";
34
35 // Verify that the delay estimate in the OnMoreIOData() callback is larger
36 // than an expected minumum value.
37 MATCHER_P(DelayGreaterThan, value, "") {
38   return (arg.hardware_delay_bytes > value.hardware_delay_bytes);
39 }
40
41 // Used to terminate a loop from a different thread than the loop belongs to.
42 // |loop| should be a MessageLoopProxy.
43 ACTION_P(QuitLoop, loop) {
44   loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
45 }
46
47 class MockUnifiedSourceCallback
48     : public AudioOutputStream::AudioSourceCallback {
49  public:
50   MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus,
51                                AudioBuffersState buffers_state));
52   MOCK_METHOD3(OnMoreIOData, int(AudioBus* source,
53                                  AudioBus* dest,
54                                  AudioBuffersState buffers_state));
55   MOCK_METHOD1(OnError, void(AudioOutputStream* stream));
56 };
57
58 // AudioOutputStream::AudioSourceCallback implementation which enables audio
59 // play-through. It also creates a text file that contains times between two
60 // successive callbacks. Units are in milliseconds. This file can be used for
61 // off-line analysis of the callback sequence.
62 class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback {
63  public:
64   explicit UnifiedSourceCallback()
65       : previous_call_time_(base::TimeTicks::Now()),
66         text_file_(NULL),
67         elements_to_write_(0) {
68     delta_times_.reset(new int[kMaxDeltaSamples]);
69   }
70
71   virtual ~UnifiedSourceCallback() {
72     base::FilePath file_name;
73     EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
74     file_name = file_name.AppendASCII(kDeltaTimeMsFileName);
75
76     EXPECT_TRUE(!text_file_);
77     text_file_ = base::OpenFile(file_name, "wt");
78     DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";
79     VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
80
81     // Write the array which contains delta times to a text file.
82     size_t elements_written = 0;
83     while (elements_written < elements_to_write_) {
84       fprintf(text_file_, "%d\n", delta_times_[elements_written]);
85       ++elements_written;
86     }
87     base::CloseFile(text_file_);
88   }
89
90   virtual int OnMoreData(AudioBus* dest,
91                          AudioBuffersState buffers_state) {
92     NOTREACHED();
93     return 0;
94   };
95
96   virtual int OnMoreIOData(AudioBus* source,
97                            AudioBus* dest,
98                            AudioBuffersState buffers_state) {
99     // Store time between this callback and the previous callback.
100     const base::TimeTicks now_time = base::TimeTicks::Now();
101     const int diff = (now_time - previous_call_time_).InMilliseconds();
102     previous_call_time_ = now_time;
103     if (elements_to_write_ < kMaxDeltaSamples) {
104       delta_times_[elements_to_write_] = diff;
105       ++elements_to_write_;
106     }
107
108     // Play out the recorded audio samples in loop back. Perform channel mixing
109     // if required using a channel mixer which is created only if needed.
110     if (source->channels() == dest->channels()) {
111       source->CopyTo(dest);
112     } else {
113       // A channel mixer is required for converting audio between two different
114       // channel layouts.
115       if (!channel_mixer_) {
116         // Guessing the channel layout will work OK for this unit test.
117         // Main thing is that the number of channels is correct.
118         ChannelLayout input_layout = GuessChannelLayout(source->channels());
119         ChannelLayout output_layout = GuessChannelLayout(dest->channels());
120         channel_mixer_.reset(new ChannelMixer(input_layout, output_layout));
121         DVLOG(1) << "Remixing channel layout from " << input_layout
122                  << " to " << output_layout << "; from "
123                  << source->channels() << " channels to "
124                  << dest->channels() << " channels.";
125       }
126       if (channel_mixer_)
127         channel_mixer_->Transform(source, dest);
128     }
129     return source->frames();
130   };
131
132   virtual void OnError(AudioOutputStream* stream) {
133     NOTREACHED();
134   }
135
136  private:
137   base::TimeTicks previous_call_time_;
138   scoped_ptr<int[]> delta_times_;
139   FILE* text_file_;
140   size_t elements_to_write_;
141   scoped_ptr<ChannelMixer> channel_mixer_;
142 };
143
144 // Convenience method which ensures that we fulfill all required conditions
145 // to run unified audio tests on Windows.
146 static bool CanRunUnifiedAudioTests(AudioManager* audio_man) {
147   if (!CoreAudioUtil::IsSupported()) {
148     LOG(WARNING) << "This tests requires Windows Vista or higher.";
149     return false;
150   }
151
152   if (!audio_man->HasAudioOutputDevices()) {
153     LOG(WARNING) << "No output devices detected.";
154     return false;
155   }
156
157   if (!audio_man->HasAudioInputDevices()) {
158     LOG(WARNING) << "No input devices detected.";
159     return false;
160   }
161
162   return true;
163 }
164
165 // Convenience class which simplifies creation of a unified AudioOutputStream
166 // object.
167 class AudioUnifiedStreamWrapper {
168  public:
169   explicit AudioUnifiedStreamWrapper(AudioManager* audio_manager)
170       : com_init_(ScopedCOMInitializer::kMTA),
171         audio_man_(audio_manager) {
172     // We open up both both sides (input and output) using the preferred
173     // set of audio parameters. These parameters corresponds to the mix format
174     // that the audio engine uses internally for processing of shared-mode
175     // output streams.
176     AudioParameters out_params;
177     EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
178         eRender, eConsole, &out_params)));
179
180     // WebAudio is the only real user of unified audio and it always asks
181     // for stereo.
182     // TODO(henrika): extend support to other input channel layouts as well.
183     const int kInputChannels = 2;
184
185     params_.Reset(out_params.format(),
186                   out_params.channel_layout(),
187                   out_params.channels(),
188                   kInputChannels,
189                   out_params.sample_rate(),
190                   out_params.bits_per_sample(),
191                   out_params.frames_per_buffer());
192   }
193
194   ~AudioUnifiedStreamWrapper() {}
195
196   // Creates an AudioOutputStream object using default parameters.
197   WASAPIUnifiedStream* Create() {
198     return static_cast<WASAPIUnifiedStream*>(CreateOutputStream());
199   }
200
201   // Creates an AudioOutputStream object using default parameters but a
202   // specified input device.
203   WASAPIUnifiedStream* Create(const std::string device_id) {
204     return static_cast<WASAPIUnifiedStream*>(CreateOutputStream(device_id));
205   }
206
207   AudioParameters::Format format() const { return params_.format(); }
208   int channels() const { return params_.channels(); }
209   int bits_per_sample() const { return params_.bits_per_sample(); }
210   int sample_rate() const { return params_.sample_rate(); }
211   int frames_per_buffer() const { return params_.frames_per_buffer(); }
212   int bytes_per_buffer() const { return params_.GetBytesPerBuffer(); }
213   int input_channels() const { return params_.input_channels(); }
214
215  private:
216   AudioOutputStream* CreateOutputStream() {
217     // Get the unique device ID of the default capture device instead of using
218     // AudioManagerBase::kDefaultDeviceId since it provides slightly better
219     // test coverage and will utilize the same code path as if a non default
220     // input device was used.
221     ScopedComPtr<IMMDevice> audio_device =
222       CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole);
223     AudioDeviceName name;
224     EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name)));
225     const std::string& input_device_id = name.unique_id;
226     EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole,
227         input_device_id));
228
229     // Create the unified audio I/O stream using the default input device.
230     AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_,
231         "", input_device_id);
232     EXPECT_TRUE(aos);
233     return aos;
234   }
235
236   AudioOutputStream* CreateOutputStream(const std::string& input_device_id) {
237     // Create the unified audio I/O stream using the specified input device.
238     AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_,
239         "", input_device_id);
240     EXPECT_TRUE(aos);
241     return aos;
242   }
243
244   ScopedCOMInitializer com_init_;
245   AudioManager* audio_man_;
246   AudioParameters params_;
247 };
248
249 // Convenience method which creates a default WASAPIUnifiedStream object.
250 static WASAPIUnifiedStream* CreateDefaultUnifiedStream(
251     AudioManager* audio_manager) {
252   AudioUnifiedStreamWrapper aosw(audio_manager);
253   return aosw.Create();
254 }
255
256 // Convenience method which creates a default WASAPIUnifiedStream object but
257 // with a specified audio input device.
258 static WASAPIUnifiedStream* CreateDefaultUnifiedStream(
259     AudioManager* audio_manager, const std::string& device_id) {
260   AudioUnifiedStreamWrapper aosw(audio_manager);
261   return aosw.Create(device_id);
262 }
263
264 // Test Open(), Close() calling sequence.
265 TEST(WASAPIUnifiedStreamTest, OpenAndClose) {
266   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
267   if (!CanRunUnifiedAudioTests(audio_manager.get()))
268     return;
269
270   WASAPIUnifiedStream* wus = CreateDefaultUnifiedStream(audio_manager.get());
271   EXPECT_TRUE(wus->Open());
272   wus->Close();
273 }
274
275 // Test Open(), Close() calling sequence for all available capture devices.
276 TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) {
277   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
278   if (!CanRunUnifiedAudioTests(audio_manager.get()))
279     return;
280
281   AudioDeviceNames device_names;
282   audio_manager->GetAudioInputDeviceNames(&device_names);
283   for (AudioDeviceNames::iterator i = device_names.begin();
284        i != device_names.end(); ++i) {
285     WASAPIUnifiedStream* wus = CreateDefaultUnifiedStream(
286         audio_manager.get(), i->unique_id);
287     EXPECT_TRUE(wus->Open());
288     wus->Close();
289   }
290 }
291
292 // Test Open(), Start(), Close() calling sequence.
293 TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) {
294   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
295   if (!CanRunUnifiedAudioTests(audio_manager.get()))
296     return;
297
298   MockUnifiedSourceCallback source;
299   AudioUnifiedStreamWrapper ausw(audio_manager.get());
300   WASAPIUnifiedStream* wus = ausw.Create();
301
302   EXPECT_TRUE(wus->Open());
303   EXPECT_CALL(source, OnError(wus))
304       .Times(0);
305   EXPECT_CALL(source, OnMoreIOData(NotNull(), NotNull(), _))
306       .Times(Between(0, 1))
307       .WillOnce(Return(ausw.frames_per_buffer()));
308   wus->Start(&source);
309   wus->Close();
310 }
311
312 // Verify that IO callbacks starts as they should.
313 TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) {
314   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
315   if (!CanRunUnifiedAudioTests(audio_manager.get()))
316     return;
317
318   base::MessageLoopForUI loop;
319   MockUnifiedSourceCallback source;
320   AudioUnifiedStreamWrapper ausw(audio_manager.get());
321   WASAPIUnifiedStream* wus = ausw.Create();
322
323   // Set up expected minimum delay estimation where we use a minium delay
324   // which is equal to the sum of render and capture sizes. We can never
325   // reach a delay lower than this value.
326   AudioBuffersState min_total_audio_delay(0, 2 * ausw.bytes_per_buffer());
327
328   EXPECT_TRUE(wus->Open());
329   EXPECT_CALL(source, OnError(wus))
330       .Times(0);
331   EXPECT_CALL(source, OnMoreIOData(
332       NotNull(), NotNull(), DelayGreaterThan(min_total_audio_delay)))
333       .Times(AtLeast(2))
334       .WillOnce(Return(ausw.frames_per_buffer()))
335       .WillOnce(DoAll(
336           QuitLoop(loop.message_loop_proxy()),
337           Return(ausw.frames_per_buffer())));
338   wus->Start(&source);
339   loop.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(),
340                        TestTimeouts::action_timeout());
341   loop.Run();
342   wus->Stop();
343   wus->Close();
344 }
345
346 // Perform a real-time test in loopback where the recorded audio is echoed
347 // back to the speaker. This test allows the user to verify that the audio
348 // sounds OK. A text file with name |kDeltaTimeMsFileName| is also generated.
349 TEST(WASAPIUnifiedStreamTest, DISABLED_RealTimePlayThrough) {
350   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
351   if (!CanRunUnifiedAudioTests(audio_manager.get()))
352     return;
353
354   base::MessageLoopForUI loop;
355   UnifiedSourceCallback source;
356   WASAPIUnifiedStream* wus = CreateDefaultUnifiedStream(audio_manager.get());
357
358   EXPECT_TRUE(wus->Open());
359   wus->Start(&source);
360   loop.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(),
361                        base::TimeDelta::FromMilliseconds(10000));
362   loop.Run();
363   wus->Close();
364 }
365
366 }  // namespace media