blob: 408e95a5e0d3f54ff167e15d01b4440a2437d88e [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cobalt/media/base/audio_buffer_converter.h"
#include <memory>
#include "cobalt/media/base/audio_buffer.h"
#include "cobalt/media/base/sinc_resampler.h"
#include "cobalt/media/base/test_helpers.h"
#include "starboard/types.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace media {
// Important: Use an odd buffer size here so SIMD issues are caught.
const int kOutFrameSize = 441;
const int kOutSampleRate = 44100;
const ChannelLayout kOutChannelLayout = CHANNEL_LAYOUT_STEREO;
const int kOutChannelCount = 2;
static scoped_refptr<AudioBuffer> MakeTestBuffer(int sample_rate,
ChannelLayout channel_layout,
int channel_count,
int frames) {
return MakeAudioBuffer<uint8_t>(kSampleFormatU8, channel_layout,
channel_count, sample_rate, 0, 1, frames,
base::TimeDelta::FromSeconds(0));
}
class AudioBufferConverterTest : public ::testing::Test {
public:
AudioBufferConverterTest()
: input_frames_(0),
expected_output_frames_(0.0),
output_frames_(0),
output_params_(AudioParameters::AUDIO_PCM_LOW_LATENCY,
kOutChannelLayout, kOutSampleRate, 16, kOutFrameSize) {
audio_buffer_converter_.reset(new AudioBufferConverter(output_params_));
}
void Reset() {
audio_buffer_converter_->Reset();
output_frames_ = expected_output_frames_ = input_frames_ = 0;
}
void AddInput(const scoped_refptr<AudioBuffer>& in) {
if (!in->end_of_stream()) {
input_frames_ += in->frame_count();
expected_output_frames_ +=
in->frame_count() *
(static_cast<double>(output_params_.sample_rate()) /
in->sample_rate());
}
audio_buffer_converter_->AddInput(in);
}
void ConsumeOutput() {
ASSERT_TRUE(audio_buffer_converter_->HasNextBuffer());
scoped_refptr<AudioBuffer> out = audio_buffer_converter_->GetNextBuffer();
if (!out->end_of_stream()) {
output_frames_ += out->frame_count();
EXPECT_EQ(out->sample_rate(), output_params_.sample_rate());
EXPECT_EQ(out->channel_layout(), output_params_.channel_layout());
EXPECT_EQ(out->channel_count(), output_params_.channels());
} else {
EXPECT_FALSE(audio_buffer_converter_->HasNextBuffer());
}
}
void ConsumeAllOutput() {
AddInput(AudioBuffer::CreateEOSBuffer());
while (audio_buffer_converter_->HasNextBuffer()) ConsumeOutput();
EXPECT_EQ(output_frames_, ceil(expected_output_frames_));
}
protected:
std::unique_ptr<AudioBufferConverter> audio_buffer_converter_;
int input_frames_;
double expected_output_frames_;
int output_frames_;
int input_buffers_;
AudioParameters output_params_;
};
TEST_F(AudioBufferConverterTest, PassThrough) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512);
AddInput(in);
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, Downsample) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(48000, kOutChannelLayout, kOutChannelCount, 512);
AddInput(in);
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, Upsample) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(8000, kOutChannelLayout, kOutChannelCount, 512);
AddInput(in);
ConsumeAllOutput();
}
// Test resampling a buffer smaller than the SincResampler's kernel size.
TEST_F(AudioBufferConverterTest, Resample_TinyBuffer) {
AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_STEREO, 2,
SincResampler::kKernelSize - 1));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, Resample_DifferingBufferSizes) {
const int input_sample_rate = 48000;
AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout,
kOutChannelCount, 100));
AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout,
kOutChannelCount, 200));
AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout,
kOutChannelCount, 300));
AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout,
kOutChannelCount, 400));
AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout,
kOutChannelCount, 500));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ChannelDownmix) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_MONO, 1, 512);
AddInput(in);
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ChannelUpmix) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_5_1, 6, 512);
AddInput(in);
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ResampleAndRemix) {
scoped_refptr<AudioBuffer> in =
MakeTestBuffer(48000, CHANNEL_LAYOUT_5_1, 6, 512);
AddInput(in);
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ConfigChange_SampleRate) {
AddInput(MakeTestBuffer(48000, kOutChannelLayout, kOutChannelCount, 512));
AddInput(MakeTestBuffer(44100, kOutChannelLayout, kOutChannelCount, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ConfigChange_ChannelLayout) {
AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_STEREO, 2, 512));
AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_MONO, 1, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ConfigChange_SampleRateAndChannelLayout) {
AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512));
AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_MONO, 1, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ConfigChange_Multiple) {
AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512));
AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_MONO, 1, 512));
AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_5_1, 6, 512));
AddInput(MakeTestBuffer(22050, CHANNEL_LAYOUT_STEREO, 2, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, Reset) {
AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512));
Reset();
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ResampleThenReset) {
// Resampling is likely to leave some data buffered in AudioConverter's
// fifo or resampler, so make sure Reset() cleans that all up.
AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_STEREO, 2, 512));
Reset();
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, ResetThenConvert) {
AddInput(
MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512));
Reset();
// Make sure we can keep using the AudioBufferConverter after we've Reset().
AddInput(
MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, DiscreteChannelLayout) {
output_params_ =
AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
CHANNEL_LAYOUT_DISCRETE, kOutSampleRate, 16, 512);
output_params_.set_channels_for_discrete(2);
audio_buffer_converter_.reset(new AudioBufferConverter(output_params_));
AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_STEREO, 2, 512));
ConsumeAllOutput();
}
TEST_F(AudioBufferConverterTest, LargeBuffersResampling) {
output_params_ = AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
kOutChannelLayout, kOutSampleRate, 16, 2048);
audio_buffer_converter_.reset(new AudioBufferConverter(output_params_));
const int kInputSampleRate = 48000;
const int kInputFrameSize = 8192;
ASSERT_NE(kInputSampleRate, kOutSampleRate);
const int kInputBuffers = 3;
for (int i = 0; i < kInputBuffers; ++i) {
AddInput(MakeTestBuffer(kInputSampleRate, kOutChannelLayout,
kOutChannelCount, kInputFrameSize));
}
// Do not add an EOS packet here, as it will invoke flushing.
while (audio_buffer_converter_->HasNextBuffer()) ConsumeOutput();
// Since the input buffer size is a multiple of the input request size there
// should never be any frames remaining at this point.
ASSERT_EQ(kInputFrameSize %
audio_buffer_converter_->input_buffer_size_for_testing(),
0);
EXPECT_EQ(0, audio_buffer_converter_->input_frames_left_for_testing());
}
} // namespace media
} // namespace cobalt