blob: a17c6b015b47c44016ff264d78dcc2386aeb2927 [file] [log] [blame]
// Copyright (c) 2012 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 "base/bind.h"
#include "base/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/message_loop.h"
#include "base/stl_util.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/data_buffer.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/mock_audio_renderer_sink.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/filters/audio_renderer_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::NiceMock;
using ::testing::StrictMock;
namespace media {
// Constants for distinguishing between muted audio and playing audio when using
// ConsumeBufferedData().
static uint8 kMutedAudio = 0x00;
static uint8 kPlayingAudio = 0x99;
class AudioRendererImplTest : public ::testing::Test {
public:
// Give the decoder some non-garbage media properties.
AudioRendererImplTest()
: renderer_(new AudioRendererImpl(new NiceMock<MockAudioRendererSink>(),
SetDecryptorReadyCB())),
demuxer_stream_(new MockDemuxerStream()),
decoder_(new MockAudioDecoder()),
audio_config_(kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO,
44100, NULL, 0, false) {
EXPECT_CALL(*demuxer_stream_, type())
.WillRepeatedly(Return(DemuxerStream::AUDIO));
EXPECT_CALL(*demuxer_stream_, audio_decoder_config())
.WillRepeatedly(ReturnRef(audio_config_));
// Queue all reads from the decoder by default.
ON_CALL(*decoder_, Read(_))
.WillByDefault(Invoke(this, &AudioRendererImplTest::SaveReadCallback));
// Set up audio properties.
SetSupportedAudioDecoderProperties();
EXPECT_CALL(*decoder_, bits_per_channel())
.Times(AnyNumber());
EXPECT_CALL(*decoder_, channel_layout())
.Times(AnyNumber());
EXPECT_CALL(*decoder_, samples_per_second())
.Times(AnyNumber());
decoders_.push_back(decoder_);
}
virtual ~AudioRendererImplTest() {
message_loop_.RunUntilIdle();
renderer_->Stop(NewExpectedClosure());
}
void SetSupportedAudioDecoderProperties() {
ON_CALL(*decoder_, bits_per_channel())
.WillByDefault(Return(16));
ON_CALL(*decoder_, channel_layout())
.WillByDefault(Return(CHANNEL_LAYOUT_MONO));
ON_CALL(*decoder_, samples_per_second())
.WillByDefault(Return(44100));
}
void SetUnsupportedAudioDecoderProperties() {
ON_CALL(*decoder_, bits_per_channel())
.WillByDefault(Return(3));
ON_CALL(*decoder_, channel_layout())
.WillByDefault(Return(CHANNEL_LAYOUT_UNSUPPORTED));
ON_CALL(*decoder_, samples_per_second())
.WillByDefault(Return(0));
}
MOCK_METHOD1(OnPrerollComplete, void(PipelineStatus));
PipelineStatusCB NewPrerollCB() {
return base::Bind(&AudioRendererImplTest::OnPrerollComplete,
base::Unretained(this));
}
MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
MOCK_METHOD0(OnUnderflow, void());
MOCK_METHOD0(OnEnded, void());
MOCK_METHOD0(OnDisabled, void());
MOCK_METHOD1(OnError, void(PipelineStatus));
void OnAudioTimeCallback(
base::TimeDelta current_time, base::TimeDelta max_time) {
CHECK(current_time <= max_time);
}
void Initialize() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
InitializeWithStatus(PIPELINE_OK);
message_loop_.RunUntilIdle();
int channels = ChannelLayoutToChannelCount(decoder_->channel_layout());
int bytes_per_frame = decoder_->bits_per_channel() * channels / 8;
next_timestamp_.reset(new AudioTimestampHelper(
bytes_per_frame, decoder_->samples_per_second()));
}
void InitializeWithStatus(PipelineStatus expected) {
renderer_->Initialize(
demuxer_stream_,
decoders_,
NewExpectedStatusCB(expected),
base::Bind(&AudioRendererImplTest::OnStatistics,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnEnded,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnDisabled,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnError,
base::Unretained(this)));
}
void Preroll() {
next_timestamp_->SetBaseTimestamp(base::TimeDelta());
// Fill entire buffer to complete prerolling.
EXPECT_CALL(*decoder_, Read(_));
renderer_->Preroll(base::TimeDelta(), NewPrerollCB());
EXPECT_CALL(*this, OnPrerollComplete(PIPELINE_OK));
message_loop_.RunUntilIdle();
DeliverRemainingAudio();
}
void Play() {
renderer_->Play(NewExpectedClosure());
renderer_->SetPlaybackRate(1.0f);
}
void Preroll(base::TimeDelta preroll_time) {
next_timestamp_->SetBaseTimestamp(preroll_time);
// Fill entire buffer to complete prerolling.
EXPECT_CALL(*decoder_, Read(_));
renderer_->Preroll(preroll_time, NewPrerollCB());
EXPECT_CALL(*this, OnPrerollComplete(PIPELINE_OK));
DeliverRemainingAudio();
}
// Delivers |size| bytes with value kPlayingAudio to |renderer_|.
//
// There must be a pending read callback.
void FulfillPendingRead(size_t size) {
CHECK(!read_cb_.is_null());
scoped_refptr<DataBuffer> buffer(new DataBuffer(size));
buffer->SetDataSize(size);
memset(buffer->GetWritableData(), kPlayingAudio, buffer->GetDataSize());
buffer->SetTimestamp(next_timestamp_->GetTimestamp());
buffer->SetDuration(next_timestamp_->GetDuration(buffer->GetDataSize()));
next_timestamp_->AddBytes(buffer->GetDataSize());
base::ResetAndReturn(&read_cb_).Run(AudioDecoder::kOk, buffer);
}
void AbortPendingRead() {
base::ResetAndReturn(&read_cb_).Run(AudioDecoder::kAborted, NULL);
}
// Delivers an end of stream buffer to |renderer_|.
//
// There must be a pending read callback.
void DeliverEndOfStream() {
FulfillPendingRead(0);
}
// Delivers bytes until |renderer_|'s internal buffer is full and no longer
// has pending reads.
void DeliverRemainingAudio() {
CHECK(!read_cb_.is_null());
FulfillPendingRead(bytes_remaining_in_buffer());
CHECK(read_cb_.is_null());
}
// Attempts to consume |size| bytes from |renderer_|'s internal buffer,
// returning true if all |size| bytes were consumed, false if less than
// |size| bytes were consumed.
//
// |muted| is optional and if passed will get set if the byte value of
// the consumed data is muted audio.
bool ConsumeBufferedData(uint32 size, bool* muted) {
scoped_array<uint8> buffer(new uint8[size]);
uint32 bytes_per_frame = (decoder_->bits_per_channel() / 8) *
ChannelLayoutToChannelCount(decoder_->channel_layout());
uint32 requested_frames = size / bytes_per_frame;
uint32 frames_read = renderer_->FillBuffer(
buffer.get(), requested_frames, 0);
if (frames_read > 0 && muted) {
*muted = (buffer[0] == kMutedAudio);
}
return (frames_read == requested_frames);
}
uint32 bytes_buffered() {
return renderer_->algorithm_->bytes_buffered();
}
uint32 buffer_capacity() {
return renderer_->algorithm_->QueueCapacity();
}
uint32 bytes_remaining_in_buffer() {
// This can happen if too much data was delivered, in which case the buffer
// will accept the data but not increase capacity.
if (bytes_buffered() > buffer_capacity()) {
return 0;
}
return buffer_capacity() - bytes_buffered();
}
void CallResumeAfterUnderflow() {
renderer_->ResumeAfterUnderflow(false);
}
// Fixture members.
scoped_refptr<AudioRendererImpl> renderer_;
scoped_refptr<MockDemuxerStream> demuxer_stream_;
scoped_refptr<MockAudioDecoder> decoder_;
AudioRendererImpl::AudioDecoderList decoders_;
AudioDecoder::ReadCB read_cb_;
scoped_ptr<AudioTimestampHelper> next_timestamp_;
AudioDecoderConfig audio_config_;
MessageLoop message_loop_;
private:
void SaveReadCallback(const AudioDecoder::ReadCB& callback) {
CHECK(read_cb_.is_null()) << "Overlapping reads are not permitted";
read_cb_ = callback;
}
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest);
};
TEST_F(AudioRendererImplTest, Initialize_Failed) {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
SetUnsupportedAudioDecoderProperties();
InitializeWithStatus(PIPELINE_ERROR_INITIALIZATION_FAILED);
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, Initialize_Successful) {
Initialize();
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, Initialize_DecoderInitFailure) {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED));
InitializeWithStatus(DECODER_ERROR_NOT_SUPPORTED);
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, Initialize_MultipleDecoders) {
scoped_refptr<MockAudioDecoder> decoder1 = new MockAudioDecoder();
// Insert |decoder1| as the first decoder in the list.
decoders_.push_front(decoder1);
EXPECT_CALL(*decoder1, Initialize(_, _, _))
.WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED));
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
InitializeWithStatus(PIPELINE_OK);
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, Preroll) {
Initialize();
Preroll();
}
TEST_F(AudioRendererImplTest, Play) {
Initialize();
Preroll();
Play();
// Drain internal buffer, we should have a pending read.
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
}
TEST_F(AudioRendererImplTest, EndOfStream) {
Initialize();
Preroll();
Play();
// Drain internal buffer, we should have a pending read.
int audio_bytes_filled = bytes_buffered();
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(audio_bytes_filled, NULL));
// Check and clear |earliest_end_time_| so the ended event fires on the next
// ConsumeBufferedData() call.
base::TimeDelta audio_play_time = base::TimeDelta::FromMicroseconds(
audio_bytes_filled * base::Time::kMicrosecondsPerSecond /
static_cast<float>(renderer_->audio_parameters_.GetBytesPerSecond()));
base::TimeDelta time_until_ended =
renderer_->earliest_end_time_ - base::Time::Now();
EXPECT_TRUE(time_until_ended > base::TimeDelta());
EXPECT_TRUE(time_until_ended <= audio_play_time);
renderer_->earliest_end_time_ = base::Time();
// Fulfill the read with an end-of-stream packet, we shouldn't report ended
// nor have a read until we drain the internal buffer.
DeliverEndOfStream();
// Drain internal buffer, now we should report ended.
EXPECT_CALL(*this, OnEnded());
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
}
TEST_F(AudioRendererImplTest, Underflow) {
Initialize();
Preroll();
Play();
// Drain internal buffer, we should have a pending read.
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
const size_t kDataSize = 1024;
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
renderer_->ResumeAfterUnderflow(false);
// Verify after resuming that we're still not getting data.
//
// NOTE: FillBuffer() satisfies the read but returns muted audio, which
// is crazy http://crbug.com/106600
bool muted = false;
EXPECT_EQ(0u, bytes_buffered());
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, Underflow_EndOfStream) {
Initialize();
Preroll();
Play();
// Drain internal buffer, we should have a pending read.
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
const size_t kDataSize = 1024;
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Deliver a little bit of data.
EXPECT_CALL(*decoder_, Read(_));
FulfillPendingRead(kDataSize);
// Verify we're getting muted audio during underflow.
//
// NOTE: FillBuffer() satisfies the read but returns muted audio, which
// is crazy http://crbug.com/106600
bool muted = false;
EXPECT_EQ(kDataSize, bytes_buffered());
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Now deliver end of stream, we should get our little bit of data back.
DeliverEndOfStream();
EXPECT_CALL(*decoder_, Read(_));
EXPECT_EQ(kDataSize, bytes_buffered());
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
// Deliver another end of stream buffer and attempt to read to make sure
// we're truly at the end of stream.
//
// TODO(scherkus): fix AudioRendererImpl and AudioRendererAlgorithmBase to
// stop reading after receiving an end of stream buffer. It should have also
// fired the ended callback http://crbug.com/106641
DeliverEndOfStream();
EXPECT_CALL(*this, OnEnded());
// Clear |earliest_end_time_| so ended fires on the next ConsumeBufferedData()
// call.
renderer_->earliest_end_time_ = base::Time();
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, Underflow_ResumeFromCallback) {
Initialize();
Preroll();
Play();
// Drain internal buffer, we should have a pending read.
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
const size_t kDataSize = 1024;
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify after resuming that we're still not getting data.
bool muted = false;
EXPECT_EQ(0u, bytes_buffered());
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, AbortPendingRead_Preroll) {
Initialize();
// Start prerolling.
EXPECT_CALL(*decoder_, Read(_));
renderer_->Preroll(base::TimeDelta(), NewPrerollCB());
// Simulate the decoder aborting the pending read.
EXPECT_CALL(*this, OnPrerollComplete(PIPELINE_OK));
AbortPendingRead();
// Preroll again to verify it completed normally.
Preroll(base::TimeDelta::FromSeconds(1));
ASSERT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, AbortPendingRead_Pause) {
Initialize();
Preroll();
Play();
// Partially drain internal buffer so we get a pending read.
EXPECT_CALL(*decoder_, Read(_));
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered() / 2, NULL));
renderer_->Pause(NewExpectedClosure());
AbortPendingRead();
Preroll(base::TimeDelta::FromSeconds(1));
}
} // namespace media