| 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 "media/audio/win/audio_low_latency_input_win.h" | 5 #include "media/audio/win/audio_low_latency_input_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | |
| 8 #include <mmsystem.h> | 7 #include <mmsystem.h> |
| 9 #include <stddef.h> | 8 #include <stddef.h> |
| 10 #include <stdint.h> | 9 #include <stdint.h> |
| 10 #include <windows.h> |
| 11 | 11 |
| 12 #include <memory> | 12 #include <memory> |
| 13 | 13 |
| 14 #include "base/environment.h" | 14 #include "base/environment.h" |
| 15 #include "base/files/file_util.h" | 15 #include "base/files/file_util.h" |
| 16 #include "base/macros.h" | 16 #include "base/macros.h" |
| 17 #include "base/message_loop/message_loop.h" | 17 #include "base/message_loop/message_loop.h" |
| 18 #include "base/path_service.h" | 18 #include "base/path_service.h" |
| 19 #include "base/run_loop.h" | 19 #include "base/run_loop.h" |
| 20 #include "base/single_thread_task_runner.h" | 20 #include "base/single_thread_task_runner.h" |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 FakeAudioInputCallback() | 57 FakeAudioInputCallback() |
| 58 : num_received_audio_frames_(0), | 58 : num_received_audio_frames_(0), |
| 59 data_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 59 data_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| 60 base::WaitableEvent::InitialState::NOT_SIGNALED), | 60 base::WaitableEvent::InitialState::NOT_SIGNALED), |
| 61 error_(false) {} | 61 error_(false) {} |
| 62 | 62 |
| 63 bool error() const { return error_; } | 63 bool error() const { return error_; } |
| 64 int num_received_audio_frames() const { return num_received_audio_frames_; } | 64 int num_received_audio_frames() const { return num_received_audio_frames_; } |
| 65 | 65 |
| 66 // Waits until OnData() is called on another thread. | 66 // Waits until OnData() is called on another thread. |
| 67 void WaitForData() { | 67 void WaitForData() { data_event_.Wait(); } |
| 68 data_event_.Wait(); | |
| 69 } | |
| 70 | 68 |
| 71 void OnData(AudioInputStream* stream, | 69 void OnData(AudioInputStream* stream, |
| 72 const AudioBus* src, | 70 const AudioBus* src, |
| 73 uint32_t hardware_delay_bytes, | 71 uint32_t hardware_delay_bytes, |
| 74 double volume) override { | 72 double volume) override { |
| 75 EXPECT_GE(hardware_delay_bytes, 0u); | 73 EXPECT_GE(hardware_delay_bytes, 0u); |
| 76 EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked. | 74 EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked. |
| 77 num_received_audio_frames_ += src->frames(); | 75 num_received_audio_frames_ += src->frames(); |
| 78 data_event_.Signal(); | 76 data_event_.Signal(); |
| 79 } | 77 } |
| 80 | 78 |
| 81 void OnError(AudioInputStream* stream) override { | 79 void OnError(AudioInputStream* stream) override { error_ = true; } |
| 82 error_ = true; | |
| 83 } | |
| 84 | 80 |
| 85 private: | 81 private: |
| 86 int num_received_audio_frames_; | 82 int num_received_audio_frames_; |
| 87 base::WaitableEvent data_event_; | 83 base::WaitableEvent data_event_; |
| 88 bool error_; | 84 bool error_; |
| 89 | 85 |
| 90 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback); | 86 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback); |
| 91 }; | 87 }; |
| 92 | 88 |
| 93 // This audio sink implementation should be used for manual tests only since | 89 // This audio sink implementation should be used for manual tests only since |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 class AudioInputStreamWrapper { | 166 class AudioInputStreamWrapper { |
| 171 public: | 167 public: |
| 172 explicit AudioInputStreamWrapper(AudioManager* audio_manager) | 168 explicit AudioInputStreamWrapper(AudioManager* audio_manager) |
| 173 : audio_man_(audio_manager) { | 169 : audio_man_(audio_manager) { |
| 174 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( | 170 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( |
| 175 AudioDeviceDescription::kDefaultDeviceId, false, &default_params_))); | 171 AudioDeviceDescription::kDefaultDeviceId, false, &default_params_))); |
| 176 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); | 172 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); |
| 177 frames_per_buffer_ = default_params_.frames_per_buffer(); | 173 frames_per_buffer_ = default_params_.frames_per_buffer(); |
| 178 } | 174 } |
| 179 | 175 |
| 176 AudioInputStreamWrapper(AudioManager* audio_manager, |
| 177 const AudioParameters& default_params) |
| 178 : audio_man_(audio_manager), default_params_(default_params) { |
| 179 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); |
| 180 frames_per_buffer_ = default_params_.frames_per_buffer(); |
| 181 } |
| 182 |
| 180 ~AudioInputStreamWrapper() {} | 183 ~AudioInputStreamWrapper() {} |
| 181 | 184 |
| 182 // Creates AudioInputStream object using default parameters. | 185 // Creates AudioInputStream object using default parameters. |
| 183 AudioInputStream* Create() { | 186 AudioInputStream* Create() { return CreateInputStream(); } |
| 184 return CreateInputStream(); | |
| 185 } | |
| 186 | 187 |
| 187 // Creates AudioInputStream object using non-default parameters where the | 188 // Creates AudioInputStream object using non-default parameters where the |
| 188 // frame size is modified. | 189 // frame size is modified. |
| 189 AudioInputStream* Create(int frames_per_buffer) { | 190 AudioInputStream* Create(int frames_per_buffer) { |
| 190 frames_per_buffer_ = frames_per_buffer; | 191 frames_per_buffer_ = frames_per_buffer; |
| 191 return CreateInputStream(); | 192 return CreateInputStream(); |
| 192 } | 193 } |
| 193 | 194 |
| 194 AudioParameters::Format format() const { return default_params_.format(); } | 195 AudioParameters::Format format() const { return default_params_.format(); } |
| 195 int channels() const { | 196 int channels() const { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 218 // Convenience method which creates a default AudioInputStream object. | 219 // Convenience method which creates a default AudioInputStream object. |
| 219 static AudioInputStream* CreateDefaultAudioInputStream( | 220 static AudioInputStream* CreateDefaultAudioInputStream( |
| 220 AudioManager* audio_manager) { | 221 AudioManager* audio_manager) { |
| 221 AudioInputStreamWrapper aisw(audio_manager); | 222 AudioInputStreamWrapper aisw(audio_manager); |
| 222 AudioInputStream* ais = aisw.Create(); | 223 AudioInputStream* ais = aisw.Create(); |
| 223 return ais; | 224 return ais; |
| 224 } | 225 } |
| 225 | 226 |
| 226 class ScopedAudioInputStream { | 227 class ScopedAudioInputStream { |
| 227 public: | 228 public: |
| 228 explicit ScopedAudioInputStream(AudioInputStream* stream) | 229 explicit ScopedAudioInputStream(AudioInputStream* stream) : stream_(stream) {} |
| 229 : stream_(stream) {} | |
| 230 | 230 |
| 231 ~ScopedAudioInputStream() { | 231 ~ScopedAudioInputStream() { |
| 232 if (stream_) | 232 if (stream_) |
| 233 stream_->Close(); | 233 stream_->Close(); |
| 234 } | 234 } |
| 235 | 235 |
| 236 void Close() { | 236 void Close() { |
| 237 if (stream_) | 237 if (stream_) |
| 238 stream_->Close(); | 238 stream_->Close(); |
| 239 stream_ = NULL; | 239 stream_ = NULL; |
| 240 } | 240 } |
| 241 | 241 |
| 242 AudioInputStream* operator->() { | 242 AudioInputStream* operator->() { return stream_; } |
| 243 return stream_; | |
| 244 } | |
| 245 | 243 |
| 246 AudioInputStream* get() const { return stream_; } | 244 AudioInputStream* get() const { return stream_; } |
| 247 | 245 |
| 248 void Reset(AudioInputStream* new_stream) { | 246 void Reset(AudioInputStream* new_stream) { |
| 249 Close(); | 247 Close(); |
| 250 stream_ = new_stream; | 248 stream_ = new_stream; |
| 251 } | 249 } |
| 252 | 250 |
| 253 private: | 251 private: |
| 254 AudioInputStream* stream_; | 252 AudioInputStream* stream_; |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 // Store current packet size (to be used in the subsequent tests). | 395 // Store current packet size (to be used in the subsequent tests). |
| 398 int frames_per_buffer_10ms = aisw.frames_per_buffer(); | 396 int frames_per_buffer_10ms = aisw.frames_per_buffer(); |
| 399 | 397 |
| 400 ais.Close(); | 398 ais.Close(); |
| 401 | 399 |
| 402 // 20 ms packet size. | 400 // 20 ms packet size. |
| 403 | 401 |
| 404 count = 0; | 402 count = 0; |
| 405 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); | 403 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); |
| 406 EXPECT_TRUE(ais->Open()); | 404 EXPECT_TRUE(ais->Open()); |
| 407 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * | 405 bytes_per_packet = |
| 408 (aisw.bits_per_sample() / 8); | 406 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); |
| 409 | 407 |
| 410 { | 408 { |
| 411 base::RunLoop run_loop; | 409 base::RunLoop run_loop; |
| 412 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) | 410 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) |
| 413 .Times(AtLeast(10)) | 411 .Times(AtLeast(10)) |
| 414 .WillRepeatedly( | 412 .WillRepeatedly( |
| 415 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), | 413 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), |
| 416 run_loop.QuitWhenIdleClosure())); | 414 run_loop.QuitWhenIdleClosure())); |
| 417 ais->Start(&sink); | 415 ais->Start(&sink); |
| 418 run_loop.Run(); | 416 run_loop.Run(); |
| 419 ais->Stop(); | 417 ais->Stop(); |
| 420 ais.Close(); | 418 ais.Close(); |
| 421 } | 419 } |
| 422 | 420 |
| 423 // 5 ms packet size. | 421 // 5 ms packet size. |
| 424 | 422 |
| 425 count = 0; | 423 count = 0; |
| 426 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); | 424 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); |
| 427 EXPECT_TRUE(ais->Open()); | 425 EXPECT_TRUE(ais->Open()); |
| 428 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * | 426 bytes_per_packet = |
| 429 (aisw.bits_per_sample() / 8); | 427 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8); |
| 430 | 428 |
| 431 { | 429 { |
| 432 base::RunLoop run_loop; | 430 base::RunLoop run_loop; |
| 433 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) | 431 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) |
| 434 .Times(AtLeast(10)) | 432 .Times(AtLeast(10)) |
| 435 .WillRepeatedly( | 433 .WillRepeatedly( |
| 436 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), | 434 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), |
| 437 run_loop.QuitWhenIdleClosure())); | 435 run_loop.QuitWhenIdleClosure())); |
| 438 ais->Start(&sink); | 436 ais->Start(&sink); |
| 439 run_loop.Run(); | 437 run_loop.Run(); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. | 474 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. |
| 477 // To include disabled tests in test execution, just invoke the test program | 475 // To include disabled tests in test execution, just invoke the test program |
| 478 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS | 476 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS |
| 479 // environment variable to a value greater than 0. | 477 // environment variable to a value greater than 0. |
| 480 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { | 478 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { |
| 481 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); | 479 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); |
| 482 | 480 |
| 483 // Name of the output PCM file containing captured data. The output file | 481 // Name of the output PCM file containing captured data. The output file |
| 484 // will be stored in the directory containing 'media_unittests.exe'. | 482 // will be stored in the directory containing 'media_unittests.exe'. |
| 485 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. | 483 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. |
| 486 const char* file_name = "out_stereo_10sec.pcm"; | 484 const char* file_name = "out_10sec.pcm"; |
| 487 | 485 |
| 488 AudioInputStreamWrapper aisw(audio_manager_.get()); | 486 AudioInputStreamWrapper aisw(audio_manager_.get()); |
| 489 ScopedAudioInputStream ais(aisw.Create()); | 487 ScopedAudioInputStream ais(aisw.Create()); |
| 490 EXPECT_TRUE(ais->Open()); | 488 ASSERT_TRUE(ais->Open()); |
| 491 | 489 |
| 492 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; | 490 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; |
| 493 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); | 491 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); |
| 494 VLOG(0) << ">> Speak into the default microphone while recording."; | 492 VLOG(0) << ">> Speak into the default microphone while recording."; |
| 495 ais->Start(&file_sink); | 493 ais->Start(&file_sink); |
| 496 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); | 494 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); |
| 497 ais->Stop(); | 495 ais->Stop(); |
| 498 VLOG(0) << ">> Recording has stopped."; | 496 VLOG(0) << ">> Recording has stopped."; |
| 499 ais.Close(); | 497 ais.Close(); |
| 500 } | 498 } |
| 501 | 499 |
| 500 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) { |
| 501 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); |
| 502 |
| 503 // This is basically the same test as WASAPIAudioInputStreamRecordToFile |
| 504 // except it forces use of a different sample rate than is preferred by |
| 505 // the hardware. This functionality is offered while we still have code |
| 506 // that doesn't ask the lower levels for what the preferred audio parameters |
| 507 // are (and previously depended on the old Wave API to do this automatically). |
| 508 |
| 509 AudioParameters params; |
| 510 ASSERT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( |
| 511 AudioDeviceDescription::kDefaultDeviceId, false, ¶ms))); |
| 512 |
| 513 VLOG(0) << ">> Hardware sample rate: " << params.sample_rate() << " [Hz]"; |
| 514 |
| 515 params.set_sample_rate(params.sample_rate() == 48000 ? 44100 : 48000); |
| 516 params.set_frames_per_buffer(params.sample_rate() / 100); |
| 517 |
| 518 static const char kFileName[] = "out_10sec_resampled.pcm"; |
| 519 |
| 520 AudioInputStreamWrapper aisw(audio_manager_.get(), params); |
| 521 ScopedAudioInputStream ais(aisw.Create()); |
| 522 ASSERT_TRUE(ais->Open()); |
| 523 |
| 524 VLOG(0) << ">> Resampled rate will be: " << aisw.sample_rate() << " [Hz]"; |
| 525 WriteToFileAudioSink file_sink(kFileName, aisw.bits_per_sample()); |
| 526 VLOG(0) << ">> Speak into the default microphone while recording."; |
| 527 ais->Start(&file_sink); |
| 528 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); |
| 529 ais->Stop(); |
| 530 VLOG(0) << ">> Recording has stopped."; |
| 531 ais.Close(); |
| 532 } |
| 533 |
| 502 } // namespace media | 534 } // namespace media |
| OLD | NEW |