Removed unused VideoCaptureCapability parameters.
[chromium-blink-merge.git] / media / audio / win / audio_unified_win_unittest.cc
blobdabf4eeea67c2f7b4531e0487beae2f3c4c868cb
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/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"
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;
30 namespace media {
32 static const size_t kMaxDeltaSamples = 1000;
33 static const char kDeltaTimeMsFileName[] = "unified_delta_times_ms.txt";
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);
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());
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));
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]);
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);
76 EXPECT_TRUE(!text_file_);
77 text_file_ = file_util::OpenFile(file_name, "wt");
78 DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";
79 LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
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;
87 file_util::CloseFile(text_file_);
90 virtual int OnMoreData(AudioBus* dest,
91 AudioBuffersState buffers_state) {
92 NOTREACHED();
93 return 0;
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_;
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.";
126 if (channel_mixer_)
127 channel_mixer_->Transform(source, dest);
129 return source->frames();
132 virtual void OnError(AudioOutputStream* stream) {
133 NOTREACHED();
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_;
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;
152 if (!audio_man->HasAudioOutputDevices()) {
153 LOG(WARNING) << "No output devices detected.";
154 return false;
157 if (!audio_man->HasAudioInputDevices()) {
158 LOG(WARNING) << "No input devices detected.";
159 return false;
162 return true;
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)));
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;
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());
194 ~AudioUnifiedStreamWrapper() {}
196 // Creates an AudioOutputStream object using default parameters.
197 WASAPIUnifiedStream* Create() {
198 return static_cast<WASAPIUnifiedStream*>(CreateOutputStream());
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));
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(); }
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));
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;
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;
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