Chromium Code Reviews| Index: media/audio/win/audio_output_win_unittest.cc | 
| diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc | 
| index d954093fd4df3c34c11959faaa4e7076affe16ae..b3d6a38646442a3384b4c0584f2394dcac5ed863 100644 | 
| --- a/media/audio/win/audio_output_win_unittest.cc | 
| +++ b/media/audio/win/audio_output_win_unittest.cc | 
| @@ -8,6 +8,7 @@ | 
| #include "base/basictypes.h" | 
| #include "base/base_paths.h" | 
| #include "base/file_util.h" | 
| +#include "base/memory/aligned_memory.h" | 
| #include "base/path_service.h" | 
| #include "base/sync_socket.h" | 
| #include "base/win/scoped_com_initializer.h" | 
| @@ -24,6 +25,7 @@ using ::testing::_; | 
| using ::testing::AnyNumber; | 
| using ::testing::DoAll; | 
| using ::testing::Field; | 
| +using ::testing::Invoke; | 
| using ::testing::InSequence; | 
| using ::testing::NiceMock; | 
| using ::testing::NotNull; | 
| @@ -45,14 +47,12 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { | 
| had_error_(0) { | 
| } | 
| // AudioSourceCallback::OnMoreData implementation: | 
| - virtual uint32 OnMoreData(uint8* dest, | 
| - uint32 max_size, | 
| - AudioBuffersState buffers_state) { | 
| + virtual int OnMoreData(AudioBus* audio_bus, | 
| + AudioBuffersState buffers_state) { | 
| ++callback_count_; | 
| - // Touch the first byte to make sure memory is good. | 
| - if (max_size) | 
| - reinterpret_cast<char*>(dest)[0] = 1; | 
| - return max_size; | 
| + // Touch the channel memory value to make sure memory is good. | 
| + audio_bus->Zero(); | 
| + return audio_bus->frames(); | 
| } | 
| // AudioSourceCallback::OnError implementation: | 
| virtual void OnError(AudioOutputStream* stream, int code) { | 
| @@ -77,46 +77,6 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { | 
| }; | 
| const int kMaxNumBuffers = 3; | 
| -// Specializes TestSourceBasic to detect that the AudioStream is using | 
| -// triple buffering correctly. | 
| -class TestSourceTripleBuffer : public TestSourceBasic { | 
| 
 
DaleCurtis
2012/08/22 23:09:21
Misleading test to begin with since it's trying to
 
henrika (OOO until Aug 14)
2012/08/23 14:42:53
ALSA on Windows :-)
 
 | 
| - public: | 
| - TestSourceTripleBuffer() { | 
| - buffer_address_[0] = NULL; | 
| - buffer_address_[1] = NULL; | 
| - buffer_address_[2] = NULL; | 
| - } | 
| - // Override of TestSourceBasic::OnMoreData. | 
| - virtual uint32 OnMoreData(uint8* dest, | 
| - uint32 max_size, | 
| - AudioBuffersState buffers_state) { | 
| - // Call the base, which increments the callback_count_. | 
| - TestSourceBasic::OnMoreData(dest, max_size, buffers_state); | 
| - if (callback_count() % NumberOfWaveOutBuffers() == 2) { | 
| - set_error(!CompareExistingIfNotNULL(2, dest)); | 
| - } else if (callback_count() % NumberOfWaveOutBuffers() == 1) { | 
| - set_error(!CompareExistingIfNotNULL(1, dest)); | 
| - } else { | 
| - set_error(!CompareExistingIfNotNULL(0, dest)); | 
| - } | 
| - if (callback_count() > kMaxNumBuffers) { | 
| - 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_[kMaxNumBuffers]; | 
| -}; | 
| - | 
| // Specializes TestSourceBasic to simulate a source that blocks for some time | 
| // in the OnMoreData callback. | 
| class TestSourceLaggy : public TestSourceBasic { | 
| @@ -124,15 +84,14 @@ class TestSourceLaggy : public TestSourceBasic { | 
| TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) | 
| : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { | 
| } | 
| - virtual uint32 OnMoreData(uint8* dest, | 
| - uint32 max_size, | 
| - AudioBuffersState buffers_state) { | 
| + virtual int OnMoreData(AudioBus* audio_bus, | 
| + AudioBuffersState buffers_state) { | 
| // Call the base, which increments the callback_count_. | 
| - TestSourceBasic::OnMoreData(dest, max_size, buffers_state); | 
| + TestSourceBasic::OnMoreData(audio_bus, buffers_state); | 
| if (callback_count() > kMaxNumBuffers) { | 
| ::Sleep(lag_in_ms_); | 
| } | 
| - return max_size; | 
| + return audio_bus->frames(); | 
| } | 
| private: | 
| int laggy_after_buffer_; | 
| @@ -141,10 +100,14 @@ class TestSourceLaggy : public TestSourceBasic { | 
| class MockAudioSource : public AudioOutputStream::AudioSourceCallback { | 
| public: | 
| - MOCK_METHOD3(OnMoreData, uint32(uint8* dest, | 
| - uint32 max_size, | 
| - AudioBuffersState buffers_state)); | 
| + MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, | 
| + AudioBuffersState buffers_state)); | 
| MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); | 
| + | 
| + static int ClearData(AudioBus* audio_bus, AudioBuffersState buffers_state) { | 
| + audio_bus->Zero(); | 
| + return audio_bus->frames(); | 
| + } | 
| }; | 
| // Helper class to memory map an entire file. The mapping is read-only. Don't | 
| @@ -295,30 +258,6 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) { | 
| 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) { | 
| - scoped_ptr<AudioManager> audio_man(AudioManager::Create()); | 
| - if (!audio_man->HasAudioOutputDevices()) { | 
| - LOG(WARNING) << "No output device detected."; | 
| - return; | 
| - } | 
| - | 
| - AudioOutputStream* oas = audio_man->MakeAudioOutputStream( | 
| - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, | 
| - 16000, 16, 256)); | 
| - ASSERT_TRUE(NULL != oas); | 
| - TestSourceTripleBuffer test_triple_buffer; | 
| - EXPECT_TRUE(oas->Open()); | 
| - oas->Start(&test_triple_buffer); | 
| - ::Sleep(300); | 
| - EXPECT_GT(test_triple_buffer.callback_count(), kMaxNumBuffers); | 
| - 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. | 
| @@ -362,8 +301,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { | 
| AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, | 
| - 200.0, AudioParameters::kAudioCDSampleRate); | 
| + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); | 
| EXPECT_TRUE(oas->Open()); | 
| oas->SetVolume(1.0); | 
| @@ -393,8 +331,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { | 
| AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, | 
| - 200.0, AudioParameters::kAudioCDSampleRate); | 
| + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); | 
| EXPECT_TRUE(oas->Open()); | 
| oas->SetVolume(1.0); | 
| @@ -422,8 +359,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { | 
| samples_100_ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, | 
| - 200.0, AudioParameters::kAudioCDSampleRate/2); | 
| + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); | 
| EXPECT_TRUE(oas->Open()); | 
| @@ -440,11 +376,9 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { | 
| oas->Close(); | 
| } | 
| -// Uses the PushSource to play a 2 seconds file clip for about 5 seconds. We | 
| +// Uses a restricted source to play ~2 seconds of audio 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. | 
| +// object roughly at the same time. | 
| TEST(WinAudioTest, PushSourceFile16KHz) { | 
| scoped_ptr<AudioManager> audio_man(AudioManager::Create()); | 
| if (!audio_man->HasAudioOutputDevices()) { | 
| @@ -452,44 +386,29 @@ TEST(WinAudioTest, PushSourceFile16KHz) { | 
| 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()); | 
| - | 
| + static const int kSampleRate = 16000; | 
| + SineWaveAudioSource source(1, 200.0, kSampleRate); | 
| // Compute buffer size for 100ms of audio. | 
| - const uint32 kSamples100ms = (16000 / 1000) * 100; | 
| - const uint32 kSize100ms = kSamples100ms * 2; | 
| + const uint32 kSamples100ms = (kSampleRate / 1000) * 100; | 
| + // Restrict SineWaveAudioSource to 100ms of samples. | 
| + source.CapSamples(kSamples100ms); | 
| AudioOutputStream* oas = audio_man->MakeAudioOutputStream( | 
| AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, | 
| - 16000, 16, kSamples100ms)); | 
| + kSampleRate, 16, kSamples100ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| EXPECT_TRUE(oas->Open()); | 
| - uint32 offset = 0; | 
| - const uint32 kMaxStartOffset = file_reader.size() - kSize100ms; | 
| + oas->SetVolume(1.0); | 
| + oas->Start(&source); | 
| 
 
DaleCurtis
2012/08/22 23:09:21
Test was broken before, it never started the AOS.
 
 | 
| // We buffer and play at the same time, buffering happens every ~10ms and the | 
| // consuming of the buffer happens every ~100ms. We do 100 buffers which | 
| // effectively wrap around the file more than once. | 
| - PushSource push_source; | 
| for (uint32 ix = 0; ix != 100; ++ix) { | 
| - push_source.Write(file_reader.GetChunkAt(offset), kSize100ms); | 
| - if (ix == 2) { | 
| - // For glitch free, start playing after some buffers are in. | 
| - oas->Start(&push_source); | 
| - } | 
| ::Sleep(10); | 
| - offset += kSize100ms; | 
| - if (offset > kMaxStartOffset) | 
| - offset = 0; | 
| + source.Reset(); | 
| } | 
| // Play a little bit more of the file. | 
| @@ -515,8 +434,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { | 
| AudioParameters::kAudioCDSampleRate, 16, samples_100_ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, | 
| - 200.0, AudioParameters::kAudioCDSampleRate); | 
| + SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); | 
| EXPECT_TRUE(oas->Open()); | 
| oas->SetVolume(1.0); | 
| @@ -561,8 +479,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { | 
| 16, n * samples_10_ms)); | 
| ASSERT_TRUE(NULL != oas); | 
| - SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, | 
| - 200.0, sample_rate); | 
| + SineWaveAudioSource source(1, 200, sample_rate); | 
| bool opened = oas->Open(); | 
| if (!opened) { | 
| @@ -608,35 +525,37 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { | 
| // pending bytes will go down and eventually read zero. | 
| InSequence s; | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, 0))) | 
| - .WillOnce(Return(bytes_100_ms)); | 
| + .WillOnce(Invoke(MockAudioSource::ClearData)); | 
| switch (NumberOfWaveOutBuffers()) { | 
| case 2: | 
| break; // Calls are the same as at end of 3-buffer scheme. | 
| case 3: | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, | 
| bytes_100_ms))) | 
| - .WillOnce(Return(bytes_100_ms)); | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + .WillOnce(Invoke(MockAudioSource::ClearData)); | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, | 
| 2 * bytes_100_ms))) | 
| - .WillOnce(Return(bytes_100_ms)); | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + .WillOnce(Invoke(MockAudioSource::ClearData)); | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, | 
| 2 * bytes_100_ms))) | 
| .Times(AnyNumber()) | 
| .WillRepeatedly(Return(0)); | 
| + break; | 
| 
 
DaleCurtis
2012/08/22 23:09:21
Not sure how this ever passed without this break;
 
 | 
| default: | 
| - ASSERT_TRUE(false) << "Unexpected number of buffers"; | 
| + ASSERT_TRUE(false) | 
| + << "Unexpected number of buffers: " << NumberOfWaveOutBuffers(); | 
| } | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, | 
| bytes_100_ms))) | 
| .Times(AnyNumber()) | 
| .WillRepeatedly(Return(0)); | 
| - EXPECT_CALL(source, OnMoreData(NotNull(), bytes_100_ms, | 
| + EXPECT_CALL(source, OnMoreData(NotNull(), | 
| Field(&AudioBuffersState::pending_bytes, 0))) | 
| .Times(AnyNumber()) | 
| .WillRepeatedly(Return(0)); | 
| @@ -651,19 +570,24 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { | 
| // from a potentially remote thread. | 
| class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { | 
| public: | 
| - explicit SyncSocketSource(base::SyncSocket* socket) | 
| - : socket_(socket) {} | 
| - | 
| - ~SyncSocketSource() { | 
| + SyncSocketSource(base::SyncSocket* socket, const AudioParameters& params) | 
| + : socket_(socket) { | 
| + // Setup AudioBus wrapping data we'll receive over the sync socket. | 
| + data_size_ = AudioBus::CalculateMemorySize(params); | 
| + data_.reset(static_cast<float*>( | 
| + base::AlignedAlloc(data_size_, AudioBus::kChannelAlignment))); | 
| + audio_bus_ = AudioBus::WrapMemory(params, data_.get()); | 
| } | 
| + ~SyncSocketSource() {} | 
| // AudioSourceCallback::OnMoreData implementation: | 
| - virtual uint32 OnMoreData(uint8* dest, | 
| - uint32 max_size, | 
| - AudioBuffersState buffers_state) { | 
| + virtual int OnMoreData(AudioBus* audio_bus, | 
| + AudioBuffersState buffers_state) { | 
| socket_->Send(&buffers_state, sizeof(buffers_state)); | 
| - uint32 got = socket_->Receive(dest, max_size); | 
| - return got; | 
| + uint32 size = socket_->Receive(data_.get(), data_size_); | 
| + DCHECK_EQ(static_cast<size_t>(size) % sizeof(*audio_bus_->channel(0)), 0U); | 
| + audio_bus_->CopyTo(audio_bus); | 
| + return audio_bus_->frames(); | 
| } | 
| // AudioSourceCallback::OnError implementation: | 
| virtual void OnError(AudioOutputStream* stream, int code) { | 
| @@ -671,11 +595,16 @@ class SyncSocketSource : public AudioOutputStream::AudioSourceCallback { | 
| private: | 
| base::SyncSocket* socket_; | 
| + int data_size_; | 
| + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data_; | 
| + scoped_ptr<AudioBus> audio_bus_; | 
| }; | 
| struct SyncThreadContext { | 
| base::SyncSocket* socket; | 
| int sample_rate; | 
| + int channels; | 
| + int frames; | 
| double sine_freq; | 
| uint32 packet_size_bytes; | 
| }; | 
| @@ -689,24 +618,26 @@ struct SyncThreadContext { | 
| DWORD __stdcall SyncSocketThread(void* context) { | 
| SyncThreadContext& ctx = *(reinterpret_cast<SyncThreadContext*>(context)); | 
| - const int kTwoSecBytes = | 
| - AudioParameters::kAudioCDSampleRate * 2 * sizeof(uint16); // NOLINT | 
| - uint8* buffer = new uint8[kTwoSecBytes]; | 
| - SineWaveAudioSource sine(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, | 
| - 1, ctx.sine_freq, ctx.sample_rate); | 
| - sine.OnMoreData(buffer, kTwoSecBytes, AudioBuffersState()); | 
| + // Setup AudioBus wrapping data we'll pass over the sync socket. | 
| + scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data(static_cast<float*>( | 
| + base::AlignedAlloc(ctx.packet_size_bytes, AudioBus::kChannelAlignment))); | 
| + scoped_ptr<AudioBus> audio_bus = AudioBus::WrapMemory( | 
| + ctx.channels, ctx.frames, data.get()); | 
| + | 
| + SineWaveAudioSource sine(1, ctx.sine_freq, ctx.sample_rate); | 
| + const int kTwoSecFrames = ctx.sample_rate * 2; | 
| AudioBuffersState buffers_state; | 
| int times = 0; | 
| - for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size_bytes) { | 
| + for (int ix = 0; ix < kTwoSecFrames; ix += ctx.frames) { | 
| if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0) | 
| break; | 
| if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak(); | 
| - ctx.socket->Send(&buffer[ix], ctx.packet_size_bytes); | 
| + sine.OnMoreData(audio_bus.get(), buffers_state); | 
| + ctx.socket->Send(data.get(), ctx.packet_size_bytes); | 
| ++times; | 
| } | 
| - delete buffer; | 
| return 0; | 
| } | 
| @@ -725,11 +656,13 @@ TEST(WinAudioTest, SyncSocketBasic) { | 
| return; | 
| } | 
| - int sample_rate = AudioParameters::kAudioCDSampleRate; | 
| - const uint32 kSamples20ms = sample_rate / 50; | 
| - AudioOutputStream* oas = audio_man->MakeAudioOutputStream( | 
| - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, | 
| - CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms)); | 
| + static const int sample_rate = AudioParameters::kAudioCDSampleRate; | 
| + static const uint32 kSamples20ms = sample_rate / 50; | 
| + AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, | 
| + CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms); | 
| + | 
| + | 
| + AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params); | 
| ASSERT_TRUE(NULL != oas); | 
| ASSERT_TRUE(oas->Open()); | 
| @@ -737,12 +670,14 @@ TEST(WinAudioTest, SyncSocketBasic) { | 
| base::SyncSocket sockets[2]; | 
| ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets[0], &sockets[1])); | 
| - SyncSocketSource source(&sockets[0]); | 
| + SyncSocketSource source(&sockets[0], params); | 
| SyncThreadContext thread_context; | 
| - thread_context.sample_rate = sample_rate; | 
| + thread_context.sample_rate = params.sample_rate(); | 
| thread_context.sine_freq = 200.0; | 
| - thread_context.packet_size_bytes = kSamples20ms * 2; | 
| + thread_context.packet_size_bytes = AudioBus::CalculateMemorySize(params); | 
| + thread_context.frames = params.frames_per_buffer(); | 
| + thread_context.channels = params.channels(); | 
| thread_context.socket = &sockets[1]; | 
| HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread, |