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.
8 #include "base/basictypes.h"
9 #include "base/base_paths.h"
10 #include "base/memory/aligned_memory.h"
11 #include "base/sync_socket.h"
12 #include "base/win/scoped_com_initializer.h"
13 #include "base/win/windows_version.h"
14 #include "media/base/limits.h"
15 #include "media/audio/audio_io.h"
16 #include "media/audio/audio_manager.h"
17 #include "media/audio/audio_unittest_util.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"
24 using ::testing::AnyNumber
;
25 using ::testing::DoAll
;
26 using ::testing::Field
;
27 using ::testing::Invoke
;
28 using ::testing::InSequence
;
29 using ::testing::NiceMock
;
30 using ::testing::NotNull
;
31 using ::testing::Return
;
35 static int ClearData(AudioBus
* audio_bus
, uint32 total_bytes_delay
) {
37 return audio_bus
->frames();
40 // This class allows to find out if the callbacks are occurring as
41 // expected and if any error has been reported.
42 class TestSourceBasic
: public AudioOutputStream::AudioSourceCallback
{
48 // AudioSourceCallback::OnMoreData implementation:
49 int OnMoreData(AudioBus
* audio_bus
, uint32 total_bytes_delay
) override
{
51 // Touch the channel memory value to make sure memory is good.
53 return audio_bus
->frames();
55 // AudioSourceCallback::OnError implementation:
56 void OnError(AudioOutputStream
* stream
) override
{ ++had_error_
; }
57 // Returns how many times OnMoreData() has been called.
58 int callback_count() const {
59 return callback_count_
;
61 // Returns how many times the OnError callback was called.
62 int had_error() const {
66 void set_error(bool error
) {
67 had_error_
+= error
? 1 : 0;
75 const int kMaxNumBuffers
= 3;
76 // Specializes TestSourceBasic to simulate a source that blocks for some time
77 // in the OnMoreData callback.
78 class TestSourceLaggy
: public TestSourceBasic
{
80 explicit TestSourceLaggy(int lag_in_ms
)
81 : lag_in_ms_(lag_in_ms
) {
83 int OnMoreData(AudioBus
* audio_bus
, uint32 total_bytes_delay
) override
{
84 // Call the base, which increments the callback_count_.
85 TestSourceBasic::OnMoreData(audio_bus
, total_bytes_delay
);
86 if (callback_count() > kMaxNumBuffers
) {
89 return audio_bus
->frames();
95 // Helper class to memory map an entire file. The mapping is read-only. Don't
96 // use for gigabyte-sized files. Attempts to write to this memory generate
97 // memory access violations.
98 class ReadOnlyMappedFile
{
100 explicit ReadOnlyMappedFile(const wchar_t* file_name
)
101 : fmap_(NULL
), start_(NULL
), size_(0) {
102 HANDLE file
= ::CreateFileW(file_name
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
103 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
104 if (INVALID_HANDLE_VALUE
== file
)
106 fmap_
= ::CreateFileMappingW(file
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
110 start_
= reinterpret_cast<char*>(::MapViewOfFile(fmap_
, FILE_MAP_READ
,
114 MEMORY_BASIC_INFORMATION mbi
= {0};
115 ::VirtualQuery(start_
, &mbi
, sizeof(mbi
));
116 size_
= mbi
.RegionSize
;
118 ~ReadOnlyMappedFile() {
120 ::UnmapViewOfFile(start_
);
121 ::CloseHandle(fmap_
);
124 // Returns true if the file was successfully mapped.
125 bool is_valid() const {
126 return ((start_
> 0) && (size_
> 0));
128 // Returns the size in bytes of the mapped memory.
129 uint32
size() const {
132 // Returns the memory backing the file.
133 const void* GetChunkAt(uint32 offset
) {
134 return &start_
[offset
];
143 // ===========================================================================
144 // Validation of AudioManager::AUDIO_PCM_LINEAR
147 // The tests can fail on the build bots when somebody connects to them via
148 // remote-desktop and the rdp client installs an audio device that fails to open
149 // at some point, possibly when the connection goes idle.
151 // Test that can it be created and closed.
152 TEST(WinAudioTest
, PCMWaveStreamGetAndClose
) {
153 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
154 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
156 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
157 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_STEREO
,
160 ASSERT_TRUE(NULL
!= oas
);
164 // Test that can it be cannot be created with invalid parameters.
165 TEST(WinAudioTest
, SanityOnMakeParams
) {
166 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
167 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
169 AudioParameters::Format fmt
= AudioParameters::AUDIO_PCM_LINEAR
;
170 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
171 AudioParameters(fmt
, CHANNEL_LAYOUT_UNSUPPORTED
, 8000, 16, 256),
173 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
174 AudioParameters(fmt
, CHANNEL_LAYOUT_MONO
, 1024 * 1024, 16, 256),
176 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
177 AudioParameters(fmt
, CHANNEL_LAYOUT_STEREO
, 8000, 80, 256),
179 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
180 AudioParameters(fmt
, CHANNEL_LAYOUT_UNSUPPORTED
, 8000, 16, 256),
182 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
183 AudioParameters(fmt
, CHANNEL_LAYOUT_STEREO
, -8000, 16, 256),
185 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
186 AudioParameters(fmt
, CHANNEL_LAYOUT_MONO
, 8000, 16, -100),
188 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
189 AudioParameters(fmt
, CHANNEL_LAYOUT_MONO
, 8000, 16, 0),
191 EXPECT_TRUE(NULL
== audio_man
->MakeAudioOutputStream(
192 AudioParameters(fmt
, CHANNEL_LAYOUT_MONO
, 8000, 16,
193 media::limits::kMaxSamplesPerPacket
+ 1),
197 // Test that it can be opened and closed.
198 TEST(WinAudioTest
, PCMWaveStreamOpenAndClose
) {
199 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
200 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
202 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
203 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_STEREO
,
206 ASSERT_TRUE(NULL
!= oas
);
207 EXPECT_TRUE(oas
->Open());
211 // Test that it has a maximum packet size.
212 TEST(WinAudioTest
, PCMWaveStreamOpenLimit
) {
213 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
214 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
216 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
217 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_STEREO
,
218 8000, 16, 1024 * 1024 * 1024),
220 EXPECT_TRUE(NULL
== oas
);
225 // Test potential deadlock situation if the source is slow or blocks for some
226 // time. The actual EXPECT_GT are mostly meaningless and the real test is that
227 // the test completes in reasonable time.
228 TEST(WinAudioTest
, PCMWaveSlowSource
) {
229 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
230 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
232 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
233 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
236 ASSERT_TRUE(NULL
!= oas
);
237 TestSourceLaggy
test_laggy(90);
238 EXPECT_TRUE(oas
->Open());
239 // The test parameters cause a callback every 32 ms and the source is
240 // sleeping for 90 ms, so it is guaranteed that we run out of ready buffers.
241 oas
->Start(&test_laggy
);
243 EXPECT_GT(test_laggy
.callback_count(), 2);
244 EXPECT_FALSE(test_laggy
.had_error());
250 // Test another potential deadlock situation if the thread that calls Start()
251 // gets paused. This test is best when run over RDP with audio enabled. See
252 // bug 19276 for more details.
253 TEST(WinAudioTest
, PCMWaveStreamPlaySlowLoop
) {
254 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
255 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
257 uint32 samples_100_ms
= AudioParameters::kAudioCDSampleRate
/ 10;
258 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
259 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
260 AudioParameters::kAudioCDSampleRate
, 16, samples_100_ms
),
262 ASSERT_TRUE(NULL
!= oas
);
264 SineWaveAudioSource
source(1, 200.0, AudioParameters::kAudioCDSampleRate
);
266 EXPECT_TRUE(oas
->Open());
269 for (int ix
= 0; ix
!= 5; ++ix
) {
278 // This test produces actual audio for .5 seconds on the default wave
279 // device at 44.1K s/sec. Parameters have been chosen carefully so you should
280 // not hear pops or noises while the sound is playing.
281 TEST(WinAudioTest
, PCMWaveStreamPlay200HzTone44Kss
) {
282 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
283 if (!audio_man
->HasAudioOutputDevices()) {
284 LOG(WARNING
) << "No output device detected.";
288 uint32 samples_100_ms
= AudioParameters::kAudioCDSampleRate
/ 10;
289 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
290 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
291 AudioParameters::kAudioCDSampleRate
, 16, samples_100_ms
),
293 ASSERT_TRUE(NULL
!= oas
);
295 SineWaveAudioSource
source(1, 200.0, AudioParameters::kAudioCDSampleRate
);
297 EXPECT_TRUE(oas
->Open());
305 // This test produces actual audio for for .5 seconds on the default wave
306 // device at 22K s/sec. Parameters have been chosen carefully so you should
307 // not hear pops or noises while the sound is playing. The audio also should
308 // sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss.
309 TEST(WinAudioTest
, PCMWaveStreamPlay200HzTone22Kss
) {
310 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
311 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
313 uint32 samples_100_ms
= AudioParameters::kAudioCDSampleRate
/ 20;
314 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
315 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
316 AudioParameters::kAudioCDSampleRate
/ 2, 16,
319 ASSERT_TRUE(NULL
!= oas
);
321 SineWaveAudioSource
source(1, 200.0, AudioParameters::kAudioCDSampleRate
/2);
323 EXPECT_TRUE(oas
->Open());
329 // Test that the volume is within the set limits.
331 oas
->GetVolume(&volume
);
332 EXPECT_LT(volume
, 0.51);
333 EXPECT_GT(volume
, 0.49);
338 // Uses a restricted source to play ~2 seconds of audio for about 5 seconds. We
339 // try hard to generate situation where the two threads are accessing the
340 // object roughly at the same time.
341 TEST(WinAudioTest
, PushSourceFile16KHz
) {
342 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
343 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
345 static const int kSampleRate
= 16000;
346 SineWaveAudioSource
source(1, 200.0, kSampleRate
);
347 // Compute buffer size for 100ms of audio.
348 const uint32 kSamples100ms
= (kSampleRate
/ 1000) * 100;
349 // Restrict SineWaveAudioSource to 100ms of samples.
350 source
.CapSamples(kSamples100ms
);
352 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
353 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
354 kSampleRate
, 16, kSamples100ms
),
356 ASSERT_TRUE(NULL
!= oas
);
358 EXPECT_TRUE(oas
->Open());
363 // We buffer and play at the same time, buffering happens every ~10ms and the
364 // consuming of the buffer happens every ~100ms. We do 100 buffers which
365 // effectively wrap around the file more than once.
366 for (uint32 ix
= 0; ix
!= 100; ++ix
) {
371 // Play a little bit more of the file.
378 // This test is to make sure an AudioOutputStream can be started after it was
379 // stopped. You will here two .5 seconds wave signal separated by 0.5 seconds
381 TEST(WinAudioTest
, PCMWaveStreamPlayTwice200HzTone44Kss
) {
382 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
383 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
385 uint32 samples_100_ms
= AudioParameters::kAudioCDSampleRate
/ 10;
386 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
387 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
388 AudioParameters::kAudioCDSampleRate
, 16, samples_100_ms
),
390 ASSERT_TRUE(NULL
!= oas
);
392 SineWaveAudioSource
source(1, 200.0, AudioParameters::kAudioCDSampleRate
);
393 EXPECT_TRUE(oas
->Open());
396 // Play the wave for .5 seconds.
401 // Sleep to give silence after stopping the AudioOutputStream.
404 // Start again and play for .5 seconds.
412 // With the low latency mode, WASAPI is utilized by default for Vista and
413 // higher and Wave is used for XP and lower. It is possible to utilize a
414 // smaller buffer size for WASAPI than for Wave.
415 TEST(WinAudioTest
, PCMWaveStreamPlay200HzToneLowLatency
) {
416 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
417 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
419 // Use 10 ms buffer size for WASAPI and 50 ms buffer size for Wave.
420 // Take the existing native sample rate into account.
421 const AudioParameters params
= audio_man
->GetDefaultOutputStreamParameters();
422 int sample_rate
= params
.sample_rate();
423 uint32 samples_10_ms
= sample_rate
/ 100;
425 (base::win::GetVersion() <= base::win::VERSION_XP
) ? n
= 5 : n
= 1;
426 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
427 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
428 CHANNEL_LAYOUT_MONO
, sample_rate
,
429 16, n
* samples_10_ms
),
431 ASSERT_TRUE(NULL
!= oas
);
433 SineWaveAudioSource
source(1, 200, sample_rate
);
435 bool opened
= oas
->Open();
437 // It was not possible to open this audio device in mono.
438 // No point in continuing the test so let's break here.
439 LOG(WARNING
) << "Mono is not supported. Skipping test.";
445 // Play the wave for .8 seconds.
452 // Check that the pending bytes value is correct what the stream starts.
453 TEST(WinAudioTest
, PCMWaveStreamPendingBytes
) {
454 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
455 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
457 uint32 samples_100_ms
= AudioParameters::kAudioCDSampleRate
/ 10;
458 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(
459 AudioParameters(AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_MONO
,
460 AudioParameters::kAudioCDSampleRate
, 16, samples_100_ms
),
462 ASSERT_TRUE(NULL
!= oas
);
464 NiceMock
<MockAudioSourceCallback
> source
;
465 EXPECT_TRUE(oas
->Open());
467 uint32 bytes_100_ms
= samples_100_ms
* 2;
469 // Audio output stream has either a double or triple buffer scheme.
470 // We expect the amount of pending bytes will reaching up to 2 times of
471 // |bytes_100_ms| depending on number of buffers used.
472 // From that it would decrease as we are playing the data but not providing
473 // new one. And then we will try to provide zero data so the amount of
474 // pending bytes will go down and eventually read zero.
477 EXPECT_CALL(source
, OnMoreData(NotNull(), 0))
478 .WillOnce(Invoke(ClearData
));
480 // Note: If AudioManagerWin::NumberOfWaveOutBuffers() ever changes, or if this
481 // test is run on Vista, these expectations will fail.
482 EXPECT_CALL(source
, OnMoreData(NotNull(), bytes_100_ms
))
483 .WillOnce(Invoke(ClearData
));
484 EXPECT_CALL(source
, OnMoreData(NotNull(), 2 * bytes_100_ms
))
485 .WillOnce(Invoke(ClearData
));
486 EXPECT_CALL(source
, OnMoreData(NotNull(), 2 * bytes_100_ms
))
488 .WillRepeatedly(Return(0));
489 EXPECT_CALL(source
, OnMoreData(NotNull(), bytes_100_ms
))
491 .WillRepeatedly(Return(0));
492 EXPECT_CALL(source
, OnMoreData(NotNull(), 0))
494 .WillRepeatedly(Return(0));
502 // Simple source that uses a SyncSocket to retrieve the audio data
503 // from a potentially remote thread.
504 class SyncSocketSource
: public AudioOutputStream::AudioSourceCallback
{
506 SyncSocketSource(base::SyncSocket
* socket
, const AudioParameters
& params
)
508 // Setup AudioBus wrapping data we'll receive over the sync socket.
509 data_size_
= AudioBus::CalculateMemorySize(params
);
510 data_
.reset(static_cast<float*>(
511 base::AlignedAlloc(data_size_
, AudioBus::kChannelAlignment
)));
512 audio_bus_
= AudioBus::WrapMemory(params
, data_
.get());
514 ~SyncSocketSource() override
{}
516 // AudioSourceCallback::OnMoreData implementation:
517 int OnMoreData(AudioBus
* audio_bus
, uint32 total_bytes_delay
) override
{
518 socket_
->Send(&total_bytes_delay
, sizeof(total_bytes_delay
));
519 uint32 size
= socket_
->Receive(data_
.get(), data_size_
);
520 DCHECK_EQ(static_cast<size_t>(size
) % sizeof(*audio_bus_
->channel(0)), 0U);
521 audio_bus_
->CopyTo(audio_bus
);
522 return audio_bus_
->frames();
525 // AudioSourceCallback::OnError implementation:
526 void OnError(AudioOutputStream
* stream
) override
{}
529 base::SyncSocket
* socket_
;
531 scoped_ptr
<float, base::AlignedFreeDeleter
> data_
;
532 scoped_ptr
<AudioBus
> audio_bus_
;
535 struct SyncThreadContext
{
536 base::SyncSocket
* socket
;
541 uint32 packet_size_bytes
;
544 // This thread provides the data that the SyncSocketSource above needs
545 // using the other end of a SyncSocket. The protocol is as follows:
547 // SyncSocketSource ---send 4 bytes ------------> SyncSocketThread
548 // <--- audio packet ----------
550 DWORD __stdcall
SyncSocketThread(void* context
) {
551 SyncThreadContext
& ctx
= *(reinterpret_cast<SyncThreadContext
*>(context
));
553 // Setup AudioBus wrapping data we'll pass over the sync socket.
554 scoped_ptr
<float, base::AlignedFreeDeleter
> data(static_cast<float*>(
555 base::AlignedAlloc(ctx
.packet_size_bytes
, AudioBus::kChannelAlignment
)));
556 scoped_ptr
<AudioBus
> audio_bus
= AudioBus::WrapMemory(
557 ctx
.channels
, ctx
.frames
, data
.get());
559 SineWaveAudioSource
sine(1, ctx
.sine_freq
, ctx
.sample_rate
);
560 const int kTwoSecFrames
= ctx
.sample_rate
* 2;
562 uint32 total_bytes_delay
= 0;
564 for (int ix
= 0; ix
< kTwoSecFrames
; ix
+= ctx
.frames
) {
565 if (ctx
.socket
->Receive(&total_bytes_delay
, sizeof(total_bytes_delay
)) == 0)
567 if ((times
> 0) && (total_bytes_delay
< 1000)) __debugbreak();
568 sine
.OnMoreData(audio_bus
.get(), total_bytes_delay
);
569 ctx
.socket
->Send(data
.get(), ctx
.packet_size_bytes
);
576 // Test the basic operation of AudioOutputStream used with a SyncSocket.
577 // The emphasis is to verify that it is possible to feed data to the audio
578 // layer using a source based on SyncSocket. In a real situation we would
579 // go for the low-latency version in combination with SyncSocket, but to keep
580 // the test more simple, AUDIO_PCM_LINEAR is utilized instead. The main
581 // principle of the test still remains and we avoid the additional complexity
582 // related to the two different audio-layers for AUDIO_PCM_LOW_LATENCY.
583 // In this test you should hear a continuous 200Hz tone for 2 seconds.
584 TEST(WinAudioTest
, SyncSocketBasic
) {
585 scoped_ptr
<AudioManager
> audio_man(AudioManager::CreateForTesting());
586 ABORT_AUDIO_TEST_IF_NOT(audio_man
->HasAudioOutputDevices());
588 static const int sample_rate
= AudioParameters::kAudioCDSampleRate
;
589 static const uint32 kSamples20ms
= sample_rate
/ 50;
590 AudioParameters
params(AudioParameters::AUDIO_PCM_LINEAR
,
591 CHANNEL_LAYOUT_MONO
, sample_rate
, 16, kSamples20ms
);
594 AudioOutputStream
* oas
= audio_man
->MakeAudioOutputStream(params
,
596 ASSERT_TRUE(NULL
!= oas
);
598 ASSERT_TRUE(oas
->Open());
600 base::SyncSocket sockets
[2];
601 ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets
[0], &sockets
[1]));
603 SyncSocketSource
source(&sockets
[0], params
);
605 SyncThreadContext thread_context
;
606 thread_context
.sample_rate
= params
.sample_rate();
607 thread_context
.sine_freq
= 200.0;
608 thread_context
.packet_size_bytes
= AudioBus::CalculateMemorySize(params
);
609 thread_context
.frames
= params
.frames_per_buffer();
610 thread_context
.channels
= params
.channels();
611 thread_context
.socket
= &sockets
[1];
613 HANDLE thread
= ::CreateThread(NULL
, 0, SyncSocketThread
,
614 &thread_context
, 0, NULL
);
618 ::WaitForSingleObject(thread
, INFINITE
);
619 ::CloseHandle(thread
);