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