audio_output_win_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 3// found in the LICENSE file. 4 5#include <windows.h> 6#include <mmsystem.h> 7 8#include "base/basictypes.h" 9#include "base/base_paths.h" 10#include "base/memory/aligned_memory.h" 11#include "base/path_service.h" 12#include "base/sync_socket.h" 13#include "base/win/scoped_com_initializer.h" 14#include "base/win/windows_version.h" 15#include "media/base/limits.h" 16#include "media/audio/audio_io.h" 17#include "media/audio/audio_util.h" 18#include "media/audio/audio_manager.h" 19#include "media/audio/simple_sources.h" 20#include "testing/gmock/include/gmock/gmock.h" 21#include "testing/gtest/include/gtest/gtest.h" 22 23using ::testing::_; 24using ::testing::AnyNumber; 25using ::testing::DoAll; 26using ::testing::Field; 27using ::testing::Invoke; 28using ::testing::InSequence; 29using ::testing::NiceMock; 30using ::testing::NotNull; 31using ::testing::Return; 32 33using base::win::ScopedCOMInitializer; 34 35namespace media { 36 37static const wchar_t kAudioFile1_16b_m_16K[] 38 = L"media\\test\\data\\sweep02_16b_mono_16KHz.raw"; 39 40// This class allows to find out if the callbacks are occurring as 41// expected and if any error has been reported. 42class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { 43 public: 44 explicit TestSourceBasic() 45 : callback_count_(0), 46 had_error_(0) { 47 } 48 // AudioSourceCallback::OnMoreData implementation: 49 virtual int OnMoreData(AudioBus* audio_bus, 50 AudioBuffersState buffers_state) { 51 ++callback_count_; 52 // Touch the channel memory value to make sure memory is good. 53 audio_bus->Zero(); 54 return audio_bus->frames(); 55 } 56 virtual int OnMoreIOData(AudioBus* source, 57 AudioBus* dest, 58 AudioBuffersState buffers_state) { 59 NOTREACHED(); 60 return 0; 61 } 62 // AudioSourceCallback::OnError implementation: 63 virtual void OnError(AudioOutputStream* stream) { 64 ++had_error_; 65 } 66 // Returns how many times OnMoreData() has been called. 67 int callback_count() const { 68 return callback_count_; 69 } 70 // Returns how many times the OnError callback was called. 71 int had_error() const { 72 return had_error_; 73 } 74 75 void set_error(bool error) { 76 had_error_ += error ? 1 : 0; 77 } 78 79 private: 80 int callback_count_; 81 int had_error_; 82}; 83 84const int kMaxNumBuffers = 3; 85// Specializes TestSourceBasic to simulate a source that blocks for some time 86// in the OnMoreData callback. 87class TestSourceLaggy : public TestSourceBasic { 88 public: 89 TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) 90 : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { 91 } 92 virtual int OnMoreData(AudioBus* audio_bus, 93 AudioBuffersState buffers_state) { 94 // Call the base, which increments the callback_count_. 95 TestSourceBasic::OnMoreData(audio_bus, buffers_state); 96 if (callback_count() > kMaxNumBuffers) { 97 ::Sleep(lag_in_ms_); 98 } 99 return audio_bus->frames(); 100 } 101 private: 102 int laggy_after_buffer_; 103 int lag_in_ms_; 104}; 105 106class MockAudioSource : public AudioOutputStream::AudioSourceCallback { 107 public: 108 MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, 109 AudioBuffersState buffers_state)); 110 MOCK_METHOD3(OnMoreIOData, int(AudioBus* source, 111 AudioBus* dest, 112 AudioBuffersState buffers_state)); 113 MOCK_METHOD1(OnError, void(AudioOutputStream* stream)); 114 115 static int ClearData(AudioBus* audio_bus, AudioBuffersState buffers_state) { 116 audio_bus->Zero(); 117 return audio_bus->frames(); 118 } 119}; 120 121// Helper class to memory map an entire file. The mapping is read-only. Don't 122// use for gigabyte-sized files. Attempts to write to this memory generate 123// memory access violations. 124class ReadOnlyMappedFile { 125 public: 126 explicit ReadOnlyMappedFile(const wchar_t* file_name) 127 : fmap_(NULL), start_(NULL), size_(0) { 128 HANDLE file = ::CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, 129 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 130 if (INVALID_HANDLE_VALUE == file) 131 return; 132 fmap_ = ::CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); 133 ::CloseHandle(file); 134 if (!fmap_) 135 return; 136 start_ = reinterpret_cast<char*>(::MapViewOfFile(fmap_, FILE_MAP_READ, 137 0, 0, 0)); 138 if (!start_) 139 return; 140 MEMORY_BASIC_INFORMATION mbi = {0}; 141 ::VirtualQuery(start_, &mbi, sizeof(mbi)); 142 size_ = mbi.RegionSize; 143 } 144 ~ReadOnlyMappedFile() { 145 if (start_) { 146 ::UnmapViewOfFile(start_); 147 ::CloseHandle(fmap_); 148 } 149 } 150 // Returns true if the file was successfully mapped. 151 bool is_valid() const { 152 return ((start_ > 0) && (size_ > 0)); 153 } 154 // Returns the size in bytes of the mapped memory. 155 uint32 size() const { 156 return size_; 157 } 158 // Returns the memory backing the file. 159 const void* GetChunkAt(uint32 offset) { 160 return &start_[offset]; 161 } 162 163 private: 164 HANDLE fmap_; 165 char* start_; 166 uint32 size_; 167}; 168 169// =========================================================================== 170// Validation of AudioManager::AUDIO_PCM_LINEAR 171// 172// NOTE: 173// The tests can fail on the build bots when somebody connects to them via 174// remote-desktop and the rdp client installs an audio device that fails to open 175// at some point, possibly when the connection goes idle. 176 177// Test that can it be created and closed. 178TEST(WinAudioTest, PCMWaveStreamGetAndClose) { 179 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 180 if (!audio_man->HasAudioOutputDevices()) { 181 LOG(WARNING) << "No output device detected."; 182 return; 183 } 184 185 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 186 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 187 8000, 16, 256)); 188 ASSERT_TRUE(NULL != oas); 189 oas->Close(); 190} 191 192// Test that can it be cannot be created with invalid parameters. 193TEST(WinAudioTest, SanityOnMakeParams) { 194 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 195 if (!audio_man->HasAudioOutputDevices()) { 196 LOG(WARNING) << "No output device detected."; 197 return; 198 } 199 200 AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR; 201 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 202 AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256))); 203 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 204 AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16, 256))); 205 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 206 AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80, 256))); 207 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 208 AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256))); 209 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 210 AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16, 256))); 211 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 212 AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, -100))); 213 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 214 AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 0))); 215 EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( 216 AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 217 media::limits::kMaxSamplesPerPacket + 1))); 218} 219 220// Test that it can be opened and closed. 221TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { 222 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 223 if (!audio_man->HasAudioOutputDevices()) { 224 LOG(WARNING) << "No output device detected."; 225 return; 226 } 227 228 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 229 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 230 8000, 16, 256)); 231 ASSERT_TRUE(NULL != oas); 232 EXPECT_TRUE(oas->Open()); 233 oas->Close(); 234} 235 236// Test that it has a maximum packet size. 237TEST(WinAudioTest, PCMWaveStreamOpenLimit) { 238 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 239 if (!audio_man->HasAudioOutputDevices()) { 240 LOG(WARNING) << "No output device detected."; 241 return; 242 } 243 244 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 245 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 246 8000, 16, 1024 * 1024 * 1024)); 247 EXPECT_TRUE(NULL == oas); 248 if (oas) 249 oas->Close(); 250} 251 252// Test potential deadlock situation if the source is slow or blocks for some 253// time. The actual EXPECT_GT are mostly meaningless and the real test is that 254// the test completes in reasonable time. 255TEST(WinAudioTest, PCMWaveSlowSource) { 256 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 257 if (!audio_man->HasAudioOutputDevices()) { 258 LOG(WARNING) << "No output device detected."; 259 return; 260 } 261 262 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 263 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 264 16000, 16, 256)); 265 ASSERT_TRUE(NULL != oas); 266 TestSourceLaggy test_laggy(2, 90); 267 EXPECT_TRUE(oas->Open()); 268 // The test parameters cause a callback every 32 ms and the source is 269 // sleeping for 90 ms, so it is guaranteed that we run out of ready buffers. 270 oas->Start(&test_laggy); 271 ::Sleep(500); 272 EXPECT_GT(test_laggy.callback_count(), 2); 273 EXPECT_FALSE(test_laggy.had_error()); 274 oas->Stop(); 275 ::Sleep(500); 276 oas->Close(); 277} 278 279// Test another potential deadlock situation if the thread that calls Start() 280// gets paused. This test is best when run over RDP with audio enabled. See 281// bug 19276 for more details. 282TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { 283 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 284 if (!audio_man->HasAudioOutputDevices()) { 285 LOG(WARNING) << "No output device detected."; 286 return; 287 } 288 289 uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10; 290 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 291 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 292 AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); 293 ASSERT_TRUE(NULL != oas); 294 295 SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); 296 297 EXPECT_TRUE(oas->Open()); 298 oas->SetVolume(1.0); 299 300 for (int ix = 0; ix != 5; ++ix) { 301 oas->Start(&source); 302 ::Sleep(10); 303 oas->Stop(); 304 } 305 oas->Close(); 306} 307 308 309// This test produces actual audio for .5 seconds on the default wave 310// device at 44.1K s/sec. Parameters have been chosen carefully so you should 311// not hear pops or noises while the sound is playing. 312TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { 313 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 314 if (!audio_man->HasAudioOutputDevices()) { 315 LOG(WARNING) << "No output device detected."; 316 return; 317 } 318 319 uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10; 320 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 321 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 322 AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); 323 ASSERT_TRUE(NULL != oas); 324 325 SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); 326 327 EXPECT_TRUE(oas->Open()); 328 oas->SetVolume(1.0); 329 oas->Start(&source); 330 ::Sleep(500); 331 oas->Stop(); 332 oas->Close(); 333} 334 335// This test produces actual audio for for .5 seconds on the default wave 336// device at 22K s/sec. Parameters have been chosen carefully so you should 337// not hear pops or noises while the sound is playing. The audio also should 338// sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss. 339TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { 340 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 341 if (!audio_man->HasAudioOutputDevices()) { 342 LOG(WARNING) << "No output device detected."; 343 return; 344 } 345 346 uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 20; 347 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 348 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 349 AudioParameters::kAudioCDSampleRate / 2, 16, 350 samples_100_ms)); 351 ASSERT_TRUE(NULL != oas); 352 353 SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); 354 355 EXPECT_TRUE(oas->Open()); 356 357 oas->SetVolume(0.5); 358 oas->Start(&source); 359 ::Sleep(500); 360 361 // Test that the volume is within the set limits. 362 double volume = 0.0; 363 oas->GetVolume(&volume); 364 EXPECT_LT(volume, 0.51); 365 EXPECT_GT(volume, 0.49); 366 oas->Stop(); 367 oas->Close(); 368} 369 370// Uses a restricted source to play ~2 seconds of audio for about 5 seconds. We 371// try hard to generate situation where the two threads are accessing the 372// object roughly at the same time. 373TEST(WinAudioTest, PushSourceFile16KHz) { 374 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 375 if (!audio_man->HasAudioOutputDevices()) { 376 LOG(WARNING) << "No output device detected."; 377 return; 378 } 379 380 static const int kSampleRate = 16000; 381 SineWaveAudioSource source(1, 200.0, kSampleRate); 382 // Compute buffer size for 100ms of audio. 383 const uint32 kSamples100ms = (kSampleRate / 1000) * 100; 384 // Restrict SineWaveAudioSource to 100ms of samples. 385 source.CapSamples(kSamples100ms); 386 387 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 388 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 389 kSampleRate, 16, kSamples100ms)); 390 ASSERT_TRUE(NULL != oas); 391 392 EXPECT_TRUE(oas->Open()); 393 394 oas->SetVolume(1.0); 395 oas->Start(&source); 396 397 // We buffer and play at the same time, buffering happens every ~10ms and the 398 // consuming of the buffer happens every ~100ms. We do 100 buffers which 399 // effectively wrap around the file more than once. 400 for (uint32 ix = 0; ix != 100; ++ix) { 401 ::Sleep(10); 402 source.Reset(); 403 } 404 405 // Play a little bit more of the file. 406 ::Sleep(500); 407 408 oas->Stop(); 409 oas->Close(); 410} 411 412// This test is to make sure an AudioOutputStream can be started after it was 413// stopped. You will here two .5 seconds wave signal separated by 0.5 seconds 414// of silence. 415TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { 416 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 417 if (!audio_man->HasAudioOutputDevices()) { 418 LOG(WARNING) << "No output device detected."; 419 return; 420 } 421 422 uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10; 423 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 424 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 425 AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); 426 ASSERT_TRUE(NULL != oas); 427 428 SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); 429 EXPECT_TRUE(oas->Open()); 430 oas->SetVolume(1.0); 431 432 // Play the wave for .5 seconds. 433 oas->Start(&source); 434 ::Sleep(500); 435 oas->Stop(); 436 437 // Sleep to give silence after stopping the AudioOutputStream. 438 ::Sleep(250); 439 440 // Start again and play for .5 seconds. 441 oas->Start(&source); 442 ::Sleep(500); 443 oas->Stop(); 444 445 oas->Close(); 446} 447 448// With the low latency mode, WASAPI is utilized by default for Vista and 449// higher and Wave is used for XP and lower. It is possible to utilize a 450// smaller buffer size for WASAPI than for Wave. 451TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { 452 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 453 if (!audio_man->HasAudioOutputDevices()) { 454 LOG(WARNING) << "No output device detected."; 455 return; 456 } 457 458 // The WASAPI API requires a correct COM environment. 459 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); 460 461 // Use 10 ms buffer size for WASAPI and 50 ms buffer size for Wave. 462 // Take the existing native sample rate into account. 463 const AudioParameters params = audio_man->GetDefaultOutputStreamParameters(); 464 int sample_rate = params.sample_rate(); 465 uint32 samples_10_ms = sample_rate / 100; 466 int n = 1; 467 (base::win::GetVersion() <= base::win::VERSION_XP) ? n = 5 : n = 1; 468 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 469 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, 470 CHANNEL_LAYOUT_MONO, sample_rate, 471 16, n * samples_10_ms)); 472 ASSERT_TRUE(NULL != oas); 473 474 SineWaveAudioSource source(1, 200, sample_rate); 475 476 bool opened = oas->Open(); 477 if (!opened) { 478 // It was not possible to open this audio device in mono. 479 // No point in continuing the test so let's break here. 480 LOG(WARNING) << "Mono is not supported. Skipping test."; 481 oas->Close(); 482 return; 483 } 484 oas->SetVolume(1.0); 485 486 // Play the wave for .8 seconds. 487 oas->Start(&source); 488 ::Sleep(800); 489 oas->Stop(); 490 oas->Close(); 491} 492 493// Check that the pending bytes value is correct what the stream starts. 494TEST(WinAudioTest, PCMWaveStreamPendingBytes) { 495 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 496 if (!audio_man->HasAudioOutputDevices()) { 497 LOG(WARNING) << "No output device detected."; 498 return; 499 } 500 501 uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10; 502 AudioOutputStream* oas = audio_man->MakeAudioOutputStream( 503 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 504 AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); 505 ASSERT_TRUE(NULL != oas); 506 507 NiceMock<MockAudioSource> source; 508 EXPECT_TRUE(oas->Open()); 509 510 uint32 bytes_100_ms = samples_100_ms * 2; 511 512 // Audio output stream has either a double or triple buffer scheme. 513 // We expect the amount of pending bytes will reaching up to 2 times of 514 // |bytes_100_ms| depending on number of buffers used. 515 // From that it would decrease as we are playing the data but not providing 516 // new one. And then we will try to provide zero data so the amount of 517 // pending bytes will go down and eventually read zero. 518 InSequence s; 519 520 EXPECT_CALL(source, OnMoreData(NotNull(), 521 Field(&AudioBuffersState::pending_bytes, 0))) 522 .WillOnce(Invoke(MockAudioSource::ClearData)); 523 switch (NumberOfWaveOutBuffers()) { 524 case 2: 525 break; // Calls are the same as at end of 3-buffer scheme. 526 case 3: 527 EXPECT_CALL(source, OnMoreData(NotNull(), 528 Field(&AudioBuffersState::pending_bytes, 529 bytes_100_ms))) 530 .WillOnce(Invoke(MockAudioSource::ClearData)); 531 EXPECT_CALL(source, OnMoreData(NotNull(), 532 Field(&AudioBuffersState::pending_bytes, 533 2 * bytes_100_ms))) 534 .WillOnce(Invoke(MockAudioSource::ClearData)); 535 EXPECT_CALL(source, OnMoreData(NotNull(), 536 Field(&AudioBuffersState::pending_bytes, 537 2 * bytes_100_ms))) 538 .Times(AnyNumber()) 539 .WillRepeatedly(Return(0)); 540 break; 541 default: 542 ASSERT_TRUE(false) 543 << "Unexpected number of buffers: " << NumberOfWaveOutBuffers(); 544 } 545 EXPECT_CALL(source, OnMoreData(NotNull(), 546 Field(&AudioBuffersState::pending_bytes, 547 bytes_100_ms))) 548 .Times(AnyNumber()) 549 .WillRepeatedly(Return(0)); 550 EXPECT_CALL(source, OnMoreData(NotNull(), 551 Field(&AudioBuffersState::pending_bytes, 0))) 552 .Times(AnyNumber()) 553 .WillRepeatedly(Return(0)); 554 555 oas->Start(&source); 556 ::Sleep(500); 557 oas->Stop(); 558 oas->Close(); 559} 560 561// Simple source that uses a SyncSocket to retrieve the audio data 562// from a potentially remote thread. 563class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { 564 public: 565 SyncSocketSource(base::SyncSocket* socket, const AudioParameters& params) 566 : socket_(socket) { 567 // Setup AudioBus wrapping data we'll receive over the sync socket. 568 data_size_ = AudioBus::CalculateMemorySize(params); 569 data_.reset(static_cast<float*>( 570 base::AlignedAlloc(data_size_, AudioBus::kChannelAlignment))); 571 audio_bus_ = AudioBus::WrapMemory(params, data_.get()); 572 } 573 ~SyncSocketSource() {} 574 575 // AudioSourceCallback::OnMoreData implementation: 576 virtual int OnMoreData(AudioBus* audio_bus, 577 AudioBuffersState buffers_state) { 578 socket_->Send(&buffers_state, sizeof(buffers_state)); 579 uint32 size = socket_->Receive(data_.get(), data_size_); 580 DCHECK_EQ(static_cast<size_t>(size) % sizeof(*audio_bus_->channel(0)), 0U); 581 audio_bus_->CopyTo(audio_bus); 582 return audio_bus_->frames(); 583 } 584 virtual int OnMoreIOData(AudioBus* source, 585 AudioBus* dest, 586 AudioBuffersState buffers_state) { 587 NOTREACHED(); 588 return 0; 589 } 590 // AudioSourceCallback::OnError implementation: 591 virtual void OnError(AudioOutputStream* stream) { 592 } 593 594 private: 595 base::SyncSocket* socket_; 596 int data_size_; 597 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data_; 598 scoped_ptr<AudioBus> audio_bus_; 599}; 600 601struct SyncThreadContext { 602 base::SyncSocket* socket; 603 int sample_rate; 604 int channels; 605 int frames; 606 double sine_freq; 607 uint32 packet_size_bytes; 608}; 609 610// This thread provides the data that the SyncSocketSource above needs 611// using the other end of a SyncSocket. The protocol is as follows: 612// 613// SyncSocketSource ---send 4 bytes ------------> SyncSocketThread 614// <--- audio packet ---------- 615// 616DWORD __stdcall SyncSocketThread(void* context) { 617 SyncThreadContext& ctx = *(reinterpret_cast<SyncThreadContext*>(context)); 618 619 // Setup AudioBus wrapping data we'll pass over the sync socket. 620 scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data(static_cast<float*>( 621 base::AlignedAlloc(ctx.packet_size_bytes, AudioBus::kChannelAlignment))); 622 scoped_ptr<AudioBus> audio_bus = AudioBus::WrapMemory( 623 ctx.channels, ctx.frames, data.get()); 624 625 SineWaveAudioSource sine(1, ctx.sine_freq, ctx.sample_rate); 626 const int kTwoSecFrames = ctx.sample_rate * 2; 627 628 AudioBuffersState buffers_state; 629 int times = 0; 630 for (int ix = 0; ix < kTwoSecFrames; ix += ctx.frames) { 631 if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0) 632 break; 633 if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak(); 634 sine.OnMoreData(audio_bus.get(), buffers_state); 635 ctx.socket->Send(data.get(), ctx.packet_size_bytes); 636 ++times; 637 } 638 639 return 0; 640} 641 642// Test the basic operation of AudioOutputStream used with a SyncSocket. 643// The emphasis is to verify that it is possible to feed data to the audio 644// layer using a source based on SyncSocket. In a real situation we would 645// go for the low-latency version in combination with SyncSocket, but to keep 646// the test more simple, AUDIO_PCM_LINEAR is utilized instead. The main 647// principle of the test still remains and we avoid the additional complexity 648// related to the two different audio-layers for AUDIO_PCM_LOW_LATENCY. 649// In this test you should hear a continuous 200Hz tone for 2 seconds. 650TEST(WinAudioTest, SyncSocketBasic) { 651 scoped_ptr<AudioManager> audio_man(AudioManager::Create()); 652 if (!audio_man->HasAudioOutputDevices()) { 653 LOG(WARNING) << "No output device detected."; 654 return; 655 } 656 657 static const int sample_rate = AudioParameters::kAudioCDSampleRate; 658 static const uint32 kSamples20ms = sample_rate / 50; 659 AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, 660 CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms); 661 662 663 AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params); 664 ASSERT_TRUE(NULL != oas); 665 666 ASSERT_TRUE(oas->Open()); 667 668 base::SyncSocket sockets[2]; 669 ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets[0], &sockets[1])); 670 671 SyncSocketSource source(&sockets[0], params); 672 673 SyncThreadContext thread_context; 674 thread_context.sample_rate = params.sample_rate(); 675 thread_context.sine_freq = 200.0; 676 thread_context.packet_size_bytes = AudioBus::CalculateMemorySize(params); 677 thread_context.frames = params.frames_per_buffer(); 678 thread_context.channels = params.channels(); 679 thread_context.socket = &sockets[1]; 680 681 HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread, 682 &thread_context, 0, NULL); 683 684 oas->Start(&source); 685 686 ::WaitForSingleObject(thread, INFINITE); 687 ::CloseHandle(thread); 688 689 oas->Stop(); 690 oas->Close(); 691} 692 693} // namespace media 694