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

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

Issue 12049070: Avoids irregular OnMoreData callbacks on Windows using Core Audio (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: cleaned up Created 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <windows.h> 5 #include <windows.h>
6 #include <mmsystem.h> 6 #include <mmsystem.h>
7 7
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/environment.h" 9 #include "base/environment.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
(...skipping 10 matching lines...) Expand all
21 #include "media/audio/win/core_audio_util_win.h" 21 #include "media/audio/win/core_audio_util_win.h"
22 #include "media/base/decoder_buffer.h" 22 #include "media/base/decoder_buffer.h"
23 #include "media/base/seekable_buffer.h" 23 #include "media/base/seekable_buffer.h"
24 #include "media/base/test_data_util.h" 24 #include "media/base/test_data_util.h"
25 #include "testing/gmock_mutant.h" 25 #include "testing/gmock_mutant.h"
26 #include "testing/gmock/include/gmock/gmock.h" 26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h" 27 #include "testing/gtest/include/gtest/gtest.h"
28 28
29 using ::testing::_; 29 using ::testing::_;
30 using ::testing::AnyNumber; 30 using ::testing::AnyNumber;
31 using ::testing::AtLeast;
31 using ::testing::Between; 32 using ::testing::Between;
32 using ::testing::CreateFunctor; 33 using ::testing::CreateFunctor;
33 using ::testing::DoAll; 34 using ::testing::DoAll;
34 using ::testing::Gt; 35 using ::testing::Gt;
35 using ::testing::InvokeWithoutArgs; 36 using ::testing::InvokeWithoutArgs;
36 using ::testing::NotNull; 37 using ::testing::NotNull;
37 using ::testing::Return; 38 using ::testing::Return;
38 using base::win::ScopedCOMInitializer; 39 using base::win::ScopedCOMInitializer;
39 40
40 namespace media { 41 namespace media {
41 42
42 static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw"; 43 static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw";
43 static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw"; 44 static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw";
44 static const size_t kFileDurationMs = 20000; 45 static const size_t kFileDurationMs = 20000;
45 static const size_t kNumFileSegments = 2; 46 static const size_t kNumFileSegments = 2;
46 static const int kBitsPerSample = 16; 47 static const int kBitsPerSample = 16;
47 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
48 static const size_t kMaxDeltaSamples = 1000; 48 static const size_t kMaxDeltaSamples = 1000;
49 static const char* kDeltaTimeMsFileName = "delta_times_ms.txt"; 49 static const char* kDeltaTimeMsFileName = "delta_times_ms.txt";
50 50
51 MATCHER_P(HasValidDelay, value, "") { 51 MATCHER_P(HasValidDelay, value, "") {
52 // It is difficult to come up with a perfect test condition for the delay 52 // It is difficult to come up with a perfect test condition for the delay
53 // estimation. For now, verify that the produced output delay is always 53 // estimation. For now, verify that the produced output delay is always
54 // larger than the selected buffer size. 54 // larger than the selected buffer size.
55 return arg.hardware_delay_bytes > value.hardware_delay_bytes; 55 return arg.hardware_delay_bytes >= value.hardware_delay_bytes;
56 } 56 }
57 57
58 // Used to terminate a loop from a different thread than the loop belongs to. 58 // Used to terminate a loop from a different thread than the loop belongs to.
59 // |loop| should be a MessageLoopProxy. 59 // |loop| should be a MessageLoopProxy.
60 ACTION_P(QuitLoop, loop) { 60 ACTION_P(QuitLoop, loop) {
61 loop->PostTask(FROM_HERE, MessageLoop::QuitClosure()); 61 loop->PostTask(FROM_HERE, MessageLoop::QuitClosure());
62 } 62 }
63 63
64 class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { 64 class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
65 public: 65 public:
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 return false; 174 return false;
175 } 175 }
176 176
177 // TODO(henrika): note that we use Wave today to query the number of 177 // TODO(henrika): note that we use Wave today to query the number of
178 // existing output devices. 178 // existing output devices.
179 if (!audio_man->HasAudioOutputDevices()) { 179 if (!audio_man->HasAudioOutputDevices()) {
180 LOG(WARNING) << "No output devices detected."; 180 LOG(WARNING) << "No output devices detected.";
181 return false; 181 return false;
182 } 182 }
183 183
184 if (WASAPIAudioOutputStream::HardwareChannelLayout() != kChannelLayout) {
185 LOG(WARNING) << "This test requires stereo audio output.";
186 return false;
187 }
188
189 return true; 184 return true;
190 } 185 }
191 186
192 // Convenience method which creates a default AudioOutputStream object but 187 // Convenience method which creates a default AudioOutputStream object but
193 // also allows the user to modify the default settings. 188 // also allows the user to modify the default settings.
194 class AudioOutputStreamWrapper { 189 class AudioOutputStreamWrapper {
195 public: 190 public:
196 explicit AudioOutputStreamWrapper(AudioManager* audio_manager) 191 explicit AudioOutputStreamWrapper(AudioManager* audio_manager)
197 : com_init_(ScopedCOMInitializer::kMTA), 192 : audio_man_(audio_manager),
198 audio_man_(audio_manager),
199 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), 193 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
200 channel_layout_(kChannelLayout),
201 bits_per_sample_(kBitsPerSample) { 194 bits_per_sample_(kBitsPerSample) {
202 // Use native/mixing sample rate and 10ms frame size as default. 195 AudioParameters preferred_params;
203 sample_rate_ = static_cast<int>( 196 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
204 WASAPIAudioOutputStream::HardwareSampleRate(eConsole)); 197 eRender, eConsole, &preferred_params)));
205 samples_per_packet_ = sample_rate_ / 100; 198 channel_layout_ = preferred_params.channel_layout();
206 DCHECK(sample_rate_); 199 sample_rate_ = preferred_params.sample_rate();
200 samples_per_packet_ = preferred_params.frames_per_buffer();
207 } 201 }
208 202
209 ~AudioOutputStreamWrapper() {} 203 ~AudioOutputStreamWrapper() {}
210 204
211 // Creates AudioOutputStream object using default parameters. 205 // Creates AudioOutputStream object using default parameters.
212 AudioOutputStream* Create() { 206 AudioOutputStream* Create() {
213 return CreateOutputStream(); 207 return CreateOutputStream();
214 } 208 }
215 209
216 // Creates AudioOutputStream object using non-default parameters where the 210 // Creates AudioOutputStream object using non-default parameters where the
(...skipping 19 matching lines...) Expand all
236 230
237 private: 231 private:
238 AudioOutputStream* CreateOutputStream() { 232 AudioOutputStream* CreateOutputStream() {
239 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( 233 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(
240 AudioParameters(format_, channel_layout_, sample_rate_, 234 AudioParameters(format_, channel_layout_, sample_rate_,
241 bits_per_sample_, samples_per_packet_)); 235 bits_per_sample_, samples_per_packet_));
242 EXPECT_TRUE(aos); 236 EXPECT_TRUE(aos);
243 return aos; 237 return aos;
244 } 238 }
245 239
246 ScopedCOMInitializer com_init_;
tommi (sloooow) - chröme 2013/01/31 13:42:08 where is COM now initialized?
henrika (OOO until Aug 14) 2013/01/31 14:29:38 AudioManagerBase::AudioManagerBase() does that for
247 AudioManager* audio_man_; 240 AudioManager* audio_man_;
248 AudioParameters::Format format_; 241 AudioParameters::Format format_;
249 ChannelLayout channel_layout_; 242 ChannelLayout channel_layout_;
250 int bits_per_sample_; 243 int bits_per_sample_;
251 int sample_rate_; 244 int sample_rate_;
252 int samples_per_packet_; 245 int samples_per_packet_;
253 }; 246 };
254 247
255 // Convenience method which creates a default AudioOutputStream object. 248 // Convenience method which creates a default AudioOutputStream object.
256 static AudioOutputStream* CreateDefaultAudioOutputStream( 249 static AudioOutputStream* CreateDefaultAudioOutputStream(
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 aos->Stop(); 425 aos->Stop();
433 EXPECT_FALSE(waos->started()); 426 EXPECT_FALSE(waos->started());
434 aos->Start(&source); 427 aos->Start(&source);
435 EXPECT_TRUE(waos->started()); 428 EXPECT_TRUE(waos->started());
436 aos->Stop(); 429 aos->Stop();
437 EXPECT_FALSE(waos->started()); 430 EXPECT_FALSE(waos->started());
438 431
439 aos->Close(); 432 aos->Close();
440 } 433 }
441 434
442 // Use default packet size (10ms) and verify that rendering starts. 435 // Use preferred packet size and verify that rendering starts.
443 TEST(WASAPIAudioOutputStreamTest, PacketSizeInMilliseconds) { 436 TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) {
444 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); 437 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
445 if (!CanRunAudioTests(audio_manager.get())) 438 if (!CanRunAudioTests(audio_manager.get()))
446 return; 439 return;
447 440
448 MessageLoopForUI loop; 441 MessageLoopForUI loop;
449 MockAudioSourceCallback source; 442 MockAudioSourceCallback source;
450 443
451 // Create default WASAPI output stream which plays out in stereo using 444 // Create default WASAPI output stream which plays out in stereo using
452 // the shared mixing rate. The default buffer size is 10ms. 445 // the shared mixing rate. The default buffer size is 10ms.
453 AudioOutputStreamWrapper aosw(audio_manager.get()); 446 AudioOutputStreamWrapper aosw(audio_manager.get());
(...skipping 14 matching lines...) Expand all
468 Return(aosw.samples_per_packet()))); 461 Return(aosw.samples_per_packet())));
469 462
470 aos->Start(&source); 463 aos->Start(&source);
471 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), 464 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
472 TestTimeouts::action_timeout()); 465 TestTimeouts::action_timeout());
473 loop.Run(); 466 loop.Run();
474 aos->Stop(); 467 aos->Stop();
475 aos->Close(); 468 aos->Close();
476 } 469 }
477 470
478 // Use a fixed packets size (independent of sample rate) and verify 471 // Use a non-preferred packet size and verify that Open() fails.
479 // that rendering starts. 472 TEST(WASAPIAudioOutputStreamTest, InvalidPacketSize) {
480 TEST(WASAPIAudioOutputStreamTest, PacketSizeInSamples) {
481 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); 473 scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
482 if (!CanRunAudioTests(audio_manager.get())) 474 if (!CanRunAudioTests(audio_manager.get()))
483 return; 475 return;
484 476
485 MessageLoopForUI loop; 477 if (ExclusiveModeIsEnabled())
486 MockAudioSourceCallback source; 478 return;
487 479
488 // Create default WASAPI output stream which reads data in stereo using 480 AudioParameters preferred_params;
489 // the native mixing rate and channel count. The buffer size is set to 481 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
490 // 1024 samples. 482 eRender, eConsole, &preferred_params)));
483 int too_large_packet_size = 2 * preferred_params.frames_per_buffer();
484
491 AudioOutputStreamWrapper aosw(audio_manager.get()); 485 AudioOutputStreamWrapper aosw(audio_manager.get());
492 AudioOutputStream* aos = aosw.Create(1024); 486 AudioOutputStream* aos = aosw.Create(too_large_packet_size);
493 EXPECT_TRUE(aos->Open()); 487 EXPECT_FALSE(aos->Open());
494 488
495 // Derive the expected size in bytes of each packet.
496 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
497 (aosw.bits_per_sample() / 8);
498
499 // Set up expected minimum delay estimation.
500 AudioBuffersState state(0, bytes_per_packet);
501
502 // Ensure that callbacks start correctly.
503 EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state)))
504 .WillOnce(DoAll(
505 QuitLoop(loop.message_loop_proxy()),
506 Return(aosw.samples_per_packet())))
507 .WillRepeatedly(Return(aosw.samples_per_packet()));
508
509 aos->Start(&source);
510 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
511 TestTimeouts::action_timeout());
512 loop.Run();
513 aos->Stop();
514 aos->Close(); 489 aos->Close();
515 } 490 }
516 491
517 // This test is intended for manual tests and should only be enabled 492 // This test is intended for manual tests and should only be enabled
518 // when it is required to play out data from a local PCM file. 493 // when it is required to play out data from a local PCM file.
519 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. 494 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
520 // To include disabled tests in test execution, just invoke the test program 495 // To include disabled tests in test execution, just invoke the test program
521 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS 496 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
522 // environment variable to a value greater than 0. 497 // environment variable to a value greater than 0.
523 // The test files are approximately 20 seconds long. 498 // The test files are approximately 20 seconds long.
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 AudioOutputStream* aos = aosw.Create(48000, 160); 672 AudioOutputStream* aos = aosw.Create(48000, 160);
698 EXPECT_TRUE(aos->Open()); 673 EXPECT_TRUE(aos->Open());
699 674
700 // Derive the expected size in bytes of each packet. 675 // Derive the expected size in bytes of each packet.
701 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * 676 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() *
702 (aosw.bits_per_sample() / 8); 677 (aosw.bits_per_sample() / 8);
703 678
704 // Set up expected minimum delay estimation. 679 // Set up expected minimum delay estimation.
705 AudioBuffersState state(0, bytes_per_packet); 680 AudioBuffersState state(0, bytes_per_packet);
706 681
707 // Wait for the first callback and verify its parameters. 682 // Wait for the first callback and verify its parameters.
708 EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) 683 EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state)))
709 .WillOnce(DoAll( 684 .WillOnce(DoAll(
710 QuitLoop(loop.message_loop_proxy()), 685 QuitLoop(loop.message_loop_proxy()),
711 Return(aosw.samples_per_packet()))) 686 Return(aosw.samples_per_packet())))
712 .WillRepeatedly(Return(aosw.samples_per_packet())); 687 .WillRepeatedly(Return(aosw.samples_per_packet()));
713 688
714 aos->Start(&source); 689 aos->Start(&source);
715 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), 690 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
716 TestTimeouts::action_timeout()); 691 TestTimeouts::action_timeout());
717 loop.Run(); 692 loop.Run();
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 729
755 aos->Start(&source); 730 aos->Start(&source);
756 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), 731 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
757 TestTimeouts::action_timeout()); 732 TestTimeouts::action_timeout());
758 loop.Run(); 733 loop.Run();
759 aos->Stop(); 734 aos->Stop();
760 aos->Close(); 735 aos->Close();
761 } 736 }
762 737
763 } // namespace media 738 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698