// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/basictypes.h" #include "base/base_paths.h" #include "base/file_util.h" #include "base/path_service.h" #include "base/sync_socket.h" #include "media/audio/audio_output.h" #include "media/audio/simple_sources.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::InSequence; using ::testing::NiceMock; using ::testing::NotNull; using ::testing::Return; namespace { const wchar_t kAudioFile1_16b_m_16K[] = L"media\\test\\data\\sweep02_16b_mono_16KHz.raw"; // This class allows to find out if the callbacks are occurring as // expected and if any error has been reported. class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { public: explicit TestSourceBasic() : callback_count_(0), had_error_(0), was_closed_(0) { } // AudioSourceCallback::OnMoreData implementation: virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest, uint32 max_size, uint32 pending_bytes) { ++callback_count_; // Touch the first byte to make sure memory is good. if (max_size) reinterpret_cast(dest)[0] = 1; return max_size; } // AudioSourceCallback::OnClose implementation: virtual void OnClose(AudioOutputStream* stream) { ++was_closed_; } // AudioSourceCallback::OnError implementation: virtual void OnError(AudioOutputStream* stream, int code) { ++had_error_; } // Returns how many times OnMoreData() has been called. int callback_count() const { return callback_count_; } // Returns how many times the OnError callback was called. int had_error() const { return had_error_; } void set_error(bool error) { had_error_ += error ? 1 : 0; } // Returns how many times the OnClose callback was called. int was_closed() const { return was_closed_; } private: int callback_count_; int had_error_; int was_closed_; }; bool IsRunningHeadless() { return (0 != ::GetEnvironmentVariableW(L"CHROME_HEADLESS", NULL, 0)); } } // namespace. const int kNumBuffers = 3; // Specializes TestSourceBasic to detect that the AudioStream is using // triple buffering correctly. class TestSourceTripleBuffer : public TestSourceBasic { public: TestSourceTripleBuffer() { buffer_address_[0] = NULL; buffer_address_[1] = NULL; buffer_address_[2] = NULL; } // Override of TestSourceBasic::OnMoreData. virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest, uint32 max_size, uint32 pending_bytes) { // Call the base, which increments the callback_count_. TestSourceBasic::OnMoreData(stream, dest, max_size, 0); if (callback_count() % kNumBuffers == 2) { set_error(!CompareExistingIfNotNULL(2, dest)); } else if (callback_count() % kNumBuffers == 1) { set_error(!CompareExistingIfNotNULL(1, dest)); } else { set_error(!CompareExistingIfNotNULL(0, dest)); } if (callback_count() > kNumBuffers) { set_error(buffer_address_[0] == buffer_address_[1]); set_error(buffer_address_[1] == buffer_address_[2]); } return max_size; } private: bool CompareExistingIfNotNULL(uint32 index, void* address) { void*& entry = buffer_address_[index]; if (!entry) entry = address; return (entry == address); } void* buffer_address_[kNumBuffers]; }; // Specializes TestSourceBasic to simulate a source that blocks for some time // in the OnMoreData callback. class TestSourceLaggy : public TestSourceBasic { public: TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { } virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest, uint32 max_size, uint32 pending_bytes) { // Call the base, which increments the callback_count_. TestSourceBasic::OnMoreData(stream, dest, max_size, 0); if (callback_count() > kNumBuffers) { ::Sleep(lag_in_ms_); } return max_size; } private: int laggy_after_buffer_; int lag_in_ms_; }; class MockAudioSource : public AudioOutputStream::AudioSourceCallback { public: MOCK_METHOD4(OnMoreData, uint32(AudioOutputStream* stream, void* dest, uint32 max_size, uint32 pending_bytes)); MOCK_METHOD1(OnClose, void(AudioOutputStream* stream)); MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); }; // Helper class to memory map an entire file. The mapping is read-only. Don't // use for gigabyte-sized files. Attempts to write to this memory generate // memory access violations. class ReadOnlyMappedFile { public: explicit ReadOnlyMappedFile(const wchar_t* file_name) : fmap_(NULL), start_(NULL), size_(0) { HANDLE file = ::CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == file) return; fmap_ = ::CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); ::CloseHandle(file); if (!fmap_) return; start_ = reinterpret_cast(::MapViewOfFile(fmap_, FILE_MAP_READ, 0, 0, 0)); if (!start_) return; MEMORY_BASIC_INFORMATION mbi = {0}; ::VirtualQuery(start_, &mbi, sizeof(mbi)); size_ = mbi.RegionSize; } ~ReadOnlyMappedFile() { if (start_) { ::UnmapViewOfFile(start_); ::CloseHandle(fmap_); } } // Returns true if the file was successfully mapped. bool is_valid() const { return ((start_ > 0) && (size_ > 0)); } // Returns the size in bytes of the mapped memory. uint32 size() const { return size_; } // Returns the memory backing the file. const void* GetChunkAt(uint32 offset) { return &start_[offset]; } private: HANDLE fmap_; char* start_; uint32 size_; }; // ============================================================================ // Validate that the AudioManager::AUDIO_MOCK callbacks work. TEST(WinAudioTest, MockStreamBasicCallbacks) { AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_MOCK, 2, 8000, 8); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open(256)); TestSourceBasic source; oas->Start(&source); EXPECT_GT(source.callback_count(), 0); oas->Stop(); oas->Close(); EXPECT_EQ(0, source.had_error()); EXPECT_EQ(1, source.was_closed()); } // =========================================================================== // Validation of AudioManager::AUDIO_PCM_LINEAR // // The tests tend to fail in the build bots when somebody connects to them via // via remote-desktop because it installs an audio device that fails to open // at some point, possibly when the connection goes idle. So that is why we // skipped them in headless mode. // Test that can it be created and closed. TEST(WinAudioTest, PCMWaveStreamGetAndClose) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 2, 8000, 16); ASSERT_TRUE(NULL != oas); oas->Close(); } // Test that can it be cannot be created with crazy parameters TEST(WinAudioTest, SanityOnMakeParams) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioManager::Format fmt = AudioManager::AUDIO_PCM_LINEAR; EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, 8, 8000, 16)); EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, 1, 1024 * 1024, 16)); EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, 2, 8000, 80)); EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, -2, 8000, 16)); EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, 2, -8000, 16)); EXPECT_TRUE(NULL == audio_man->MakeAudioStream(fmt, 2, -8000, -16)); } // Test that it can be opened and closed. TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 2, 8000, 16); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open(1024)); oas->Close(); } // Test that it has a maximum packet size. TEST(WinAudioTest, PCMWaveStreamOpenLimit) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 2, 8000, 16); ASSERT_TRUE(NULL != oas); EXPECT_FALSE(oas->Open(1024 * 1024 * 1024)); oas->Close(); } // Test that it uses the triple buffers correctly. Because it uses the actual // audio device, you might hear a short pop noise for a short time. TEST(WinAudioTest, PCMWaveStreamTripleBuffer) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); ASSERT_TRUE(NULL != oas); TestSourceTripleBuffer test_triple_buffer; EXPECT_TRUE(oas->Open(512)); oas->Start(&test_triple_buffer); ::Sleep(300); EXPECT_GT(test_triple_buffer.callback_count(), kNumBuffers); EXPECT_FALSE(test_triple_buffer.had_error()); oas->Stop(); ::Sleep(500); oas->Close(); } // Test potential deadlock situation if the source is slow or blocks for some // time. The actual EXPECT_GT are mostly meaningless and the real test is that // the test completes in reasonable time. TEST(WinAudioTest, PCMWaveSlowSource) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); ASSERT_TRUE(NULL != oas); TestSourceLaggy test_laggy(2, 90); EXPECT_TRUE(oas->Open(512)); // The test parameters cause a callback every 32 ms and the source is // sleeping for 90 ms, so it is guaranteed that we run out of ready buffers. oas->Start(&test_laggy); ::Sleep(500); EXPECT_GT(test_laggy.callback_count(), 2); EXPECT_FALSE(test_laggy.had_error()); oas->Stop(); ::Sleep(500); oas->Close(); } // Test another potential deadlock situation if the thread that calls Start() // gets paused. This test is best when run over RDP with audio enabled. See // bug 19276 for more details. TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate); uint32 bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(1.0); for (int ix = 0; ix != 5; ++ix) { oas->Start(&source); ::Sleep(10); oas->Stop(); } oas->Close(); } // This test produces actual audio for .5 seconds on the default wave // device at 44.1K s/sec. Parameters have been chosen carefully so you should // not hear pops or noises while the sound is playing. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate); uint32 bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(1.0); oas->Start(&source); ::Sleep(500); oas->Stop(); oas->Close(); } // This test produces actual audio for for .5 seconds on the default wave // device at 22K s/sec. Parameters have been chosen carefully so you should // not hear pops or noises while the sound is playing. The audio also should // sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate/2, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate/2); uint32 bytes_100_ms = (AudioManager::kAudioCDSampleRate / 20) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(0.5); oas->Start(&source); ::Sleep(500); // Test that the volume is within the set limits. double volume = 0.0; oas->GetVolume(&volume); EXPECT_LT(volume, 0.51); EXPECT_GT(volume, 0.49); oas->Stop(); oas->Close(); } // Uses the PushSource to play a 2 seconds file clip for about 5 seconds. We // try hard to generate situation where the two threads are accessing the // object roughly at the same time. What you hear is a sweeping tone from 1KHz // to 2KHz with a bit of fade out at the end for one second. The file is two // of these sweeping tones back to back. TEST(WinAudioTest, PushSourceFile16KHz) { if (IsRunningHeadless()) return; // Open sweep02_16b_mono_16KHz.raw which has no format. It contains the // raw 16 bit samples for a single channel in little-endian format. The // creation sample rate is 16KHz. FilePath audio_file; ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &audio_file)); audio_file = audio_file.Append(kAudioFile1_16b_m_16K); // Map the entire file in memory. ReadOnlyMappedFile file_reader(audio_file.value().c_str()); ASSERT_TRUE(file_reader.is_valid()); AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); ASSERT_TRUE(NULL != oas); // compute buffer size for 100ms of audio. Which is 3200 bytes. const uint32 kSize50ms = 2 * (16000 / 1000) * 100; EXPECT_TRUE(oas->Open(kSize50ms)); uint32 offset = 0; const uint32 kMaxStartOffset = file_reader.size() - kSize50ms; // We buffer and play at the same time, buffering happens every ~10ms and the // consuming of the buffer happens every ~50ms. We do 100 buffers which // effectively wrap around the file more than once. PushSource push_source(kSize50ms); for (uint32 ix = 0; ix != 100; ++ix) { push_source.Write(file_reader.GetChunkAt(offset), kSize50ms); if (ix == 2) { // For glitch free, start playing after some buffers are in. oas->Start(&push_source); } ::Sleep(10); offset += kSize50ms; if (offset > kMaxStartOffset) offset = 0; } // Play a little bit more of the file. ::Sleep(500); oas->Stop(); oas->Close(); } // This test is to make sure an AudioOutputStream can be started after it was // stopped. You will here two .5 seconds wave signal separated by 0.5 seconds // of silence. TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate); uint32 bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(1.0); // Play the wave for .5 seconds. oas->Start(&source); ::Sleep(500); oas->Stop(); // Sleep to give silence after stopping the AudioOutputStream. ::Sleep(250); // Start again and play for .5 seconds. oas->Start(&source); ::Sleep(500); oas->Stop(); oas->Close(); } // With the low latency mode, we have two buffers instead of 3 and we // should be able to handle 20ms buffers at 44KHz. See also the SyncSocketBasic // test below. // TODO(cpu): right now the best we can do is 50ms before it sounds choppy. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44KssLowLatency) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LOW_LATENCY, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate); uint32 bytes_50_ms = (AudioManager::kAudioCDSampleRate / 20) * sizeof(uint16); EXPECT_TRUE(oas->Open(bytes_50_ms)); oas->SetVolume(1.0); // Play the wave for .8 seconds. oas->Start(&source); ::Sleep(800); oas->Stop(); oas->Close(); } // Check that the pending bytes value is correct what the stream starts. TEST(WinAudioTest, PCMWaveStreamPendingBytes) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); NiceMock source; uint32 bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); // We expect the amount of pending bytes will reaching 2 times of // |bytes_100_ms| because the audio output stream has a triple buffer scheme. // And then we will try to provide zero data so the amount of pending bytes // will go down and eventually read zero. InSequence s; EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0)) .WillOnce(Return(bytes_100_ms)); EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms)) .WillOnce(Return(bytes_100_ms)); EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 2 * bytes_100_ms)) .WillOnce(Return(bytes_100_ms)); EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 2 * bytes_100_ms)) .WillOnce(Return(0)); EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms)) .WillOnce(Return(0)); EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0)) .Times(AnyNumber()) .WillRepeatedly(Return(0)); oas->Start(&source); ::Sleep(500); oas->Stop(); oas->Close(); } namespace { // Simple source that uses a SyncSocket to retrieve the audio data // from a potentially remote thread. class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { public: explicit SyncSocketSource(base::SyncSocket* socket) : socket_(socket) {} ~SyncSocketSource() { delete socket_; } // AudioSourceCallback::OnMoreData implementation: virtual uint32 OnMoreData(AudioOutputStream* stream, void* dest, uint32 max_size, uint32 pending_bytes) { socket_->Send(&pending_bytes, sizeof(pending_bytes)); uint32 got = socket_->Receive(dest, max_size); return got; } // AudioSourceCallback::OnClose implementation: virtual void OnClose(AudioOutputStream* stream) { } // AudioSourceCallback::OnError implementation: virtual void OnError(AudioOutputStream* stream, int code) { } private: base::SyncSocket* socket_; }; struct SyncThreadContext { base::SyncSocket* socket; int sample_rate; double sine_freq; uint32 packet_size; }; // This thread provides the data that the SyncSocketSource above needs // using the other end of a SyncSocket. The protocol is as follows: // // SyncSocketSource ---send 4 bytes ------------> SyncSocketThread // <--- audio packet ---------- // DWORD __stdcall SyncSocketThread(void* context) { SyncThreadContext& ctx = *(reinterpret_cast(context)); const int kTwoSecBytes = AudioManager::kAudioCDSampleRate * 2 * sizeof(uint16); char* buffer = new char[kTwoSecBytes]; SineWaveAudioSource sine(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, ctx.sine_freq, ctx.sample_rate); sine.OnMoreData(NULL, buffer, kTwoSecBytes, 0); int pending_bytes = -1; int times = 0; for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size) { if (ctx.socket->Receive(&pending_bytes, sizeof(pending_bytes)) == 0) break; if ((times > 0) && (pending_bytes < 1000)) __debugbreak(); ctx.socket->Send(&buffer[ix], ctx.packet_size); ++times; } delete buffer; return 0; } } // namespace // Test the basic operation of AudioOutputStream used with a SyncSocket. // The emphasis is to test low-latency with buffers less than 100ms. With // the waveout api it seems not possible to go below 50ms. In this test // you should hear a continous 200Hz tone. // // TODO(cpu): This actually sounds choppy most of the time. Fix it. TEST(WinAudioTest, SyncSocketBasic) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; int sample_rate = AudioManager::kAudioCDSampleRate; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LOW_LATENCY, 1, sample_rate, 16); ASSERT_TRUE(NULL != oas); // compute buffer size for 20ms of audio, 882 samples (mono). const uint32 kSamples20ms = sample_rate / 50 * sizeof(uint16); ASSERT_TRUE(oas->Open(kSamples20ms)); base::SyncSocket* sockets[2]; ASSERT_TRUE(base::SyncSocket::CreatePair(sockets)); SyncSocketSource source(sockets[0]); SyncThreadContext thread_context; thread_context.sample_rate = sample_rate; thread_context.sine_freq = 200.0; thread_context.packet_size = kSamples20ms; thread_context.socket = sockets[1]; HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread, &thread_context, 0, NULL); oas->Start(&source); ::WaitForSingleObject(thread, INFINITE); ::CloseHandle(thread); delete sockets[1]; oas->Stop(); oas->Close(); }