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, |