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

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

Issue 2690793002: Add basic resample support to WASAPIAudioInputStream. (Closed)
Patch Set: Add check for unsupported channel layout Created 3 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_low_latency_input_win.cc ('k') | media/base/audio_block_fifo.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "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"
21 #include "base/strings/stringprintf.h"
21 #include "base/test/test_timeouts.h" 22 #include "base/test/test_timeouts.h"
22 #include "base/win/scoped_com_initializer.h" 23 #include "base/win/scoped_com_initializer.h"
23 #include "media/audio/audio_device_description.h" 24 #include "media/audio/audio_device_description.h"
24 #include "media/audio/audio_io.h" 25 #include "media/audio/audio_io.h"
25 #include "media/audio/audio_manager.h" 26 #include "media/audio/audio_manager.h"
26 #include "media/audio/audio_unittest_util.h" 27 #include "media/audio/audio_unittest_util.h"
27 #include "media/audio/win/core_audio_util_win.h" 28 #include "media/audio/win/core_audio_util_win.h"
28 #include "media/base/seekable_buffer.h" 29 #include "media/base/seekable_buffer.h"
29 #include "testing/gmock/include/gmock/gmock.h" 30 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h" 31 #include "testing/gtest/include/gtest/gtest.h"
(...skipping 26 matching lines...) Expand all
57 FakeAudioInputCallback() 58 FakeAudioInputCallback()
58 : num_received_audio_frames_(0), 59 : num_received_audio_frames_(0),
59 data_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, 60 data_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
60 base::WaitableEvent::InitialState::NOT_SIGNALED), 61 base::WaitableEvent::InitialState::NOT_SIGNALED),
61 error_(false) {} 62 error_(false) {}
62 63
63 bool error() const { return error_; } 64 bool error() const { return error_; }
64 int num_received_audio_frames() const { return num_received_audio_frames_; } 65 int num_received_audio_frames() const { return num_received_audio_frames_; }
65 66
66 // Waits until OnData() is called on another thread. 67 // Waits until OnData() is called on another thread.
67 void WaitForData() { 68 void WaitForData() { data_event_.Wait(); }
68 data_event_.Wait();
69 }
70 69
71 void OnData(AudioInputStream* stream, 70 void OnData(AudioInputStream* stream,
72 const AudioBus* src, 71 const AudioBus* src,
73 uint32_t hardware_delay_bytes, 72 uint32_t hardware_delay_bytes,
74 double volume) override { 73 double volume) override {
75 EXPECT_GE(hardware_delay_bytes, 0u); 74 EXPECT_GE(hardware_delay_bytes, 0u);
76 EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked. 75 EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked.
77 num_received_audio_frames_ += src->frames(); 76 num_received_audio_frames_ += src->frames();
78 data_event_.Signal(); 77 data_event_.Signal();
79 } 78 }
80 79
81 void OnError(AudioInputStream* stream) override { 80 void OnError(AudioInputStream* stream) override { error_ = true; }
82 error_ = true;
83 }
84 81
85 private: 82 private:
86 int num_received_audio_frames_; 83 int num_received_audio_frames_;
87 base::WaitableEvent data_event_; 84 base::WaitableEvent data_event_;
88 bool error_; 85 bool error_;
89 86
90 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback); 87 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
91 }; 88 };
92 89
93 // This audio sink implementation should be used for manual tests only since 90 // This audio sink implementation should be used for manual tests only since
94 // the recorded data is stored on a raw binary data file. 91 // the recorded data is stored on a raw binary data file.
95 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { 92 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
96 public: 93 public:
97 // Allocate space for ~10 seconds of data @ 48kHz in stereo: 94 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
98 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes. 95 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
99 static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10; 96 static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
100 97
101 explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample) 98 explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample)
102 : bits_per_sample_(bits_per_sample), 99 : bits_per_sample_(bits_per_sample),
103 buffer_(0, kMaxBufferSize), 100 buffer_(0, kMaxBufferSize),
104 bytes_to_write_(0) { 101 bytes_to_write_(0) {
105 base::FilePath file_path; 102 base::FilePath file_path;
106 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path)); 103 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
107 file_path = file_path.AppendASCII(file_name); 104 file_path = file_path.AppendASCII(file_name);
108 binary_file_ = base::OpenFile(file_path, "wb"); 105 binary_file_ = base::OpenFile(file_path, "wb");
109 DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file."; 106 DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
110 VLOG(0) << ">> Output file: " << file_path.value() << " has been created."; 107 VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
111 VLOG(0) << "bits_per_sample_:" << bits_per_sample_; 108 VLOG(0) << ">> bits_per_sample_:" << bits_per_sample_;
112 } 109 }
113 110
114 ~WriteToFileAudioSink() override { 111 ~WriteToFileAudioSink() override {
115 size_t bytes_written = 0; 112 size_t bytes_written = 0;
116 while (bytes_written < bytes_to_write_) { 113 while (bytes_written < bytes_to_write_) {
117 const uint8_t* chunk; 114 const uint8_t* chunk;
118 int chunk_size; 115 int chunk_size;
119 116
120 // Stop writing if no more data is available. 117 // Stop writing if no more data is available.
121 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size)) 118 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 class AudioInputStreamWrapper { 167 class AudioInputStreamWrapper {
171 public: 168 public:
172 explicit AudioInputStreamWrapper(AudioManager* audio_manager) 169 explicit AudioInputStreamWrapper(AudioManager* audio_manager)
173 : audio_man_(audio_manager) { 170 : audio_man_(audio_manager) {
174 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters( 171 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
175 AudioDeviceDescription::kDefaultDeviceId, false, &default_params_))); 172 AudioDeviceDescription::kDefaultDeviceId, false, &default_params_)));
176 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); 173 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
177 frames_per_buffer_ = default_params_.frames_per_buffer(); 174 frames_per_buffer_ = default_params_.frames_per_buffer();
178 } 175 }
179 176
177 AudioInputStreamWrapper(AudioManager* audio_manager,
178 const AudioParameters& default_params)
179 : audio_man_(audio_manager), default_params_(default_params) {
180 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
181 frames_per_buffer_ = default_params_.frames_per_buffer();
182 }
183
180 ~AudioInputStreamWrapper() {} 184 ~AudioInputStreamWrapper() {}
181 185
182 // Creates AudioInputStream object using default parameters. 186 // Creates AudioInputStream object using default parameters.
183 AudioInputStream* Create() { 187 AudioInputStream* Create() { return CreateInputStream(); }
184 return CreateInputStream();
185 }
186 188
187 // Creates AudioInputStream object using non-default parameters where the 189 // Creates AudioInputStream object using non-default parameters where the
188 // frame size is modified. 190 // frame size is modified.
189 AudioInputStream* Create(int frames_per_buffer) { 191 AudioInputStream* Create(int frames_per_buffer) {
190 frames_per_buffer_ = frames_per_buffer; 192 frames_per_buffer_ = frames_per_buffer;
191 return CreateInputStream(); 193 return CreateInputStream();
192 } 194 }
193 195
194 AudioParameters::Format format() const { return default_params_.format(); } 196 AudioParameters::Format format() const { return default_params_.format(); }
195 int channels() const { 197 int channels() const {
(...skipping 22 matching lines...) Expand all
218 // Convenience method which creates a default AudioInputStream object. 220 // Convenience method which creates a default AudioInputStream object.
219 static AudioInputStream* CreateDefaultAudioInputStream( 221 static AudioInputStream* CreateDefaultAudioInputStream(
220 AudioManager* audio_manager) { 222 AudioManager* audio_manager) {
221 AudioInputStreamWrapper aisw(audio_manager); 223 AudioInputStreamWrapper aisw(audio_manager);
222 AudioInputStream* ais = aisw.Create(); 224 AudioInputStream* ais = aisw.Create();
223 return ais; 225 return ais;
224 } 226 }
225 227
226 class ScopedAudioInputStream { 228 class ScopedAudioInputStream {
227 public: 229 public:
228 explicit ScopedAudioInputStream(AudioInputStream* stream) 230 explicit ScopedAudioInputStream(AudioInputStream* stream) : stream_(stream) {}
229 : stream_(stream) {}
230 231
231 ~ScopedAudioInputStream() { 232 ~ScopedAudioInputStream() {
232 if (stream_) 233 if (stream_)
233 stream_->Close(); 234 stream_->Close();
234 } 235 }
235 236
236 void Close() { 237 void Close() {
237 if (stream_) 238 if (stream_)
238 stream_->Close(); 239 stream_->Close();
239 stream_ = NULL; 240 stream_ = NULL;
240 } 241 }
241 242
242 AudioInputStream* operator->() { 243 AudioInputStream* operator->() { return stream_; }
243 return stream_;
244 }
245 244
246 AudioInputStream* get() const { return stream_; } 245 AudioInputStream* get() const { return stream_; }
247 246
248 void Reset(AudioInputStream* new_stream) { 247 void Reset(AudioInputStream* new_stream) {
249 Close(); 248 Close();
250 stream_ = new_stream; 249 stream_ = new_stream;
251 } 250 }
252 251
253 private: 252 private:
254 AudioInputStream* stream_; 253 AudioInputStream* stream_;
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 // Store current packet size (to be used in the subsequent tests). 396 // Store current packet size (to be used in the subsequent tests).
398 int frames_per_buffer_10ms = aisw.frames_per_buffer(); 397 int frames_per_buffer_10ms = aisw.frames_per_buffer();
399 398
400 ais.Close(); 399 ais.Close();
401 400
402 // 20 ms packet size. 401 // 20 ms packet size.
403 402
404 count = 0; 403 count = 0;
405 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); 404 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
406 EXPECT_TRUE(ais->Open()); 405 EXPECT_TRUE(ais->Open());
407 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * 406 bytes_per_packet =
408 (aisw.bits_per_sample() / 8); 407 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8);
409 408
410 { 409 {
411 base::RunLoop run_loop; 410 base::RunLoop run_loop;
412 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) 411 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _))
413 .Times(AtLeast(10)) 412 .Times(AtLeast(10))
414 .WillRepeatedly( 413 .WillRepeatedly(
415 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), 414 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(),
416 run_loop.QuitWhenIdleClosure())); 415 run_loop.QuitWhenIdleClosure()));
417 ais->Start(&sink); 416 ais->Start(&sink);
418 run_loop.Run(); 417 run_loop.Run();
419 ais->Stop(); 418 ais->Stop();
420 ais.Close(); 419 ais.Close();
421 } 420 }
422 421
423 // 5 ms packet size. 422 // 5 ms packet size.
424 423
425 count = 0; 424 count = 0;
426 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); 425 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
427 EXPECT_TRUE(ais->Open()); 426 EXPECT_TRUE(ais->Open());
428 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * 427 bytes_per_packet =
429 (aisw.bits_per_sample() / 8); 428 aisw.channels() * aisw.frames_per_buffer() * (aisw.bits_per_sample() / 8);
430 429
431 { 430 {
432 base::RunLoop run_loop; 431 base::RunLoop run_loop;
433 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _)) 432 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _))
434 .Times(AtLeast(10)) 433 .Times(AtLeast(10))
435 .WillRepeatedly( 434 .WillRepeatedly(
436 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(), 435 CheckCountAndPostQuitTask(&count, 10, message_loop_.task_runner(),
437 run_loop.QuitWhenIdleClosure())); 436 run_loop.QuitWhenIdleClosure()));
438 ais->Start(&sink); 437 ais->Start(&sink);
439 run_loop.Run(); 438 run_loop.Run();
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 // By default, GTest will print out YOU HAVE 1 DISABLED TEST. 475 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
477 // To include disabled tests in test execution, just invoke the test program 476 // 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 477 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
479 // environment variable to a value greater than 0. 478 // environment variable to a value greater than 0.
480 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { 479 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
481 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get())); 480 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
482 481
483 // Name of the output PCM file containing captured data. The output file 482 // Name of the output PCM file containing captured data. The output file
484 // will be stored in the directory containing 'media_unittests.exe'. 483 // will be stored in the directory containing 'media_unittests.exe'.
485 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. 484 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
486 const char* file_name = "out_stereo_10sec.pcm"; 485 const char* file_name = "out_10sec.pcm";
487 486
488 AudioInputStreamWrapper aisw(audio_manager_.get()); 487 AudioInputStreamWrapper aisw(audio_manager_.get());
489 ScopedAudioInputStream ais(aisw.Create()); 488 ScopedAudioInputStream ais(aisw.Create());
490 EXPECT_TRUE(ais->Open()); 489 ASSERT_TRUE(ais->Open());
491 490
492 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; 491 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
493 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); 492 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
494 VLOG(0) << ">> Speak into the default microphone while recording."; 493 VLOG(0) << ">> Speak into the default microphone while recording.";
495 ais->Start(&file_sink); 494 ais->Start(&file_sink);
496 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); 495 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
497 ais->Stop(); 496 ais->Stop();
498 VLOG(0) << ">> Recording has stopped."; 497 VLOG(0) << ">> Recording has stopped.";
499 ais.Close(); 498 ais.Close();
500 } 499 }
501 500
501 TEST_F(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamResampleToFile) {
502 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager_.get()));
503
504 // This is basically the same test as WASAPIAudioInputStreamRecordToFile
505 // except it forces use of a different sample rate than is preferred by
506 // the hardware. This functionality is offered while we still have code
507 // that doesn't ask the lower levels for what the preferred audio parameters
508 // are (and previously depended on the old Wave API to do this automatically).
509
510 struct TestData {
511 const int rate;
512 const int frames;
513 ChannelLayout layout;
514 } tests[] = {
515 {8000, 80, CHANNEL_LAYOUT_MONO},
516 {8000, 80, CHANNEL_LAYOUT_STEREO},
517 {44100, 441, CHANNEL_LAYOUT_MONO},
518 {44100, 1024, CHANNEL_LAYOUT_STEREO},
519 };
520
521 for (const auto& test : tests) {
522 AudioParameters params;
523 ASSERT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
524 AudioDeviceDescription::kDefaultDeviceId, false, &params)));
525
526 VLOG(0) << ">> Hardware sample rate: " << params.sample_rate() << " [Hz]";
527 VLOG(0) << ">> Hardware channel layout: "
528 << ChannelLayoutToString(params.channel_layout());
529
530 // Pick a somewhat difficult sample rate to convert too.
531 // If the sample rate is 8kHz, 16kHz, 32kHz, 48kHz etc, we convert to
532 // 44.1kHz.
533 // Otherwise (e.g. 44.1kHz, 22.05kHz etc) we convert to 48kHz.
534 const int hw_sample_rate = params.sample_rate();
535 params.Reset(params.format(), test.layout, test.rate,
536 params.bits_per_sample(), test.frames);
537
538 std::string file_name(base::StringPrintf(
539 "resampled_10sec_%i_to_%i_%s.pcm", hw_sample_rate, params.sample_rate(),
540 ChannelLayoutToString(params.channel_layout())));
541
542 AudioInputStreamWrapper aisw(audio_manager_.get(), params);
543 ScopedAudioInputStream ais(aisw.Create());
544 ASSERT_TRUE(ais->Open());
545
546 VLOG(0) << ">> Resampled rate will be: " << aisw.sample_rate() << " [Hz]";
547 VLOG(0) << ">> New layout will be: "
548 << ChannelLayoutToString(params.channel_layout());
549 WriteToFileAudioSink file_sink(file_name.c_str(), aisw.bits_per_sample());
550 VLOG(0) << ">> Speak into the default microphone while recording.";
551 ais->Start(&file_sink);
552 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
553 // base::PlatformThread::Sleep(base::TimeDelta::FromMinutes(10));
554 ais->Stop();
555 VLOG(0) << ">> Recording has stopped.";
556 ais.Close();
557 }
558 }
559
502 } // namespace media 560 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/win/audio_low_latency_input_win.cc ('k') | media/base/audio_block_fifo.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698