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 76aea2d45ddd7d7930e1ae4b461ad9c28903e363..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 { |
- 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); |
// 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,36 +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; |
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)); |
@@ -652,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) { |
@@ -672,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; |
}; |
@@ -690,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; |
} |
@@ -726,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()); |
@@ -738,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, |