This source file includes following definitions.
- ClearData
- had_error_
- OnMoreData
- OnMoreIOData
- OnError
- callback_count
- had_error
- set_error
- lag_in_ms_
- OnMoreData
- size_
- is_valid
- size
- GetChunkAt
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- OnMoreData
- OnMoreIOData
- OnError
- SyncSocketThread
- TEST
#include <windows.h>
#include <mmsystem.h>
#include "base/basictypes.h"
#include "base/base_paths.h"
#include "base/memory/aligned_memory.h"
#include "base/path_service.h"
#include "base/sync_socket.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
#include "media/base/limits.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/mock_audio_source_callback.h"
#include "media/audio/simple_sources.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::Field;
using ::testing::Invoke;
using ::testing::InSequence;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using base::win::ScopedCOMInitializer;
namespace media {
static const wchar_t kAudioFile1_16b_m_16K[]
= L"media\\test\\data\\sweep02_16b_mono_16KHz.raw";
static int ClearData(AudioBus* audio_bus, AudioBuffersState buffers_state) {
audio_bus->Zero();
return audio_bus->frames();
}
class TestSourceBasic : public AudioOutputStream::AudioSourceCallback {
public:
explicit TestSourceBasic()
: callback_count_(0),
had_error_(0) {
}
virtual int OnMoreData(AudioBus* audio_bus,
AudioBuffersState buffers_state) {
++callback_count_;
audio_bus->Zero();
return audio_bus->frames();
}
virtual int OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState buffers_state) {
NOTREACHED();
return 0;
}
virtual void OnError(AudioOutputStream* stream) {
++had_error_;
}
int callback_count() const {
return callback_count_;
}
int had_error() const {
return had_error_;
}
void set_error(bool error) {
had_error_ += error ? 1 : 0;
}
private:
int callback_count_;
int had_error_;
};
const int kMaxNumBuffers = 3;
class TestSourceLaggy : public TestSourceBasic {
public:
TestSourceLaggy(int laggy_after_buffer, int lag_in_ms)
: laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) {
}
virtual int OnMoreData(AudioBus* audio_bus,
AudioBuffersState buffers_state) {
TestSourceBasic::OnMoreData(audio_bus, buffers_state);
if (callback_count() > kMaxNumBuffers) {
::Sleep(lag_in_ms_);
}
return audio_bus->frames();
}
private:
int laggy_after_buffer_;
int lag_in_ms_;
};
class ReadOnlyMappedFile {
public:
explicit ReadOnlyMappedFile(const wchar_t* file_name)
: fmap_(NULL), start_(NULL), size_(0) {
HANDLE file = ::CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == file)
return;
fmap_ = ::CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
::CloseHandle(file);
if (!fmap_)
return;
start_ = reinterpret_cast<char*>(::MapViewOfFile(fmap_, FILE_MAP_READ,
0, 0, 0));
if (!start_)
return;
MEMORY_BASIC_INFORMATION mbi = {0};
::VirtualQuery(start_, &mbi, sizeof(mbi));
size_ = mbi.RegionSize;
}
~ReadOnlyMappedFile() {
if (start_) {
::UnmapViewOfFile(start_);
::CloseHandle(fmap_);
}
}
bool is_valid() const {
return ((start_ > 0) && (size_ > 0));
}
uint32 size() const {
return size_;
}
const void* GetChunkAt(uint32 offset) {
return &start_[offset];
}
private:
HANDLE fmap_;
char* start_;
uint32 size_;
};
TEST(WinAudioTest, PCMWaveStreamGetAndClose) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
8000, 16, 256),
std::string());
ASSERT_TRUE(NULL != oas);
oas->Close();
}
TEST(WinAudioTest, SanityOnMakeParams) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR;
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16, 256),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80, 256),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16, 256),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, -100),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 0),
std::string()));
EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream(
AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16,
media::limits::kMaxSamplesPerPacket + 1),
std::string()));
}
TEST(WinAudioTest, PCMWaveStreamOpenAndClose) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
8000, 16, 256),
std::string());
ASSERT_TRUE(NULL != oas);
EXPECT_TRUE(oas->Open());
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamOpenLimit) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
8000, 16, 1024 * 1024 * 1024),
std::string());
EXPECT_TRUE(NULL == oas);
if (oas)
oas->Close();
}
TEST(WinAudioTest, PCMWaveSlowSource) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
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),
std::string());
ASSERT_TRUE(NULL != oas);
TestSourceLaggy test_laggy(2, 90);
EXPECT_TRUE(oas->Open());
oas->Start(&test_laggy);
::Sleep(500);
EXPECT_GT(test_laggy.callback_count(), 2);
EXPECT_FALSE(test_laggy.had_error());
oas->Stop();
::Sleep(500);
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
AudioParameters::kAudioCDSampleRate, 16, samples_100_ms),
std::string());
ASSERT_TRUE(NULL != oas);
SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate);
EXPECT_TRUE(oas->Open());
oas->SetVolume(1.0);
for (int ix = 0; ix != 5; ++ix) {
oas->Start(&source);
::Sleep(10);
oas->Stop();
}
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
AudioParameters::kAudioCDSampleRate, 16, samples_100_ms),
std::string());
ASSERT_TRUE(NULL != oas);
SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate);
EXPECT_TRUE(oas->Open());
oas->SetVolume(1.0);
oas->Start(&source);
::Sleep(500);
oas->Stop();
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 20;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
AudioParameters::kAudioCDSampleRate / 2, 16,
samples_100_ms),
std::string());
ASSERT_TRUE(NULL != oas);
SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2);
EXPECT_TRUE(oas->Open());
oas->SetVolume(0.5);
oas->Start(&source);
::Sleep(500);
double volume = 0.0;
oas->GetVolume(&volume);
EXPECT_LT(volume, 0.51);
EXPECT_GT(volume, 0.49);
oas->Stop();
oas->Close();
}
TEST(WinAudioTest, PushSourceFile16KHz) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
static const int kSampleRate = 16000;
SineWaveAudioSource source(1, 200.0, kSampleRate);
const uint32 kSamples100ms = (kSampleRate / 1000) * 100;
source.CapSamples(kSamples100ms);
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
kSampleRate, 16, kSamples100ms),
std::string());
ASSERT_TRUE(NULL != oas);
EXPECT_TRUE(oas->Open());
oas->SetVolume(1.0);
oas->Start(&source);
for (uint32 ix = 0; ix != 100; ++ix) {
::Sleep(10);
source.Reset();
}
::Sleep(500);
oas->Stop();
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
AudioParameters::kAudioCDSampleRate, 16, samples_100_ms),
std::string());
ASSERT_TRUE(NULL != oas);
SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate);
EXPECT_TRUE(oas->Open());
oas->SetVolume(1.0);
oas->Start(&source);
::Sleep(500);
oas->Stop();
::Sleep(250);
oas->Start(&source);
::Sleep(500);
oas->Stop();
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
const AudioParameters params = audio_man->GetDefaultOutputStreamParameters();
int sample_rate = params.sample_rate();
uint32 samples_10_ms = sample_rate / 100;
int n = 1;
(base::win::GetVersion() <= base::win::VERSION_XP) ? n = 5 : n = 1;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
CHANNEL_LAYOUT_MONO, sample_rate,
16, n * samples_10_ms),
std::string());
ASSERT_TRUE(NULL != oas);
SineWaveAudioSource source(1, 200, sample_rate);
bool opened = oas->Open();
if (!opened) {
LOG(WARNING) << "Mono is not supported. Skipping test.";
oas->Close();
return;
}
oas->SetVolume(1.0);
oas->Start(&source);
::Sleep(800);
oas->Stop();
oas->Close();
}
TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
uint32 samples_100_ms = AudioParameters::kAudioCDSampleRate / 10;
AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
AudioParameters::kAudioCDSampleRate, 16, samples_100_ms),
std::string());
ASSERT_TRUE(NULL != oas);
NiceMock<MockAudioSourceCallback> source;
EXPECT_TRUE(oas->Open());
uint32 bytes_100_ms = samples_100_ms * 2;
InSequence s;
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes, 0)))
.WillOnce(Invoke(ClearData));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes,
bytes_100_ms)))
.WillOnce(Invoke(ClearData));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes,
2 * bytes_100_ms)))
.WillOnce(Invoke(ClearData));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes,
2 * bytes_100_ms)))
.Times(AnyNumber())
.WillRepeatedly(Return(0));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes,
bytes_100_ms)))
.Times(AnyNumber())
.WillRepeatedly(Return(0));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes, 0)))
.Times(AnyNumber())
.WillRepeatedly(Return(0));
oas->Start(&source);
::Sleep(500);
oas->Stop();
oas->Close();
}
class SyncSocketSource : public AudioOutputStream::AudioSourceCallback {
public:
SyncSocketSource(base::SyncSocket* socket, const AudioParameters& params)
: socket_(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() {}
virtual int OnMoreData(AudioBus* audio_bus,
AudioBuffersState buffers_state) {
socket_->Send(&buffers_state, sizeof(buffers_state));
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();
}
virtual int OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState buffers_state) {
NOTREACHED();
return 0;
}
virtual void OnError(AudioOutputStream* stream) {
}
private:
base::SyncSocket* socket_;
int data_size_;
scoped_ptr<float, base::AlignedFreeDeleter> 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;
};
DWORD __stdcall SyncSocketThread(void* context) {
SyncThreadContext& ctx = *(reinterpret_cast<SyncThreadContext*>(context));
scoped_ptr<float, base::AlignedFreeDeleter> 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 < kTwoSecFrames; ix += ctx.frames) {
if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0)
break;
if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak();
sine.OnMoreData(audio_bus.get(), buffers_state);
ctx.socket->Send(data.get(), ctx.packet_size_bytes);
++times;
}
return 0;
}
TEST(WinAudioTest, SyncSocketBasic) {
scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
}
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,
std::string());
ASSERT_TRUE(NULL != oas);
ASSERT_TRUE(oas->Open());
base::SyncSocket sockets[2];
ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets[0], &sockets[1]));
SyncSocketSource source(&sockets[0], params);
SyncThreadContext thread_context;
thread_context.sample_rate = params.sample_rate();
thread_context.sine_freq = 200.0;
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,
&thread_context, 0, NULL);
oas->Start(&source);
::WaitForSingleObject(thread, INFINITE);
::CloseHandle(thread);
oas->Stop();
oas->Close();
}
}