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