cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / audio / win / audio_unified_win_unittest.cc
blobcfd17aea14fa3aab7f262e5c42937e4563356405
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.
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/audio_util.h"
16 #include "media/audio/win/audio_unified_win.h"
17 #include "media/audio/win/core_audio_util_win.h"
18 #include "media/base/channel_mixer.h"
19 #include "media/base/media_switches.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using ::testing::_;
24 using ::testing::AtLeast;
25 using ::testing::Between;
26 using ::testing::DoAll;
27 using ::testing::NotNull;
28 using ::testing::Return;
29 using base::win::ScopedCOMInitializer;
31 namespace media {
33 static const size_t kMaxDeltaSamples = 1000;
34 static const char kDeltaTimeMsFileName[] = "unified_delta_times_ms.txt";
36 // Verify that the delay estimate in the OnMoreIOData() callback is larger
37 // than an expected minumum value.
38 MATCHER_P(DelayGreaterThan, value, "") {
39 return (arg.hardware_delay_bytes > value.hardware_delay_bytes);
42 // Used to terminate a loop from a different thread than the loop belongs to.
43 // |loop| should be a MessageLoopProxy.
44 ACTION_P(QuitLoop, loop) {
45 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
48 class MockUnifiedSourceCallback
49 : public AudioOutputStream::AudioSourceCallback {
50 public:
51 MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus,
52 AudioBuffersState buffers_state));
53 MOCK_METHOD3(OnMoreIOData, int(AudioBus* source,
54 AudioBus* dest,
55 AudioBuffersState buffers_state));
56 MOCK_METHOD1(OnError, void(AudioOutputStream* stream));
59 // AudioOutputStream::AudioSourceCallback implementation which enables audio
60 // play-through. It also creates a text file that contains times between two
61 // successive callbacks. Units are in milliseconds. This file can be used for
62 // off-line analysis of the callback sequence.
63 class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback {
64 public:
65 explicit UnifiedSourceCallback()
66 : previous_call_time_(base::TimeTicks::Now()),
67 text_file_(NULL),
68 elements_to_write_(0) {
69 delta_times_.reset(new int[kMaxDeltaSamples]);
72 virtual ~UnifiedSourceCallback() {
73 base::FilePath file_name;
74 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
75 file_name = file_name.AppendASCII(kDeltaTimeMsFileName);
77 EXPECT_TRUE(!text_file_);
78 text_file_ = file_util::OpenFile(file_name, "wt");
79 DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";
80 LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
82 // Write the array which contains delta times to a text file.
83 size_t elements_written = 0;
84 while (elements_written < elements_to_write_) {
85 fprintf(text_file_, "%d\n", delta_times_[elements_written]);
86 ++elements_written;
88 file_util::CloseFile(text_file_);
91 virtual int OnMoreData(AudioBus* dest,
92 AudioBuffersState buffers_state) {
93 NOTREACHED();
94 return 0;
97 virtual int OnMoreIOData(AudioBus* source,
98 AudioBus* dest,
99 AudioBuffersState buffers_state) {
100 // Store time between this callback and the previous callback.
101 const base::TimeTicks now_time = base::TimeTicks::Now();
102 const int diff = (now_time - previous_call_time_).InMilliseconds();
103 previous_call_time_ = now_time;
104 if (elements_to_write_ < kMaxDeltaSamples) {
105 delta_times_[elements_to_write_] = diff;
106 ++elements_to_write_;
109 // Play out the recorded audio samples in loop back. Perform channel mixing
110 // if required using a channel mixer which is created only if needed.
111 if (source->channels() == dest->channels()) {
112 source->CopyTo(dest);
113 } else {
114 // A channel mixer is required for converting audio between two different
115 // channel layouts.
116 if (!channel_mixer_) {
117 // Guessing the channel layout will work OK for this unit test.
118 // Main thing is that the number of channels is correct.
119 ChannelLayout input_layout = GuessChannelLayout(source->channels());
120 ChannelLayout output_layout = GuessChannelLayout(dest->channels());
121 channel_mixer_.reset(new ChannelMixer(input_layout, output_layout));
122 DVLOG(1) << "Remixing channel layout from " << input_layout
123 << " to " << output_layout << "; from "
124 << source->channels() << " channels to "
125 << dest->channels() << " channels.";
127 if (channel_mixer_)
128 channel_mixer_->Transform(source, dest);
130 return source->frames();
133 virtual void OnError(AudioOutputStream* stream) {
134 NOTREACHED();
137 private:
138 base::TimeTicks previous_call_time_;
139 scoped_ptr<int[]> delta_times_;
140 FILE* text_file_;
141 size_t elements_to_write_;
142 scoped_ptr<ChannelMixer> channel_mixer_;
145 // Convenience method which ensures that we fulfill all required conditions
146 // to run unified audio tests on Windows.
147 static bool CanRunUnifiedAudioTests(AudioManager* audio_man) {
148 if (!CoreAudioUtil::IsSupported()) {
149 LOG(WARNING) << "This tests requires Windows Vista or higher.";
150 return false;
153 if (!audio_man->HasAudioOutputDevices()) {
154 LOG(WARNING) << "No output devices detected.";
155 return false;
158 if (!audio_man->HasAudioInputDevices()) {
159 LOG(WARNING) << "No input devices detected.";
160 return false;
163 return true;
166 // Convenience class which simplifies creation of a unified AudioOutputStream
167 // object.
168 class AudioUnifiedStreamWrapper {
169 public:
170 explicit AudioUnifiedStreamWrapper(AudioManager* audio_manager)
171 : com_init_(ScopedCOMInitializer::kMTA),
172 audio_man_(audio_manager) {
173 // We open up both both sides (input and output) using the preferred
174 // set of audio parameters. These parameters corresponds to the mix format
175 // that the audio engine uses internally for processing of shared-mode
176 // output streams.
177 AudioParameters out_params;
178 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
179 eRender, eConsole, &out_params)));
181 // WebAudio is the only real user of unified audio and it always asks
182 // for stereo.
183 // TODO(henrika): extend support to other input channel layouts as well.
184 const int kInputChannels = 2;
186 params_.Reset(out_params.format(),
187 out_params.channel_layout(),
188 out_params.channels(),
189 kInputChannels,
190 out_params.sample_rate(),
191 out_params.bits_per_sample(),
192 out_params.frames_per_buffer());
195 ~AudioUnifiedStreamWrapper() {}
197 // Creates an AudioOutputStream object using default parameters.
198 WASAPIUnifiedStream* Create() {
199 return static_cast<WASAPIUnifiedStream*> (CreateOutputStream());
202 // Creates an AudioOutputStream object using default parameters but a
203 // specified input device.
204 WASAPIUnifiedStream* Create(const std::string device_id) {
205 return static_cast<WASAPIUnifiedStream*> (CreateOutputStream(device_id));
208 AudioParameters::Format format() const { return params_.format(); }
209 int channels() const { return params_.channels(); }
210 int bits_per_sample() const { return params_.bits_per_sample(); }
211 int sample_rate() const { return params_.sample_rate(); }
212 int frames_per_buffer() const { return params_.frames_per_buffer(); }
213 int bytes_per_buffer() const { return params_.GetBytesPerBuffer(); }
214 int input_channels() const { return params_.input_channels(); }
216 private:
217 AudioOutputStream* CreateOutputStream() {
218 // Get the unique device ID of the default capture device instead of using
219 // AudioManagerBase::kDefaultDeviceId since it provides slightly better
220 // test coverage and will utilize the same code path as if a non default
221 // input device was used.
222 ScopedComPtr<IMMDevice> audio_device =
223 CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole);
224 AudioDeviceName name;
225 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name)));
226 const std::string& device_id = name.unique_id;
227 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, device_id));
229 // Create the unified audio I/O stream using the default input device.
230 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_,
231 device_id);
232 EXPECT_TRUE(aos);
233 return aos;
236 AudioOutputStream* CreateOutputStream(const std::string& device_id) {
237 // Create the unified audio I/O stream using the specified input device.
238 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_,
239 device_id);
240 EXPECT_TRUE(aos);
241 return aos;
244 ScopedCOMInitializer com_init_;
245 AudioManager* audio_man_;
246 AudioParameters params_;
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();
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);
264 // Test Open(), Close() calling sequence.
265 TEST(WASAPIUnifiedStreamTest, OpenAndClose) {
266 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
267 if (!CanRunUnifiedAudioTests(audio_manager.get()))
268 return;
270 WASAPIUnifiedStream* wus = CreateDefaultUnifiedStream(audio_manager.get());
271 EXPECT_TRUE(wus->Open());
272 wus->Close();
275 // Test Open(), Close() calling sequence for all available capture devices.
276 TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) {
277 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
278 if (!CanRunUnifiedAudioTests(audio_manager.get()))
279 return;
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();
292 // Test Open(), Start(), Close() calling sequence.
293 TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) {
294 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
295 if (!CanRunUnifiedAudioTests(audio_manager.get()))
296 return;
298 MockUnifiedSourceCallback source;
299 AudioUnifiedStreamWrapper ausw(audio_manager.get());
300 WASAPIUnifiedStream* wus = ausw.Create();
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();
312 // Verify that IO callbacks starts as they should.
313 TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) {
314 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
315 if (!CanRunUnifiedAudioTests(audio_manager.get()))
316 return;
318 base::MessageLoopForUI loop;
319 MockUnifiedSourceCallback source;
320 AudioUnifiedStreamWrapper ausw(audio_manager.get());
321 WASAPIUnifiedStream* wus = ausw.Create();
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());
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();
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::Create());
351 if (!CanRunUnifiedAudioTests(audio_manager.get()))
352 return;
354 base::MessageLoopForUI loop;
355 UnifiedSourceCallback source;
356 WASAPIUnifiedStream* wus = CreateDefaultUnifiedStream(audio_manager.get());
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();
366 } // namespace media