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

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

Issue 8440002: Low-latency AudioOutputStream implementation based on WASAPI for Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « media/audio/win/audio_low_latency_output_win.cc ('k') | media/audio/win/audio_manager_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 <windows.h>
6 #include <mmsystem.h>
7
8 #include "base/basictypes.h"
9 #include "base/environment.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/test/test_timeouts.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_low_latency_output_win.h"
16 #include "media/base/seekable_buffer.h"
17 #include "media/base/test_data_util.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 using ::testing::_;
22 using ::testing::AnyNumber;
23 using ::testing::Between;
24 using ::testing::Gt;
25 using ::testing::NotNull;
26 using ::testing::Return;
27 using base::win::ScopedCOMInitializer;
28
29 namespace media {
30
31 static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw";
32 static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw";
33
34 MATCHER_P(HasValidDelay, value, "") {
35 // It is difficult to come up with a perfect test condition for the delay
36 // estimation. For now, verify that the produced output delay is always
37 // larger than the selected buffer size.
38 return arg.hardware_delay_bytes > value.hardware_delay_bytes;
39 }
40
41 class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
42 public:
43 MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream,
44 uint8* dest,
45 uint32 max_size,
46 AudioBuffersState buffers_state));
47 MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
48 };
49
50 // This audio source implementation should be used for manual tests only since
51 // it takes about 20 seconds to play out a file.
52 class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback {
53 public:
54 explicit ReadFromFileAudioSource(const std::string& name) : pos_(0) {
55 // Reads a test file from media/test/data directory and stores it in
56 // a scoped_array.
57 ReadTestDataFile(name, &file_, &file_size_);
58 file_size_ = file_size_;
59 }
60
61 virtual ~ReadFromFileAudioSource() {}
62
63 // AudioOutputStream::AudioSourceCallback implementation.
64 virtual uint32 OnMoreData(AudioOutputStream* stream,
65 uint8* dest,
66 uint32 max_size,
67 AudioBuffersState buffers_state) {
68 if (pos_ + static_cast<int>(max_size) > file_size_)
69 max_size = file_size_ - pos_;
70 if (max_size) {
71 memcpy(dest, &file_[pos_], max_size);
72 pos_ += max_size;
73 }
74 return max_size;
75 }
76
77 virtual void OnError(AudioOutputStream* stream, int code) {}
78
79 int file_size() { return file_size_; }
80
81 private:
82 scoped_array<uint8> file_;
83 int file_size_;
84 int pos_;
85 };
86
87 // Convenience method which ensures that we are not running on the build
88 // bots and that at least one valid output device can be found.
89 static bool CanRunAudioTests() {
90 scoped_ptr<base::Environment> env(base::Environment::Create());
91 if (env->HasVar("CHROME_HEADLESS"))
92 return false;
93 AudioManager* audio_man = AudioManager::GetAudioManager();
94 if (NULL == audio_man)
95 return false;
96 // TODO(henrika): note that we use Wave today to query the number of
97 // existing output devices.
98 return audio_man->HasAudioOutputDevices();
99 }
100
101 // Convenience method which creates a default AudioOutputStream object but
102 // also allows the user to modify the default settings.
103 class AudioOutputStreamWrapper {
104 public:
105 AudioOutputStreamWrapper()
106 : com_init_(ScopedCOMInitializer::kMTA),
107 audio_man_(AudioManager::GetAudioManager()),
108 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
109 channel_layout_(CHANNEL_LAYOUT_STEREO),
110 bits_per_sample_(16) {
111 // Use native/mixing sample rate and 10ms frame size as default.
112 sample_rate_ = static_cast<int>(
113 WASAPIAudioOutputStream::HardwareSampleRate(eConsole));
114 samples_per_packet_ = sample_rate_ / 100;
115 DCHECK(sample_rate_);
116 }
117
118 ~AudioOutputStreamWrapper() {}
119
120 // Creates AudioOutputStream object using default parameters.
121 AudioOutputStream* Create() {
122 return CreateOutputStream();
123 }
124
125 // Creates AudioOutputStream object using non-default parameters where the
126 // frame size is modified.
127 AudioOutputStream* Create(int samples_per_packet) {
128 samples_per_packet_ = samples_per_packet;
129 return CreateOutputStream();
130 }
131
132 // Creates AudioOutputStream object using non-default parameters where the
133 // channel layout is modified.
134 AudioOutputStream* Create(ChannelLayout channel_layout) {
135 channel_layout_ = channel_layout;
136 return CreateOutputStream();
137 }
138
139 AudioParameters::Format format() const { return format_; }
140 int channels() const { return ChannelLayoutToChannelCount(channel_layout_); }
141 int bits_per_sample() const { return bits_per_sample_; }
142 int sample_rate() const { return sample_rate_; }
143 int samples_per_packet() const { return samples_per_packet_; }
144
145 private:
146 AudioOutputStream* CreateOutputStream() {
147 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(
148 AudioParameters(format_, channel_layout_, sample_rate_,
149 bits_per_sample_, samples_per_packet_));
150 EXPECT_TRUE(aos);
151 return aos;
152 }
153
154 ScopedCOMInitializer com_init_;
155 AudioManager* audio_man_;
156 AudioParameters::Format format_;
157 ChannelLayout channel_layout_;
158 int bits_per_sample_;
159 int sample_rate_;
160 int samples_per_packet_;
161 };
162
163 // Convenience method which creates a default AudioOutputStream object.
164 static AudioOutputStream* CreateDefaultAudioOutputStream() {
165 AudioOutputStreamWrapper aosw;
166 AudioOutputStream* aos = aosw.Create();
167 return aos;
168 }
169
170 // Verify that we can retrieve the current hardware/mixing sample rate
171 // for all supported device roles. The ERole enumeration defines constants
172 // that indicate the role that the system/user has assigned to an audio
173 // endpoint device.
174 // TODO(henrika): modify this test when we support full device enumeration.
175 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestHardwareSampleRate) {
176 if (!CanRunAudioTests())
177 return;
178
179 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
180
181 // Default device intended for games, system notification sounds,
182 // and voice commands.
183 int fs = static_cast<int>(
184 WASAPIAudioOutputStream::HardwareSampleRate(eConsole));
185 EXPECT_GE(fs, 0);
186
187 // Default communication device intended for e.g. VoIP communication.
188 fs = static_cast<int>(
189 WASAPIAudioOutputStream::HardwareSampleRate(eCommunications));
190 EXPECT_GE(fs, 0);
191
192 // Multimedia device for music, movies and live music recording.
193 fs = static_cast<int>(
194 WASAPIAudioOutputStream::HardwareSampleRate(eMultimedia));
195 EXPECT_GE(fs, 0);
196 }
197
198 // Test Create(), Close() calling sequence.
199 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestCreateAndClose) {
200 if (!CanRunAudioTests())
201 return;
202 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
203 aos->Close();
204 }
205
206 // Test Open(), Close() calling sequence.
207 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestOpenAndClose) {
208 if (!CanRunAudioTests())
209 return;
210 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
211 EXPECT_TRUE(aos->Open());
212 aos->Close();
213 }
214
215 // Test Open(), Start(), Close() calling sequence.
216 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestOpenStartAndClose) {
217 if (!CanRunAudioTests())
218 return;
219 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
220 EXPECT_TRUE(aos->Open());
221 MockAudioSourceCallback source;
222 EXPECT_CALL(source, OnError(aos, _))
223 .Times(0);
224 aos->Start(&source);
225 aos->Close();
226 }
227
228 // Test Open(), Start(), Stop(), Close() calling sequence.
229 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestOpenStartStopAndClose) {
230 if (!CanRunAudioTests())
231 return;
232 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
233 EXPECT_TRUE(aos->Open());
234 MockAudioSourceCallback source;
235 EXPECT_CALL(source, OnError(aos, _))
236 .Times(0);
237 aos->Start(&source);
238 aos->Stop();
239 aos->Close();
240 }
241
242 // Test SetVolume(), GetVolume()
243 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestVolume) {
244 if (!CanRunAudioTests())
245 return;
246 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
247
248 // Initial volume should be full volume (1.0).
249 double volume = 0.0;
250 aos->GetVolume(&volume);
251 EXPECT_TRUE(volume == 1.0);
252
253 // Verify some valid volume settings.
254 aos->SetVolume(0.0);
255 aos->GetVolume(&volume);
256 EXPECT_TRUE(volume == 0.0);
257
258 aos->SetVolume(0.5);
259 aos->GetVolume(&volume);
260 EXPECT_TRUE(volume == 0.5);
261
262 aos->SetVolume(1.0);
263 aos->GetVolume(&volume);
264 EXPECT_TRUE(volume == 1.0);
265
266 // Ensure that invalid volume setting have no effect.
267 aos->SetVolume(1.5);
268 aos->GetVolume(&volume);
269 EXPECT_TRUE(volume == 1.0);
270
271 aos->SetVolume(-0.5);
272 aos->GetVolume(&volume);
273 EXPECT_TRUE(volume == 1.0);
274
275 aos->Close();
276 }
277
278 // Test some additional calling sequences.
279 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestMiscCallingSequences) {
280 if (!CanRunAudioTests())
281 return;
282 AudioOutputStream* aos = CreateDefaultAudioOutputStream();
283 WASAPIAudioOutputStream* waos = static_cast<WASAPIAudioOutputStream*>(aos);
284
285 // Open(), Open() should fail the second time.
286 EXPECT_TRUE(aos->Open());
287 EXPECT_FALSE(aos->Open());
288
289 MockAudioSourceCallback source;
290
291 // Start(), Start() is a valid calling sequence (second call does nothing).
292 aos->Start(&source);
293 EXPECT_TRUE(waos->started());
294 aos->Start(&source);
295 EXPECT_TRUE(waos->started());
296
297 // Stop(), Stop() is a valid calling sequence (second call does nothing).
298 aos->Stop();
299 EXPECT_FALSE(waos->started());
300 aos->Stop();
301 EXPECT_FALSE(waos->started());
302
303 aos->Close();
304 }
305
306 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestPacketSizes) {
307 if (!CanRunAudioTests())
308 return;
309
310 // 10 ms packet size.
311
312 // Create default WASAPI output stream which plays out in stereo using
313 // the shared mixing rate. The default buffer size is 10ms.
314 AudioOutputStreamWrapper aosw;
315 AudioOutputStream* aos = aosw.Create();
316 EXPECT_TRUE(aos->Open());
317
318 // Derive the expected size in bytes of each packet.
319 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
320 (aosw.bits_per_sample() / 8);
321
322 // Set up expected minimum delay estimation.
323 AudioBuffersState state(0, bytes_per_packet);
324
325 MockAudioSourceCallback source;
326
327 // We use 10ms packets and will run the test for ~100ms. Given that the
328 // startup sequence takes some time, it is reasonable to expect 5-12
329 // callbacks in this time period. All should ask for the same size and
330 // contain a valid delay estimate.
331 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
332 HasValidDelay(state)))
333 .Times(Between(5, 10))
334 .WillRepeatedly(Return(bytes_per_packet));
335
336 aos->Start(&source);
337 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
338 aos->Stop();
339
340 // Store current packet size (to be used in the subsequent tests).
341 int samples_per_packet_10ms = aosw.samples_per_packet();
342
343 aos->Close();
344
345 // 20 ms packet size.
346
347 aos = aosw.Create(2 * samples_per_packet_10ms);
348 EXPECT_TRUE(aos->Open());
349 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
350 (aosw.bits_per_sample() / 8);
351
352 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
353 HasValidDelay(state)))
354 .Times(Between(5, 10))
355 .WillRepeatedly(Return(bytes_per_packet));
356
357 aos->Start(&source);
358 base::PlatformThread::Sleep(2 * TestTimeouts::tiny_timeout_ms());
Paweł Hajdan Jr. 2011/11/02 09:36:59 Please don't multiply values received from TestTim
henrika (OOO until Aug 14) 2011/11/03 15:03:35 Modified the design. Now waits for one callback in
359 aos->Stop();
360
361 aos->Close();
362
363 // 40 ms packet size.
364
365 aos = aosw.Create(4 * samples_per_packet_10ms);
366 EXPECT_TRUE(aos->Open());
367 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
368 (aosw.bits_per_sample() / 8);
369
370 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
371 HasValidDelay(state)))
372 .Times(Between(5, 10))
373 .WillRepeatedly(Return(bytes_per_packet));
374
375 aos->Start(&source);
376 base::PlatformThread::Sleep(4 * TestTimeouts::tiny_timeout_ms());
377 aos->Stop();
378
379 aos->Close();
380
381 // 50 ms packet size.
382
383 aos = aosw.Create(samples_per_packet_10ms * 5);
384 EXPECT_TRUE(aos->Open());
385 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
386 (aosw.bits_per_sample() / 8);
387
388 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
389 HasValidDelay(state)))
390 .Times(Between(5, 10))
391 .WillRepeatedly(Return(bytes_per_packet));
392
393 aos->Start(&source);
394 base::PlatformThread::Sleep(5 * TestTimeouts::tiny_timeout_ms());
395 aos->Stop();
396
397 aos->Close();
398
399 // 5 ms packet size.
400
401 aos = aosw.Create(samples_per_packet_10ms / 2);
402 EXPECT_TRUE(aos->Open());
403 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
404 (aosw.bits_per_sample() / 8);
405
406 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
407 HasValidDelay(state)))
408 .Times(Between(2 * 5, 2 * 10))
409 .WillRepeatedly(Return(bytes_per_packet));
410
411 aos->Start(&source);
412 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
413 aos->Stop();
414
415 aos->Close();
416
417 // 512 samples (independent of sample rate)
418
419 aos = aosw.Create(512);
420 EXPECT_TRUE(aos->Open());
421 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
422 (aosw.bits_per_sample() / 8);
423
424 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
425 HasValidDelay(state)))
426 .WillRepeatedly(Return(bytes_per_packet));
427
428 aos->Start(&source);
429 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
430 aos->Stop();
431
432 aos->Close();
433 }
434
435 TEST(WinAudioOutputTest, WASAPIAudioOutputStreamTestMonoStereo) {
436 if (!CanRunAudioTests())
437 return;
438
439 // CHANNEL_LAYOUT_MONO
440
441 // Create default WASAPI output stream which plays out in *mono* using
442 // the shared mixing rate. The default buffer size is 10ms.
443 AudioOutputStreamWrapper aosw;
444 AudioOutputStream* aos = aosw.Create(CHANNEL_LAYOUT_MONO);
445 EXPECT_TRUE(aos->Open());
446
447 // Derive the expected size in bytes of each packet.
448 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
449 (aosw.bits_per_sample() / 8);
450
451 // Set up expected minimum delay estimation.
452 AudioBuffersState state(0, bytes_per_packet);
453
454 MockAudioSourceCallback source;
455
456 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
457 HasValidDelay(state)))
458 .Times(Between(5, 10))
459 .WillRepeatedly(Return(bytes_per_packet));
460
461 aos->Start(&source);
462 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
463 aos->Stop();
464 aos->Close();
465
466 // CHANNEL_LAYOUT_STEREO
467
468 // Create default WASAPI output stream which plays out in *stereo* using
469 // the shared mixing rate. The default buffer size is 10ms.
470 aos = aosw.Create(CHANNEL_LAYOUT_STEREO);
471 EXPECT_TRUE(aos->Open());
472
473 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
474 (aosw.bits_per_sample() / 8);
475
476 state.pending_bytes = 0;
477 state.hardware_delay_bytes = bytes_per_packet;
478
479 EXPECT_CALL(source, OnMoreData(aos, NotNull(), bytes_per_packet,
480 HasValidDelay(state)))
481 .Times(Between(5, 10))
482 .WillRepeatedly(Return(bytes_per_packet));
483
484 aos->Start(&source);
485 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
486 aos->Stop();
487 aos->Close();
488 }
489
490 // This test is intended for manual tests and should only be enabled
491 // when it is required to store the captured data on a local file.
492 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
493 // To include disabled tests in test execution, just invoke the test program
494 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
495 // environment variable to a value greater than 0.
496 // The test files are approximately 20 seconds long.
497 TEST(WinAudioOutputTest, DISABLED_WASAPIAudioOutputStreamReadFromFile) {
498 if (!CanRunAudioTests())
499 return;
500
501 AudioOutputStreamWrapper aosw;
502 AudioOutputStream* aos = aosw.Create();
503 EXPECT_TRUE(aos->Open());
504
505 std::string file_name;
506 if (aosw.sample_rate() == 48000) {
507 file_name = kSpeechFile_16b_s_48k;
508 } else if (aosw.sample_rate() == 44100) {
509 file_name = kSpeechFile_16b_s_44k;
510 } else {
511 fprintf(stderr, "This test supports 44.1 and 48kHz only.\n");
512 return;
513 }
514
515 ReadFromFileAudioSource file_source(file_name);
516 fprintf(stderr, " File name : %s\n", file_name.c_str());
517 fprintf(stderr, " Sample rate: %d\n", aosw.sample_rate());
518 fprintf(stderr, " File size : %d\n", file_source.file_size());
519 fprintf(stderr, " >> Listen to the file while playing...\n");
520 aos->Start(&file_source);
521 base::PlatformThread::Sleep(2 * TestTimeouts::action_timeout_ms());
522 aos->Stop();
523 fprintf(stderr, " >> File playout has stopped.\n");
524 aos->Close();
525 }
526
527 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/win/audio_low_latency_output_win.cc ('k') | media/audio/win/audio_manager_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698