blob: 5682d37508e103384d7d542a0b3dda9a9f2392d3 [file] [log] [blame]
// Copyright (c) 2011 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/stl_util.h"
#include "media/base/data_buffer.h"
#include "media/base/mock_callback.h"
#include "media/base/mock_filter_host.h"
#include "media/base/mock_filters.h"
#include "media/filters/audio_renderer_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::StrictMock;
namespace media {
// Mocked subclass of AudioRendererBase for testing purposes.
class MockAudioRendererBase : public AudioRendererBase {
public:
MockAudioRendererBase()
: AudioRendererBase() {}
virtual ~MockAudioRendererBase() {}
// AudioRenderer implementation.
MOCK_METHOD1(SetVolume, void(float volume));
// AudioRendererBase implementation.
MOCK_METHOD3(OnInitialize, bool(int, ChannelLayout, int));
MOCK_METHOD0(OnStop, void());
// Used for verifying check points during tests.
MOCK_METHOD1(CheckPoint, void(int id));
private:
FRIEND_TEST(AudioRendererBaseTest, OneCompleteReadCycle);
FRIEND_TEST(AudioRendererBaseTest, Underflow);
FRIEND_TEST(AudioRendererBaseTest, Underflow_EndOfStream);
friend class AudioRendererBaseTest;
DISALLOW_COPY_AND_ASSIGN(MockAudioRendererBase);
};
class AudioRendererBaseTest : public ::testing::Test {
public:
// Give the decoder some non-garbage media properties.
AudioRendererBaseTest()
: renderer_(new MockAudioRendererBase()),
decoder_(new MockAudioDecoder()),
pending_reads_(0) {
renderer_->set_host(&host_);
// Queue all reads from the decoder.
EXPECT_CALL(*decoder_, ProduceAudioSamples(_))
.WillRepeatedly(Invoke(this, &AudioRendererBaseTest::EnqueueCallback));
// Set up audio properties.
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));
EXPECT_CALL(*decoder_, bits_per_channel())
.Times(AnyNumber());
EXPECT_CALL(*decoder_, channel_layout())
.Times(AnyNumber());
EXPECT_CALL(*decoder_, samples_per_second())
.Times(AnyNumber());
}
virtual ~AudioRendererBaseTest() {
// Expect a call into the subclass.
EXPECT_CALL(*renderer_, OnStop());
renderer_->Stop(NewExpectedClosure());
}
MOCK_METHOD0(OnUnderflow, void());
base::Closure NewUnderflowClosure() {
return base::Bind(&AudioRendererBaseTest::OnUnderflow,
base::Unretained(this));
}
scoped_refptr<DataBuffer> CreateBuffer(int data_size, uint8 value) {
scoped_refptr<DataBuffer> buffer(new DataBuffer(data_size));
buffer->SetDataSize(data_size);
memset(buffer->GetWritableData(), value, buffer->GetDataSize());
return buffer;
}
void WriteUntilNoPendingReads(int data_size, uint8 value,
uint32* bytes_buffered) {
while (pending_reads_ > 0) {
--pending_reads_;
*bytes_buffered += data_size;
decoder_->ConsumeAudioSamplesForTest(CreateBuffer(data_size, value));
}
}
void ConsumeBufferedData(uint32 data_size, uint8 expected_value,
uint32* bytes_buffered) {
DCHECK(bytes_buffered);
DCHECK_GT(*bytes_buffered, 0u);
base::TimeDelta playback_delay(base::TimeDelta::FromSeconds(1));
scoped_array<uint8> buffer(new uint8[data_size]);
while (*bytes_buffered > 0) {
EXPECT_EQ(data_size, renderer_->FillBuffer(buffer.get(), data_size,
playback_delay, false));
EXPECT_EQ(expected_value, buffer[0]);
*bytes_buffered -= data_size;
}
}
void ExpectUnderflow(uint32 data_size, int checkpoint_value) {
scoped_array<uint8> buffer(new uint8[data_size]);
base::TimeDelta playback_delay(base::TimeDelta::FromSeconds(1));
EXPECT_CALL(*this, OnUnderflow());
EXPECT_CALL(*renderer_, CheckPoint(checkpoint_value));
EXPECT_EQ(0u, renderer_->FillBuffer(buffer.get(), data_size, playback_delay,
false));
renderer_->CheckPoint(checkpoint_value);
}
protected:
static const size_t kMaxQueueSize;
// Fixture members.
scoped_refptr<MockAudioRendererBase> renderer_;
scoped_refptr<MockAudioDecoder> decoder_;
StrictMock<MockFilterHost> host_;
// Number of asynchronous read requests sent to |decoder_|.
size_t pending_reads_;
private:
void EnqueueCallback(scoped_refptr<Buffer> buffer) {
++pending_reads_;
}
DISALLOW_COPY_AND_ASSIGN(AudioRendererBaseTest);
};
const size_t AudioRendererBaseTest::kMaxQueueSize = 1u;
TEST_F(AudioRendererBaseTest, Initialize_Failed) {
InSequence s;
// Our subclass will fail when asked to initialize.
EXPECT_CALL(*renderer_, OnInitialize(_, _, _))
.WillOnce(Return(false));
// We expect to receive an error.
EXPECT_CALL(host_, SetError(PIPELINE_ERROR_INITIALIZATION_FAILED));
// Initialize, we expect to have no reads.
renderer_->Initialize(decoder_, NewExpectedClosure(), NewUnderflowClosure());
EXPECT_EQ(0u, pending_reads_);
}
TEST_F(AudioRendererBaseTest, Initialize_Successful) {
InSequence s;
// Then our subclass will be asked to initialize.
EXPECT_CALL(*renderer_, OnInitialize(_, _, _))
.WillOnce(Return(true));
// Initialize, we shouldn't have any reads.
renderer_->Initialize(decoder_, NewExpectedClosure(), NewUnderflowClosure());
EXPECT_EQ(0u, pending_reads_);
// Now seek to trigger prerolling, verifying the callback hasn't been
// executed yet.
EXPECT_CALL(*renderer_, CheckPoint(0));
renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK));
EXPECT_EQ(kMaxQueueSize, pending_reads_);
renderer_->CheckPoint(0);
// Now satisfy the read requests. Our callback should be executed after
// exiting this loop.
while (pending_reads_) {
scoped_refptr<DataBuffer> buffer(new DataBuffer(1024));
buffer->SetDataSize(1024);
--pending_reads_;
decoder_->ConsumeAudioSamplesForTest(buffer);
}
}
TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) {
InSequence s;
// Then our subclass will be asked to initialize.
EXPECT_CALL(*renderer_, OnInitialize(_, _, _))
.WillOnce(Return(true));
// Initialize, we shouldn't have any reads.
renderer_->Initialize(decoder_, NewExpectedClosure(), NewUnderflowClosure());
EXPECT_EQ(0u, pending_reads_);
// Now seek to trigger prerolling, verifying the callback hasn't been
// executed yet.
EXPECT_CALL(*renderer_, CheckPoint(0));
renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK));
EXPECT_EQ(kMaxQueueSize, pending_reads_);
renderer_->CheckPoint(0);
// Now satisfy the read requests. Our callback should be executed after
// exiting this loop.
const uint32 kDataSize = 1024;
uint32 bytes_buffered = 0;
WriteUntilNoPendingReads(kDataSize, 1, &bytes_buffered);
// Then set the renderer to play state.
renderer_->Play(NewExpectedClosure());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(1.0f, renderer_->GetPlaybackRate());
// Then flush the data in the renderer by reading from it.
uint8 buffer[kDataSize];
for (size_t i = 0; i < kMaxQueueSize; ++i) {
EXPECT_EQ(kDataSize,
renderer_->FillBuffer(buffer, kDataSize,
base::TimeDelta(), true));
bytes_buffered -= kDataSize;
}
// Make sure the read request queue is full.
EXPECT_EQ(kMaxQueueSize, pending_reads_);
// Fulfill the read with an end-of-stream packet.
scoped_refptr<DataBuffer> last_buffer(new DataBuffer(0));
decoder_->ConsumeAudioSamplesForTest(last_buffer);
--pending_reads_;
// We shouldn't report ended until all data has been flushed out.
EXPECT_FALSE(renderer_->HasEnded());
// We should have one less read request in the queue.
EXPECT_EQ(kMaxQueueSize - 1, pending_reads_);
// Flush the entire internal buffer and verify NotifyEnded() isn't called
// right away.
EXPECT_CALL(*renderer_, CheckPoint(1));
EXPECT_EQ(0u, bytes_buffered % kDataSize);
while (bytes_buffered > 0) {
EXPECT_EQ(kDataSize,
renderer_->FillBuffer(buffer, kDataSize,
base::TimeDelta(), true));
bytes_buffered -= kDataSize;
}
// Although we've emptied the buffer, we don't consider ourselves ended until
// we request another buffer. This way we know the last of the audio has
// played.
EXPECT_FALSE(renderer_->HasEnded());
renderer_->CheckPoint(1);
// Do an additional read to trigger NotifyEnded().
EXPECT_CALL(host_, NotifyEnded());
EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize,
base::TimeDelta(), true));
// We should now report ended.
EXPECT_TRUE(renderer_->HasEnded());
// Further reads should return muted audio and not notify any more.
EXPECT_EQ(0u, renderer_->FillBuffer(buffer, kDataSize,
base::TimeDelta(), true));
}
TEST_F(AudioRendererBaseTest, Underflow) {
InSequence s;
base::TimeDelta playback_delay(base::TimeDelta::FromSeconds(1));
// Then our subclass will be asked to initialize.
EXPECT_CALL(*renderer_, OnInitialize(_, _, _))
.WillOnce(Return(true));
// Initialize, we shouldn't have any reads.
renderer_->Initialize(decoder_, NewExpectedClosure(), NewUnderflowClosure());
EXPECT_EQ(0u, pending_reads_);
// Now seek to trigger prerolling, verifying the callback hasn't been
// executed yet.
EXPECT_CALL(*renderer_, CheckPoint(0));
renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK));
EXPECT_EQ(kMaxQueueSize, pending_reads_);
renderer_->CheckPoint(0);
// Now satisfy the read requests. Our callback should be executed after
// exiting this loop.
const uint32 kDataSize = 1024;
uint32 bytes_buffered = 0;
WriteUntilNoPendingReads(kDataSize, 1, &bytes_buffered);
uint32 bytes_for_preroll = bytes_buffered;
// Then set the renderer to play state.
renderer_->Play(NewExpectedClosure());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(1.0f, renderer_->GetPlaybackRate());
// Consume all of the data passed into the renderer.
ConsumeBufferedData(kDataSize, 1, &bytes_buffered);
// Make sure there are read requests pending.
EXPECT_GT(pending_reads_, 0u);
// Verify the next FillBuffer() call triggers calls the underflow callback
// since the queue is empty.
ExpectUnderflow(kDataSize, 1);
// Verify that zeroed out buffers are being returned during the underflow.
uint32 zeros_to_read = 5 * kDataSize;
ConsumeBufferedData(kDataSize, 0, &zeros_to_read);
renderer_->ResumeAfterUnderflow(false);
// Verify we are still getting zeroed out buffers since no new data has been
// pushed to the renderer.
zeros_to_read = 5 * kDataSize;
ConsumeBufferedData(kDataSize, 0, &zeros_to_read);
// Satisfy all pending read requests.
WriteUntilNoPendingReads(kDataSize, 2, &bytes_buffered);
EXPECT_GE(bytes_buffered, bytes_for_preroll);
// Verify that we are now getting the new data.
ConsumeBufferedData(kDataSize, 2, &bytes_buffered);
}
TEST_F(AudioRendererBaseTest, Underflow_EndOfStream) {
InSequence s;
base::TimeDelta playback_delay(base::TimeDelta::FromSeconds(1));
// Then our subclass will be asked to initialize.
EXPECT_CALL(*renderer_, OnInitialize(_, _, _))
.WillOnce(Return(true));
// Initialize, we shouldn't have any reads.
renderer_->Initialize(decoder_, NewExpectedClosure(), NewUnderflowClosure());
EXPECT_EQ(0u, pending_reads_);
// Now seek to trigger prerolling, verifying the callback hasn't been
// executed yet.
EXPECT_CALL(*renderer_, CheckPoint(0));
renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK));
EXPECT_EQ(kMaxQueueSize, pending_reads_);
renderer_->CheckPoint(0);
// Now satisfy the read requests. Our callback should be executed after
// exiting this loop.
const uint32 kDataSize = 1024;
uint32 bytes_buffered = 0;
WriteUntilNoPendingReads(kDataSize, 1, &bytes_buffered);
// Then set the renderer to play state.
renderer_->Play(NewExpectedClosure());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(1.0f, renderer_->GetPlaybackRate());
// Consume all of the data passed into the renderer.
ConsumeBufferedData(kDataSize, 1, &bytes_buffered);
// Make sure there are read requests pending.
EXPECT_GT(pending_reads_, 0u);
// Verify the next FillBuffer() call triggers calls the underflow callback
// since the queue is empty.
ExpectUnderflow(kDataSize, 1);
DCHECK_GE(pending_reads_, 2u);
// Write a buffer.
bytes_buffered += kDataSize;
--pending_reads_;
decoder_->ConsumeAudioSamplesForTest(CreateBuffer(kDataSize, 3));
// Verify we are getting zeroed out buffers since we haven't pushed enough
// data to satisfy preroll.
uint32 zeros_to_read = 5 * kDataSize;
ConsumeBufferedData(kDataSize, 0, &zeros_to_read);
// Send end of stream buffers for all pending reads.
while (pending_reads_ > 0) {
scoped_refptr<DataBuffer> buffer(new DataBuffer(0));
--pending_reads_;
decoder_->ConsumeAudioSamplesForTest(buffer);
}
// Verify that we are now getting the new data.
ConsumeBufferedData(kDataSize, 3, &bytes_buffered);
}
} // namespace media