Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: media/audio/win/audio_unified_win_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698