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