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