Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/command_line.h" | |
| 9 #include "base/environment.h" | 10 #include "base/environment.h" |
| 10 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "media/base/media_switches.h" | |
| 11 #include "base/memory/scoped_ptr.h" | 13 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 13 #include "base/test/test_timeouts.h" | 15 #include "base/test/test_timeouts.h" |
| 14 #include "base/time.h" | 16 #include "base/time.h" |
| 15 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 16 #include "base/win/scoped_com_initializer.h" | 18 #include "base/win/scoped_com_initializer.h" |
| 17 #include "media/audio/audio_io.h" | 19 #include "media/audio/audio_io.h" |
| 18 #include "media/audio/audio_manager.h" | 20 #include "media/audio/audio_manager.h" |
| 19 #include "media/audio/audio_util.h" | 21 #include "media/audio/audio_util.h" |
| 20 #include "media/audio/win/audio_low_latency_output_win.h" | 22 #include "media/audio/win/audio_low_latency_output_win.h" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 34 using ::testing::InvokeWithoutArgs; | 36 using ::testing::InvokeWithoutArgs; |
| 35 using ::testing::NotNull; | 37 using ::testing::NotNull; |
| 36 using ::testing::Return; | 38 using ::testing::Return; |
| 37 using base::win::ScopedCOMInitializer; | 39 using base::win::ScopedCOMInitializer; |
| 38 | 40 |
| 39 namespace media { | 41 namespace media { |
| 40 | 42 |
| 41 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"; |
| 42 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"; |
| 43 static const size_t kFileDurationMs = 20000; | 45 static const size_t kFileDurationMs = 20000; |
| 44 static const size_t kNumFileSegments = 1; | 46 static const size_t kNumFileSegments = 2; |
| 45 | 47 |
| 46 static const size_t kMaxDeltaSamples = 1000; | 48 static const size_t kMaxDeltaSamples = 1000; |
| 47 static const char* kDeltaTimeMsFileName = "delta_times_ms.txt"; | 49 static const char* kDeltaTimeMsFileName = "delta_times_ms.txt"; |
| 48 | 50 |
| 49 MATCHER_P(HasValidDelay, value, "") { | 51 MATCHER_P(HasValidDelay, value, "") { |
| 50 // 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 |
| 51 // estimation. For now, verify that the produced output delay is always | 53 // estimation. For now, verify that the produced output delay is always |
| 52 // larger than the selected buffer size. | 54 // larger than the selected buffer size. |
| 53 return arg.hardware_delay_bytes > value.hardware_delay_bytes; | 55 return arg.hardware_delay_bytes > value.hardware_delay_bytes; |
| 54 } | 56 } |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 | 133 |
| 132 private: | 134 private: |
| 133 scoped_refptr<DecoderBuffer> file_; | 135 scoped_refptr<DecoderBuffer> file_; |
| 134 scoped_array<int> delta_times_; | 136 scoped_array<int> delta_times_; |
| 135 int pos_; | 137 int pos_; |
| 136 base::Time previous_call_time_; | 138 base::Time previous_call_time_; |
| 137 FILE* text_file_; | 139 FILE* text_file_; |
| 138 size_t elements_to_write_; | 140 size_t elements_to_write_; |
| 139 }; | 141 }; |
| 140 | 142 |
| 143 static bool ExclusiveModeIsEnabled() { | |
| 144 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
| 145 return (cmd_line->HasSwitch(switches::kEnableExclusiveMode)); | |
| 146 } | |
| 147 | |
| 141 // Convenience method which ensures that we are not running on the build | 148 // Convenience method which ensures that we are not running on the build |
| 142 // bots and that at least one valid output device can be found. We also | 149 // bots and that at least one valid output device can be found. We also |
| 143 // verify that we are not running on XP since the low-latency (WASAPI- | 150 // verify that we are not running on XP since the low-latency (WASAPI- |
| 144 // based) version requires Windows Vista or higher. | 151 // based) version requires Windows Vista or higher. |
| 145 static bool CanRunAudioTests(AudioManager* audio_man) { | 152 static bool CanRunAudioTests(AudioManager* audio_man) { |
| 146 if (!media::IsWASAPISupported()) { | 153 if (!media::IsWASAPISupported()) { |
| 147 LOG(WARNING) << "This tests requires Windows Vista or higher."; | 154 LOG(WARNING) << "This tests requires Windows Vista or higher."; |
| 148 return false; | 155 return false; |
| 149 } | 156 } |
| 150 // TODO(henrika): note that we use Wave today to query the number of | 157 // TODO(henrika): note that we use Wave today to query the number of |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 179 } | 186 } |
| 180 | 187 |
| 181 // Creates AudioOutputStream object using non-default parameters where the | 188 // Creates AudioOutputStream object using non-default parameters where the |
| 182 // frame size is modified. | 189 // frame size is modified. |
| 183 AudioOutputStream* Create(int samples_per_packet) { | 190 AudioOutputStream* Create(int samples_per_packet) { |
| 184 samples_per_packet_ = samples_per_packet; | 191 samples_per_packet_ = samples_per_packet; |
| 185 return CreateOutputStream(); | 192 return CreateOutputStream(); |
| 186 } | 193 } |
| 187 | 194 |
| 188 // Creates AudioOutputStream object using non-default parameters where the | 195 // Creates AudioOutputStream object using non-default parameters where the |
| 196 // sample rate and frame size are modified. | |
| 197 AudioOutputStream* Create(int sample_rate, int samples_per_packet) { | |
| 198 sample_rate_ = sample_rate; | |
| 199 samples_per_packet_ = samples_per_packet; | |
| 200 return CreateOutputStream(); | |
| 201 } | |
| 202 | |
| 203 // Creates AudioOutputStream object using non-default parameters where the | |
| 189 // channel layout is modified. | 204 // channel layout is modified. |
| 190 AudioOutputStream* Create(ChannelLayout channel_layout) { | 205 AudioOutputStream* Create(ChannelLayout channel_layout) { |
| 191 channel_layout_ = channel_layout; | 206 channel_layout_ = channel_layout; |
| 192 return CreateOutputStream(); | 207 return CreateOutputStream(); |
| 193 } | 208 } |
| 194 | 209 |
| 195 AudioParameters::Format format() const { return format_; } | 210 AudioParameters::Format format() const { return format_; } |
| 196 int channels() const { return ChannelLayoutToChannelCount(channel_layout_); } | 211 int channels() const { return ChannelLayoutToChannelCount(channel_layout_); } |
| 197 int bits_per_sample() const { return bits_per_sample_; } | 212 int bits_per_sample() const { return bits_per_sample_; } |
| 198 int sample_rate() const { return sample_rate_; } | 213 int sample_rate() const { return sample_rate_; } |
| 199 int samples_per_packet() const { return samples_per_packet_; } | 214 int samples_per_packet() const { return samples_per_packet_; } |
| 200 | 215 |
| 201 private: | 216 private: |
| 202 AudioOutputStream* CreateOutputStream() { | 217 AudioOutputStream* CreateOutputStream() { |
| 203 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( | 218 AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( |
|
scherkus (not reviewing)
2012/06/28 00:04:41
if you change this to create a WASAPIAudioOutputSt
henrika (OOO until Aug 14)
2012/07/25 11:18:22
I explained this in a separate e-mail discussion b
| |
| 204 AudioParameters(format_, channel_layout_, sample_rate_, | 219 AudioParameters(format_, channel_layout_, sample_rate_, |
| 205 bits_per_sample_, samples_per_packet_)); | 220 bits_per_sample_, samples_per_packet_)); |
| 206 EXPECT_TRUE(aos); | 221 EXPECT_TRUE(aos); |
| 207 return aos; | 222 return aos; |
| 208 } | 223 } |
| 209 | 224 |
| 210 ScopedCOMInitializer com_init_; | 225 ScopedCOMInitializer com_init_; |
| 211 AudioManager* audio_man_; | 226 AudioManager* audio_man_; |
| 212 AudioParameters::Format format_; | 227 AudioParameters::Format format_; |
| 213 ChannelLayout channel_layout_; | 228 ChannelLayout channel_layout_; |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 555 aos->Start(&file_source); | 570 aos->Start(&file_source); |
| 556 base::PlatformThread::Sleep( | 571 base::PlatformThread::Sleep( |
| 557 base::TimeDelta::FromMilliseconds(kFileDurationMs / kNumFileSegments)); | 572 base::TimeDelta::FromMilliseconds(kFileDurationMs / kNumFileSegments)); |
| 558 aos->Stop(); | 573 aos->Stop(); |
| 559 } | 574 } |
| 560 | 575 |
| 561 LOG(INFO) << ">> File playout has stopped."; | 576 LOG(INFO) << ">> File playout has stopped."; |
| 562 aos->Close(); | 577 aos->Close(); |
| 563 } | 578 } |
| 564 | 579 |
| 580 // Verify that we can open the output stream in exclusive mode using a | |
| 581 // certain set of audio parameters and a sample rate of 48kHz. | |
| 582 // The expected outcomes of each setting in this test has been derived | |
| 583 // manually using log outputs (--v=1). | |
| 584 TEST(WinAudioOutputTest, | |
| 585 WASAPIAudioOutputStreamTestExclusiveModeBufferSizesAt48kHzSampleRate) { | |
| 586 if (!ExclusiveModeIsEnabled()) | |
| 587 return; | |
| 588 | |
| 589 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); | |
| 590 if (!CanRunAudioTests(audio_manager.get())) | |
| 591 return; | |
| 592 | |
| 593 AudioOutputStreamWrapper aosw(audio_manager.get()); | |
| 594 | |
| 595 // 10ms @ 48kHz shall work. | |
| 596 // Note that, this is the same size as we can use for shared-mode streaming | |
| 597 // but here the endpoint buffer delay is only 10ms instead of 20ms. | |
| 598 AudioOutputStream* aos = aosw.Create(48000, 480); | |
| 599 EXPECT_TRUE(aos->Open()); | |
| 600 aos->Close(); | |
| 601 | |
| 602 // 5ms @ 48kHz does not work to misalignment. | |
| 603 // This test will propose an aligned buffer size of 5.3333ms. | |
| 604 aos = aosw.Create(48000, 240); | |
| 605 EXPECT_FALSE(aos->Open()); | |
| 606 aos->Close(); | |
| 607 | |
| 608 // 5.3333ms @ 48kHz shall work (see test above). | |
| 609 aos = aosw.Create(48000, 256); | |
| 610 EXPECT_TRUE(aos->Open()); | |
| 611 aos->Close(); | |
| 612 | |
| 613 // 2.6667ms is smaller than the minimum supported size (=3ms). | |
| 614 aos = aosw.Create(48000, 128); | |
| 615 EXPECT_FALSE(aos->Open()); | |
| 616 aos->Close(); | |
| 617 | |
| 618 // 3ms does not correspond to an aligned buffer size. | |
| 619 // This test will propose an aligned buffer size of 3.3333ms. | |
| 620 aos = aosw.Create(48000, 144); | |
| 621 EXPECT_FALSE(aos->Open()); | |
| 622 aos->Close(); | |
| 623 | |
| 624 // 3.3333ms @ 48kHz <=> smallest possible buffer size we can use. | |
| 625 aos = aosw.Create(48000, 160); | |
| 626 EXPECT_TRUE(aos->Open()); | |
| 627 aos->Close(); | |
| 628 } | |
| 629 | |
| 630 // Verify that we can open the output stream in exclusive mode using a | |
| 631 // certain set of audio parameters and a sample rate of 44.1kHz. | |
| 632 // The expected outcomes of each setting in this test has been derived | |
| 633 // manually using log outputs (--v=1). | |
| 634 TEST(WinAudioOutputTest, | |
| 635 WASAPIAudioOutputStreamTestExclusiveModeBufferSizesAt44kHzSampleRate) { | |
| 636 if (!ExclusiveModeIsEnabled()) | |
| 637 return; | |
| 638 | |
| 639 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); | |
| 640 if (!CanRunAudioTests(audio_manager.get())) | |
| 641 return; | |
| 642 | |
| 643 AudioOutputStreamWrapper aosw(audio_manager.get()); | |
| 644 | |
| 645 // 10ms @ 44.1kHz does not work due to misalignment. | |
| 646 // This test will propose an aligned buffer size of 10.1587ms. | |
| 647 AudioOutputStream* aos = aosw.Create(44100, 441); | |
| 648 EXPECT_FALSE(aos->Open()); | |
| 649 aos->Close(); | |
| 650 | |
| 651 // 10.1587ms @ 44.1kHz shall work (see test above). | |
| 652 aos = aosw.Create(44100, 448); | |
| 653 EXPECT_TRUE(aos->Open()); | |
| 654 aos->Close(); | |
| 655 | |
| 656 // 5.8050ms @ 44.1 shall work. | |
| 657 aos = aosw.Create(44100, 256); | |
| 658 EXPECT_TRUE(aos->Open()); | |
| 659 aos->Close(); | |
| 660 | |
| 661 // 4.9887ms @ 44.1kHz does not work to misalignment. | |
| 662 // This test will propose an aligned buffer size of 5.0794ms. | |
| 663 aos = aosw.Create(44100, 220); | |
| 664 EXPECT_FALSE(aos->Open()); | |
| 665 aos->Close(); | |
| 666 | |
| 667 // 5.0794ms @ 44.1kHz shall work (see test above). | |
| 668 aos = aosw.Create(44100, 224); | |
| 669 EXPECT_TRUE(aos->Open()); | |
| 670 aos->Close(); | |
| 671 | |
| 672 // 2.9025ms is smaller than the minimum supported size (=3ms). | |
| 673 aos = aosw.Create(44100, 132); | |
| 674 EXPECT_FALSE(aos->Open()); | |
| 675 aos->Close(); | |
| 676 | |
| 677 // 3.01587ms is larger than the minimum size but is not aligned. | |
| 678 // This test will propose an aligned buffer size of 3.6281ms. | |
| 679 aos = aosw.Create(44100, 133); | |
| 680 EXPECT_FALSE(aos->Open()); | |
| 681 aos->Close(); | |
| 682 | |
| 683 // 3.6281ms @ 44.1kHz <=> smallest possible buffer size we can use. | |
| 684 aos = aosw.Create(44100, 160); | |
| 685 EXPECT_TRUE(aos->Open()); | |
| 686 aos->Close(); | |
| 687 } | |
| 688 | |
| 689 // Verify that we can open and start the output stream in exclusive mode at | |
| 690 // the lowest possible delay at 48kHz. | |
| 691 TEST(WinAudioOutputTest, | |
| 692 WASAPIAudioOutputStreamTestExclusiveModeMinBufferSizeAt48kHzSampleRate) { | |
| 693 if (!ExclusiveModeIsEnabled()) | |
| 694 return; | |
| 695 | |
| 696 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); | |
| 697 if (!CanRunAudioTests(audio_manager.get())) | |
| 698 return; | |
| 699 | |
| 700 MessageLoopForUI loop; | |
| 701 scoped_refptr<base::MessageLoopProxy> proxy(loop.message_loop_proxy()); | |
| 702 | |
| 703 MockAudioSourceCallback source; | |
| 704 | |
| 705 // Create exclusive-mode WASAPI output stream which plays out in stereo | |
| 706 // using the minimum buffer size at 48kHz sample rate. | |
| 707 AudioOutputStreamWrapper aosw(audio_manager.get()); | |
| 708 AudioOutputStream* aos = aosw.Create(48000, 160); | |
| 709 EXPECT_TRUE(aos->Open()); | |
| 710 | |
| 711 // Derive the expected size in bytes of each packet. | |
| 712 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * | |
| 713 (aosw.bits_per_sample() / 8); | |
| 714 | |
| 715 // Set up expected minimum delay estimation. | |
| 716 AudioBuffersState state(0, bytes_per_packet); | |
| 717 | |
| 718 // Wait for the first callback and verify its parameters. | |
| 719 EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, | |
| 720 HasValidDelay(state))) | |
| 721 .WillOnce( | |
| 722 DoAll( | |
| 723 InvokeWithoutArgs( | |
| 724 CreateFunctor(&QuitMessageLoop, proxy.get())), | |
| 725 Return(bytes_per_packet))); | |
| 726 | |
| 727 aos->Start(&source); | |
| 728 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), | |
| 729 TestTimeouts::action_timeout()); | |
| 730 loop.Run(); | |
| 731 aos->Stop(); | |
| 732 aos->Close(); | |
| 733 } | |
| 734 | |
| 735 // Verify that we can open and start the output stream in exclusive mode at | |
| 736 // the lowest possible delay at 44.1kHz. | |
| 737 TEST(WinAudioOutputTest, | |
| 738 WASAPIAudioOutputStreamTestExclusiveModeMinBufferSizeAt44kHzSampleRate) { | |
| 739 if (!ExclusiveModeIsEnabled()) | |
| 740 return; | |
| 741 | |
| 742 scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); | |
| 743 if (!CanRunAudioTests(audio_manager.get())) | |
| 744 return; | |
| 745 | |
| 746 MessageLoopForUI loop; | |
| 747 scoped_refptr<base::MessageLoopProxy> proxy(loop.message_loop_proxy()); | |
| 748 | |
| 749 MockAudioSourceCallback source; | |
| 750 | |
| 751 // Create exclusive-mode WASAPI output stream which plays out in stereo | |
| 752 // using the minimum buffer size at 48kHz sample rate. | |
| 753 AudioOutputStreamWrapper aosw(audio_manager.get()); | |
| 754 AudioOutputStream* aos = aosw.Create(44100, 160); | |
| 755 EXPECT_TRUE(aos->Open()); | |
| 756 | |
| 757 // Derive the expected size in bytes of each packet. | |
| 758 uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * | |
| 759 (aosw.bits_per_sample() / 8); | |
| 760 | |
| 761 // Set up expected minimum delay estimation. | |
| 762 AudioBuffersState state(0, bytes_per_packet); | |
| 763 | |
| 764 // Wait for the first callback and verify its parameters. | |
| 765 EXPECT_CALL(source, OnMoreData(NotNull(), bytes_per_packet, | |
| 766 HasValidDelay(state))) | |
| 767 .WillOnce( | |
| 768 DoAll( | |
| 769 InvokeWithoutArgs( | |
| 770 CreateFunctor(&QuitMessageLoop, proxy.get())), | |
| 771 Return(bytes_per_packet))); | |
| 772 | |
| 773 aos->Start(&source); | |
| 774 loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), | |
| 775 TestTimeouts::action_timeout()); | |
| 776 loop.Run(); | |
| 777 aos->Stop(); | |
| 778 aos->Close(); | |
| 779 } | |
| 780 | |
| 565 } // namespace media | 781 } // namespace media |
| OLD | NEW |