blob: ef00769db4fb7a73cbd83362f4967063f7a2efef [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/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_buffer_converter.h"
#include "media/base/audio_hardware_config.h"
#include "media/base/audio_splicer.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/fake_audio_renderer_sink.h"
#include "media/base/gmock_callback_support.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 ::base::Time;
using ::base::TimeTicks;
using ::base::TimeDelta;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SaveArg;
namespace media {
// Constants to specify the type of audio data used.
static AudioCodec kCodec = kCodecVorbis;
static SampleFormat kSampleFormat = kSampleFormatPlanarF32;
static ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
static int kChannelCount = 2;
static int kChannels = ChannelLayoutToChannelCount(kChannelLayout);
static int kSamplesPerSecond = 44100;
// Use a different output sample rate so the AudioBufferConverter is invoked.
static int kOutputSamplesPerSecond = 48000;
// Constants for distinguishing between muted audio and playing audio when using
// ConsumeBufferedData(). Must match the type needed by kSampleFormat.
static float kMutedAudio = 0.0f;
static float kPlayingAudio = 0.5f;
static const int kDataSize = 1024;
ACTION_P(EnterPendingDecoderInitStateAction, test) {
test->EnterPendingDecoderInitState(arg1);
}
class AudioRendererImplTest : public ::testing::Test {
public:
// Give the decoder some non-garbage media properties.
AudioRendererImplTest()
: hardware_config_(AudioParameters(), AudioParameters()),
needs_stop_(true),
demuxer_stream_(DemuxerStream::AUDIO),
decoder_(new MockAudioDecoder()),
last_time_update_(kNoTimestamp()) {
AudioDecoderConfig audio_config(kCodec,
kSampleFormat,
kChannelLayout,
kSamplesPerSecond,
NULL,
0,
false);
demuxer_stream_.set_audio_decoder_config(audio_config);
// Used to save callbacks and run them at a later time.
EXPECT_CALL(*decoder_, Decode(_, _))
.WillRepeatedly(Invoke(this, &AudioRendererImplTest::DecodeDecoder));
EXPECT_CALL(*decoder_, Reset(_))
.WillRepeatedly(Invoke(this, &AudioRendererImplTest::ResetDecoder));
// Mock out demuxer reads.
EXPECT_CALL(demuxer_stream_, Read(_)).WillRepeatedly(
RunCallback<0>(DemuxerStream::kOk,
scoped_refptr<DecoderBuffer>(new DecoderBuffer(0))));
EXPECT_CALL(demuxer_stream_, SupportsConfigChanges())
.WillRepeatedly(Return(true));
AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
kChannelLayout,
kOutputSamplesPerSecond,
SampleFormatToBytesPerChannel(kSampleFormat) * 8,
512);
hardware_config_.UpdateOutputConfig(out_params);
ScopedVector<AudioDecoder> decoders;
decoders.push_back(decoder_);
sink_ = new FakeAudioRendererSink();
renderer_.reset(new AudioRendererImpl(message_loop_.message_loop_proxy(),
sink_,
decoders.Pass(),
SetDecryptorReadyCB(),
&hardware_config_));
// Stub out time.
renderer_->set_now_cb_for_testing(base::Bind(
&AudioRendererImplTest::GetTime, base::Unretained(this)));
}
virtual ~AudioRendererImplTest() {
SCOPED_TRACE("~AudioRendererImplTest()");
if (needs_stop_) {
WaitableMessageLoopEvent event;
renderer_->Stop(event.GetClosure());
event.RunAndWait();
}
}
void ExpectUnsupportedAudioDecoder() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(DoAll(SaveArg<2>(&output_cb_),
RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)));
}
MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
MOCK_METHOD0(OnUnderflow, void());
MOCK_METHOD1(OnError, void(PipelineStatus));
void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) {
CHECK(current_time <= max_time);
last_time_update_ = current_time;
}
void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) {
renderer_->Initialize(
&demuxer_stream_,
pipeline_status_cb,
base::Bind(&AudioRendererImplTest::OnStatistics,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
base::Unretained(this)),
ended_event_.GetClosure(),
base::Bind(&AudioRendererImplTest::OnError,
base::Unretained(this)));
}
void Initialize() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(DoAll(SaveArg<2>(&output_cb_),
RunCallback<1>(PIPELINE_OK)));
EXPECT_CALL(*decoder_, Stop());
InitializeWithStatus(PIPELINE_OK);
next_timestamp_.reset(new AudioTimestampHelper(
hardware_config_.GetOutputConfig().sample_rate()));
}
void InitializeWithStatus(PipelineStatus expected) {
SCOPED_TRACE(base::StringPrintf("InitializeWithStatus(%d)", expected));
WaitableMessageLoopEvent event;
InitializeRenderer(event.GetPipelineStatusCB());
event.RunAndWaitForStatus(expected);
// We should have no reads.
EXPECT_TRUE(decode_cb_.is_null());
}
void InitializeAndStop() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(DoAll(SaveArg<2>(&output_cb_),
RunCallback<1>(PIPELINE_OK)));
EXPECT_CALL(*decoder_, Stop());
WaitableMessageLoopEvent event;
InitializeRenderer(event.GetPipelineStatusCB());
// Stop before we let the MessageLoop run, this simulates an interleaving
// in which we end up calling Stop() while the OnDecoderSelected callback
// is in flight.
renderer_->Stop(NewExpectedClosure());
event.RunAndWaitForStatus(PIPELINE_ERROR_ABORT);
EXPECT_EQ(renderer_->state_, AudioRendererImpl::kStopped);
}
void InitializeAndStopDuringDecoderInit() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
.WillOnce(DoAll(SaveArg<2>(&output_cb_),
EnterPendingDecoderInitStateAction(this)));
EXPECT_CALL(*decoder_, Stop());
WaitableMessageLoopEvent event;
InitializeRenderer(event.GetPipelineStatusCB());
base::RunLoop().RunUntilIdle();
DCHECK(!init_decoder_cb_.is_null());
renderer_->Stop(NewExpectedClosure());
base::ResetAndReturn(&init_decoder_cb_).Run(PIPELINE_OK);
event.RunAndWaitForStatus(PIPELINE_ERROR_ABORT);
EXPECT_EQ(renderer_->state_, AudioRendererImpl::kStopped);
}
void EnterPendingDecoderInitState(PipelineStatusCB cb) {
init_decoder_cb_ = cb;
}
void Flush() {
WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure());
flush_event.RunAndWait();
EXPECT_FALSE(IsReadPending());
}
void Preroll() {
Preroll(0, PIPELINE_OK);
}
void Preroll(int timestamp_ms, PipelineStatus expected) {
SCOPED_TRACE(base::StringPrintf("Preroll(%d, %d)", timestamp_ms, expected));
TimeDelta timestamp = TimeDelta::FromMilliseconds(timestamp_ms);
next_timestamp_->SetBaseTimestamp(timestamp);
// Fill entire buffer to complete prerolling.
WaitableMessageLoopEvent event;
renderer_->Preroll(timestamp, event.GetPipelineStatusCB());
WaitForPendingRead();
DeliverRemainingAudio();
event.RunAndWaitForStatus(PIPELINE_OK);
}
void StartRendering() {
renderer_->StartRendering();
renderer_->SetPlaybackRate(1.0f);
}
void StopRendering() {
renderer_->StopRendering();
}
void Seek() {
StopRendering();
Flush();
Preroll();
}
void WaitForEnded() {
SCOPED_TRACE("WaitForEnded()");
ended_event_.RunAndWait();
}
bool IsReadPending() const {
return !decode_cb_.is_null();
}
void WaitForPendingRead() {
SCOPED_TRACE("WaitForPendingRead()");
if (!decode_cb_.is_null())
return;
DCHECK(wait_for_pending_decode_cb_.is_null());
WaitableMessageLoopEvent event;
wait_for_pending_decode_cb_ = event.GetClosure();
event.RunAndWait();
DCHECK(!decode_cb_.is_null());
DCHECK(wait_for_pending_decode_cb_.is_null());
}
// Delivers |size| frames with value kPlayingAudio to |renderer_|.
void SatisfyPendingRead(int size) {
CHECK_GT(size, 0);
CHECK(!decode_cb_.is_null());
scoped_refptr<AudioBuffer> buffer =
MakeAudioBuffer<float>(kSampleFormat,
kChannelLayout,
kChannelCount,
kSamplesPerSecond,
kPlayingAudio,
0.0f,
size,
next_timestamp_->GetTimestamp());
next_timestamp_->AddFrames(size);
DeliverBuffer(AudioDecoder::kOk, buffer);
}
void DeliverEndOfStream() {
DCHECK(!decode_cb_.is_null());
// Return EOS buffer to trigger EOS frame.
EXPECT_CALL(demuxer_stream_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kOk,
DecoderBuffer::CreateEOSBuffer()));
// Satify pending |decode_cb_| to trigger a new DemuxerStream::Read().
message_loop_.PostTask(
FROM_HERE,
base::Bind(base::ResetAndReturn(&decode_cb_), AudioDecoder::kOk));
WaitForPendingRead();
message_loop_.PostTask(
FROM_HERE,
base::Bind(base::ResetAndReturn(&decode_cb_), AudioDecoder::kOk));
message_loop_.RunUntilIdle();
}
// Delivers frames until |renderer_|'s internal buffer is full and no longer
// has pending reads.
void DeliverRemainingAudio() {
SatisfyPendingRead(frames_remaining_in_buffer());
}
// Attempts to consume |requested_frames| frames from |renderer_|'s internal
// buffer, returning true if all |requested_frames| frames were consumed,
// false if less than |requested_frames| frames were consumed.
//
// |muted| is optional and if passed will get set if the value of
// the consumed data is muted audio.
bool ConsumeBufferedData(int requested_frames, bool* muted) {
scoped_ptr<AudioBus> bus =
AudioBus::Create(kChannels, std::max(requested_frames, 1));
int frames_read;
if (!sink_->Render(bus.get(), 0, &frames_read)) {
if (muted)
*muted = true;
return false;
}
if (muted)
*muted = frames_read < 1 || bus->channel(0)[0] == kMutedAudio;
return frames_read == requested_frames;
}
// Attempts to consume all data available from the renderer. Returns the
// number of frames read. Since time is frozen, the audio delay will increase
// as frames come in.
int ConsumeAllBufferedData() {
int frames_read = 0;
int total_frames_read = 0;
scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, 1024);
do {
TimeDelta audio_delay = TimeDelta::FromMicroseconds(
total_frames_read * Time::kMicrosecondsPerSecond /
static_cast<float>(hardware_config_.GetOutputConfig().sample_rate()));
frames_read = renderer_->Render(
bus.get(), audio_delay.InMilliseconds());
total_frames_read += frames_read;
} while (frames_read > 0);
return total_frames_read;
}
int frames_buffered() {
return renderer_->algorithm_->frames_buffered();
}
int buffer_capacity() {
return renderer_->algorithm_->QueueCapacity();
}
int frames_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 (frames_buffered() > buffer_capacity()) {
return 0;
}
return buffer_capacity() - frames_buffered();
}
void CallResumeAfterUnderflow() {
renderer_->ResumeAfterUnderflow();
}
TimeDelta CalculatePlayTime(int frames_filled) {
return TimeDelta::FromMicroseconds(
frames_filled * Time::kMicrosecondsPerSecond /
renderer_->audio_parameters_.sample_rate());
}
void EndOfStreamTest(float playback_rate) {
Initialize();
Preroll();
StartRendering();
renderer_->SetPlaybackRate(playback_rate);
// Drain internal buffer, we should have a pending read.
int total_frames = frames_buffered();
int frames_filled = ConsumeAllBufferedData();
WaitForPendingRead();
// Due to how the cross-fade algorithm works we won't get an exact match
// between the ideal and expected number of frames consumed. In the faster
// than normal playback case, more frames are created than should exist and
// vice versa in the slower than normal playback case.
const float kEpsilon = 0.20 * (total_frames / playback_rate);
EXPECT_NEAR(frames_filled, total_frames / playback_rate, kEpsilon);
// Figure out how long until the ended event should fire.
TimeDelta audio_play_time = CalculatePlayTime(frames_filled);
DVLOG(1) << "audio_play_time = " << audio_play_time.InSecondsF();
// 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();
// Advance time half way without an ended expectation.
AdvanceTime(audio_play_time / 2);
ConsumeBufferedData(frames_buffered(), NULL);
// Advance time by other half and expect the ended event.
AdvanceTime(audio_play_time / 2);
ConsumeBufferedData(frames_buffered(), NULL);
WaitForEnded();
}
void AdvanceTime(TimeDelta time) {
base::AutoLock auto_lock(lock_);
time_ += time;
}
void force_config_change() {
renderer_->OnConfigChange();
}
int converter_input_frames_left() const {
return renderer_->buffer_converter_->input_frames_left_for_testing();
}
bool splicer_has_next_buffer() const {
return renderer_->splicer_->HasNextBuffer();
}
base::TimeDelta last_time_update() const {
return last_time_update_;
}
// Fixture members.
base::MessageLoop message_loop_;
scoped_ptr<AudioRendererImpl> renderer_;
scoped_refptr<FakeAudioRendererSink> sink_;
AudioHardwareConfig hardware_config_;
// Whether or not the test needs the destructor to call Stop() on
// |renderer_| at destruction.
bool needs_stop_;
private:
TimeTicks GetTime() {
base::AutoLock auto_lock(lock_);
return time_;
}
void DecodeDecoder(const scoped_refptr<DecoderBuffer>& buffer,
const AudioDecoder::DecodeCB& decode_cb) {
// We shouldn't ever call Read() after Stop():
EXPECT_TRUE(stop_decoder_cb_.is_null());
// TODO(scherkus): Make this a DCHECK after threading semantics are fixed.
if (base::MessageLoop::current() != &message_loop_) {
message_loop_.PostTask(FROM_HERE, base::Bind(
&AudioRendererImplTest::DecodeDecoder,
base::Unretained(this), buffer, decode_cb));
return;
}
CHECK(decode_cb_.is_null()) << "Overlapping decodes are not permitted";
decode_cb_ = decode_cb;
// Wake up WaitForPendingRead() if needed.
if (!wait_for_pending_decode_cb_.is_null())
base::ResetAndReturn(&wait_for_pending_decode_cb_).Run();
}
void ResetDecoder(const base::Closure& reset_cb) {
if (!decode_cb_.is_null()) {
// |reset_cb| will be called in DeliverBuffer(), after the decoder is
// flushed.
reset_cb_ = reset_cb;
return;
}
message_loop_.PostTask(FROM_HERE, reset_cb);
}
void DeliverBuffer(AudioDecoder::Status status,
const scoped_refptr<AudioBuffer>& buffer) {
CHECK(!decode_cb_.is_null());
if (buffer && !buffer->end_of_stream())
output_cb_.Run(buffer);
base::ResetAndReturn(&decode_cb_).Run(status);
if (!reset_cb_.is_null())
base::ResetAndReturn(&reset_cb_).Run();
message_loop_.RunUntilIdle();
}
MockDemuxerStream demuxer_stream_;
MockAudioDecoder* decoder_;
// Used for stubbing out time in the audio callback thread.
base::Lock lock_;
TimeTicks time_;
// Used for satisfying reads.
AudioDecoder::OutputCB output_cb_;
AudioDecoder::DecodeCB decode_cb_;
base::Closure reset_cb_;
scoped_ptr<AudioTimestampHelper> next_timestamp_;
WaitableMessageLoopEvent ended_event_;
// Run during DecodeDecoder() to unblock WaitForPendingRead().
base::Closure wait_for_pending_decode_cb_;
base::Closure stop_decoder_cb_;
PipelineStatusCB init_decoder_cb_;
base::TimeDelta last_time_update_;
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest);
};
TEST_F(AudioRendererImplTest, Initialize_Successful) {
Initialize();
}
TEST_F(AudioRendererImplTest, Initialize_DecoderInitFailure) {
ExpectUnsupportedAudioDecoder();
InitializeWithStatus(DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(AudioRendererImplTest, Preroll) {
Initialize();
Preroll();
}
TEST_F(AudioRendererImplTest, StartRendering) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
}
TEST_F(AudioRendererImplTest, EndOfStream) {
EndOfStreamTest(1.0);
}
TEST_F(AudioRendererImplTest, EndOfStream_FasterPlaybackSpeed) {
EndOfStreamTest(2.0);
}
TEST_F(AudioRendererImplTest, EndOfStream_SlowerPlaybackSpeed) {
EndOfStreamTest(0.5);
}
TEST_F(AudioRendererImplTest, Underflow) {
Initialize();
Preroll();
int initial_capacity = buffer_capacity();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
renderer_->ResumeAfterUnderflow();
// Verify after resuming that we're still not getting data.
bool muted = false;
EXPECT_EQ(0, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Verify that the buffer capacity increased as a result of the underflow.
EXPECT_GT(buffer_capacity(), initial_capacity);
// Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
Initialize();
Preroll();
int initial_capacity = buffer_capacity();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify that the buffer capacity increased as a result of resuming after
// underflow.
EXPECT_EQ(buffer_capacity(), initial_capacity);
renderer_->ResumeAfterUnderflow();
EXPECT_GT(buffer_capacity(), initial_capacity);
// Verify that the buffer capacity is restored to the |initial_capacity|.
DeliverEndOfStream();
Flush();
EXPECT_EQ(buffer_capacity(), initial_capacity);
}
TEST_F(AudioRendererImplTest, Underflow_FlushWhileUnderflowed) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify that we can still Flush() before entering the rebuffering state.
DeliverEndOfStream();
Flush();
}
TEST_F(AudioRendererImplTest, Underflow_EndOfStream) {
Initialize();
Preroll();
StartRendering();
// Figure out how long until the ended event should fire. Since
// ConsumeBufferedData() doesn't provide audio delay information, the time
// until the ended event fires is equivalent to the longest buffered section,
// which is the initial frames_buffered() read.
TimeDelta time_until_ended = CalculatePlayTime(frames_buffered());
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Deliver a little bit of data.
SatisfyPendingRead(kDataSize);
WaitForPendingRead();
// Verify we're getting muted audio during underflow. Note: Since resampling
// is active, the number of frames_buffered() won't always match kDataSize.
bool muted = false;
const int kInitialFramesBuffered = 1114;
EXPECT_EQ(kInitialFramesBuffered, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kInitialFramesBuffered, &muted));
EXPECT_TRUE(muted);
// Now deliver end of stream, we should get our little bit of data back.
DeliverEndOfStream();
const int kNextFramesBuffered = 1408;
EXPECT_EQ(kNextFramesBuffered, frames_buffered());
EXPECT_TRUE(ConsumeBufferedData(kNextFramesBuffered, &muted));
EXPECT_FALSE(muted);
// Attempt to read to make sure we're truly at the end of stream.
AdvanceTime(time_until_ended);
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
WaitForEnded();
}
TEST_F(AudioRendererImplTest, Underflow_ResumeFromCallback) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
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(0, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, Underflow_SetPlaybackRate) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
EXPECT_EQ(0, frames_buffered());
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Simulate playback being paused.
renderer_->SetPlaybackRate(0);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Deliver data to resolve the underflow.
DeliverRemainingAudio();
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Simulate playback being resumed.
renderer_->SetPlaybackRate(1);
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
}
TEST_F(AudioRendererImplTest, Underflow_PausePlay) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
EXPECT_EQ(0, frames_buffered());
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Simulate playback being paused, and then played again.
renderer_->SetPlaybackRate(0.0);
renderer_->SetPlaybackRate(1.0);
// Deliver data to resolve the underflow.
DeliverRemainingAudio();
// We should have resumed playing now.
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
}
TEST_F(AudioRendererImplTest, PendingRead_Flush) {
Initialize();
Preroll();
StartRendering();
// Partially drain internal buffer so we get a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
WaitForPendingRead();
StopRendering();
EXPECT_TRUE(IsReadPending());
// Start flushing.
WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure());
SatisfyPendingRead(kDataSize);
flush_event.RunAndWait();
EXPECT_FALSE(IsReadPending());
// Preroll again to a different timestamp and verify it completed normally.
Preroll(1000, PIPELINE_OK);
}
TEST_F(AudioRendererImplTest, PendingRead_Stop) {
Initialize();
Preroll();
StartRendering();
// Partially drain internal buffer so we get a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
WaitForPendingRead();
StopRendering();
EXPECT_TRUE(IsReadPending());
WaitableMessageLoopEvent stop_event;
renderer_->Stop(stop_event.GetClosure());
needs_stop_ = false;
SatisfyPendingRead(kDataSize);
stop_event.RunAndWait();
EXPECT_FALSE(IsReadPending());
}
TEST_F(AudioRendererImplTest, PendingFlush_Stop) {
Initialize();
Preroll();
StartRendering();
// Partially drain internal buffer so we get a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
WaitForPendingRead();
StopRendering();
EXPECT_TRUE(IsReadPending());
// Start flushing.
WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure());
SatisfyPendingRead(kDataSize);
WaitableMessageLoopEvent event;
renderer_->Stop(event.GetClosure());
event.RunAndWait();
needs_stop_ = false;
}
TEST_F(AudioRendererImplTest, InitializeThenStop) {
InitializeAndStop();
}
TEST_F(AudioRendererImplTest, InitializeThenStopDuringDecoderInit) {
InitializeAndStopDuringDecoderInit();
}
TEST_F(AudioRendererImplTest, ConfigChangeDrainsConverter) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Deliver a little bit of data. Use an odd data size to ensure there is data
// left in the AudioBufferConverter. Ensure no buffers are in the splicer.
SatisfyPendingRead(2053);
EXPECT_FALSE(splicer_has_next_buffer());
EXPECT_GT(converter_input_frames_left(), 0);
// Force a config change and then ensure all buffered data has been put into
// the splicer.
force_config_change();
EXPECT_TRUE(splicer_has_next_buffer());
EXPECT_EQ(0, converter_input_frames_left());
}
TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) {
Initialize();
Preroll();
StartRendering();
AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond);
EXPECT_EQ(kNoTimestamp(), last_time_update());
// Preroll() should be buffered some data, consume half of it now.
int frames_to_consume = frames_buffered() / 2;
EXPECT_TRUE(ConsumeBufferedData(frames_to_consume, NULL));
WaitForPendingRead();
base::RunLoop().RunUntilIdle();
// ConsumeBufferedData() uses an audio delay of zero, so ensure we received
// a time update that's equal to |kFramesToConsume| from above.
timestamp_helper.SetBaseTimestamp(base::TimeDelta());
timestamp_helper.AddFrames(frames_to_consume);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update());
// The next time update should match the remaining frames_buffered(), but only
// after running the message loop.
frames_to_consume = frames_buffered();
EXPECT_TRUE(ConsumeBufferedData(frames_to_consume, NULL));
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update());
base::RunLoop().RunUntilIdle();
timestamp_helper.AddFrames(frames_to_consume);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update());
}
TEST_F(AudioRendererImplTest, ImmediateEndOfStream) {
Initialize();
{
SCOPED_TRACE("Preroll()");
WaitableMessageLoopEvent event;
renderer_->Preroll(base::TimeDelta(), event.GetPipelineStatusCB());
WaitForPendingRead();
DeliverEndOfStream();
event.RunAndWaitForStatus(PIPELINE_OK);
}
StartRendering();
// Read a single frame. We shouldn't be able to satisfy it.
EXPECT_FALSE(ConsumeBufferedData(1, NULL));
WaitForEnded();
}
TEST_F(AudioRendererImplTest, OnRenderErrorCausesDecodeError) {
Initialize();
Preroll();
StartRendering();
EXPECT_CALL(*this, OnError(PIPELINE_ERROR_DECODE));
sink_->OnRenderError();
}
// Test for AudioRendererImpl calling Pause()/Play() on the sink when the
// playback rate is set to zero and non-zero.
TEST_F(AudioRendererImplTest, SetPlaybackRate) {
Initialize();
Preroll();
// Rendering hasn't started. Sink should always be paused.
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
renderer_->SetPlaybackRate(0.0f);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Rendering has started with non-zero rate. Rate changes will affect sink
// state.
renderer_->StartRendering();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
renderer_->SetPlaybackRate(0.0f);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Rendering has stopped. Sink should be paused.
renderer_->StopRendering();
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Start rendering with zero playback rate. Sink should be paused until
// non-zero rate is set.
renderer_->SetPlaybackRate(0.0f);
renderer_->StartRendering();
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
}
} // namespace media