#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/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 {
+namespace {
+
+// Since AudioBufferConverter is used due to different input/output sample
+// rates, define some helper types to differentiate between the two.
+struct InputFrames {
+ explicit InputFrames(int value) : value(value) {}
+ int value;
+};
+
+struct OutputFrames {
+ explicit OutputFrames(int value) : value(value) {}
+ int value;
+};
+
+} // namespace
+
// 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;
+// Use a different output sample rate so the AudioBufferConverter is invoked.
+static int kInputSamplesPerSecond = 5000;
+static int kOutputSamplesPerSecond = 10000;
ACTION_P(EnterPendingDecoderInitStateAction, test) {
test->EnterPendingDecoderInitState(arg1);
// 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()) {
+ last_time_update_(kNoTimestamp()),
+ last_max_time_(kNoTimestamp()),
+ ended_(false) {
AudioDecoderConfig audio_config(kCodec,
kSampleFormat,
kChannelLayout,
- kSamplesPerSecond,
+ kInputSamplesPerSecond,
NULL,
0,
false);
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() {
}
MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
- MOCK_METHOD0(OnUnderflow, void());
+ MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
MOCK_METHOD1(OnError, void(PipelineStatus));
void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) {
CHECK(current_time <= max_time);
last_time_update_ = current_time;
+ last_max_time_ = max_time;
}
void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) {
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::OnBufferingStateChange,
+ base::Unretained(this)),
+ base::Bind(&AudioRendererImplTest::OnEnded,
+ base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnError,
base::Unretained(this)));
}
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()));
+ next_timestamp_.reset(new AudioTimestampHelper(kInputSamplesPerSecond));
}
void InitializeWithStatus(PipelineStatus expected) {
EXPECT_TRUE(decode_cb_.is_null());
}
- void InitializeAndStop() {
+ void InitializeAndDestroy() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
- .WillOnce(DoAll(SaveArg<2>(&output_cb_),
- RunCallback<1>(PIPELINE_OK)));
- EXPECT_CALL(*decoder_, Stop());
+ .WillOnce(RunCallback<1>(PIPELINE_OK));
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());
+ // Destroy the |renderer_| before we let the MessageLoop run, this simulates
+ // an interleaving in which we end up destroying the |renderer_| while the
+ // OnDecoderSelected callback is in flight.
+ renderer_.reset();
event.RunAndWaitForStatus(PIPELINE_ERROR_ABORT);
- EXPECT_EQ(renderer_->state_, AudioRendererImpl::kStopped);
}
- void InitializeAndStopDuringDecoderInit() {
+ void InitializeAndDestroyDuringDecoderInit() {
EXPECT_CALL(*decoder_, Initialize(_, _, _))
- .WillOnce(DoAll(SaveArg<2>(&output_cb_),
- EnterPendingDecoderInitStateAction(this)));
- EXPECT_CALL(*decoder_, Stop());
+ .WillOnce(EnterPendingDecoderInitStateAction(this));
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);
-
+ renderer_.reset();
event.RunAndWaitForStatus(PIPELINE_ERROR_ABORT);
- EXPECT_EQ(renderer_->state_, AudioRendererImpl::kStopped);
}
void EnterPendingDecoderInitState(PipelineStatusCB cb) {
init_decoder_cb_ = cb;
}
- void Flush() {
+ void FlushDuringPendingRead() {
+ SCOPED_TRACE("FlushDuringPendingRead()");
WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure());
+ SatisfyPendingRead(InputFrames(256));
flush_event.RunAndWait();
EXPECT_FALSE(IsReadPending());
next_timestamp_->SetBaseTimestamp(timestamp);
// Fill entire buffer to complete prerolling.
- WaitableMessageLoopEvent event;
- renderer_->Preroll(timestamp, event.GetPipelineStatusCB());
+ renderer_->SetMediaTime(timestamp);
+ renderer_->StartPlaying();
WaitForPendingRead();
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DeliverRemainingAudio();
- event.RunAndWaitForStatus(PIPELINE_OK);
}
- void StartRendering() {
- renderer_->StartRendering();
+ void StartTicking() {
+ renderer_->StartTicking();
renderer_->SetPlaybackRate(1.0f);
}
- void StopRendering() {
- renderer_->StopRendering();
- }
-
- void Seek() {
- StopRendering();
- Flush();
- Preroll();
- }
-
- void WaitForEnded() {
- SCOPED_TRACE("WaitForEnded()");
- ended_event_.RunAndWait();
- }
+ void StopTicking() { renderer_->StopTicking(); }
bool IsReadPending() const {
return !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);
+ // Delivers decoded frames to |renderer_|.
+ void SatisfyPendingRead(InputFrames frames) {
+ CHECK_GT(frames.value, 0);
CHECK(!decode_cb_.is_null());
scoped_refptr<AudioBuffer> buffer =
MakeAudioBuffer<float>(kSampleFormat,
kChannelLayout,
kChannelCount,
- kSamplesPerSecond,
- kPlayingAudio,
+ kInputSamplesPerSecond,
+ 1.0f,
0.0f,
- size,
+ frames.value,
next_timestamp_->GetTimestamp());
- next_timestamp_->AddFrames(size);
+ next_timestamp_->AddFrames(frames.value);
DeliverBuffer(AudioDecoder::kOk, buffer);
}
FROM_HERE,
base::Bind(base::ResetAndReturn(&decode_cb_), AudioDecoder::kOk));
- message_loop_.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
}
// Delivers frames until |renderer_|'s internal buffer is full and no longer
// has pending reads.
void DeliverRemainingAudio() {
- SatisfyPendingRead(frames_remaining_in_buffer());
+ while (frames_remaining_in_buffer().value > 0) {
+ SatisfyPendingRead(InputFrames(256));
+ }
}
// 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) {
+ // buffer. Returns true if and only if all of |requested_frames| were able
+ // to be consumed.
+ bool ConsumeBufferedData(OutputFrames requested_frames) {
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() {
+ AudioBus::Create(kChannels, requested_frames.value);
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;
+ EXPECT_TRUE(sink_->Render(bus.get(), 0, &frames_read));
+ return frames_read == requested_frames.value;
}
- int frames_buffered() {
- return renderer_->algorithm_->frames_buffered();
+ OutputFrames frames_buffered() {
+ return OutputFrames(renderer_->algorithm_->frames_buffered());
}
- int buffer_capacity() {
- return renderer_->algorithm_->QueueCapacity();
+ OutputFrames buffer_capacity() {
+ return OutputFrames(renderer_->algorithm_->QueueCapacity());
}
- int frames_remaining_in_buffer() {
+ OutputFrames 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;
+ if (frames_buffered().value > buffer_capacity().value) {
+ return OutputFrames(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;
+ return OutputFrames(buffer_capacity().value - frames_buffered().value);
}
void force_config_change() {
renderer_->OnConfigChange();
}
- int converter_input_frames_left() const {
- return renderer_->buffer_converter_->input_frames_left_for_testing();
+ InputFrames converter_input_frames_left() const {
+ return InputFrames(
+ renderer_->buffer_converter_->input_frames_left_for_testing());
}
bool splicer_has_next_buffer() const {
return last_time_update_;
}
+ base::TimeDelta last_max_time() const { return last_max_time_; }
+
+ bool ended() const { return ended_; }
+
// 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(
if (!reset_cb_.is_null())
base::ResetAndReturn(&reset_cb_).Run();
- message_loop_.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void OnEnded() {
+ CHECK(!ended_);
+ ended_ = true;
}
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_;
+ base::TimeDelta last_max_time_;
+ bool ended_;
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest);
};
Preroll();
}
-TEST_F(AudioRendererImplTest, StartRendering) {
+TEST_F(AudioRendererImplTest, StartTicking) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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();
+ StartTicking();
// Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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));
+ // Forcefully trigger underflow.
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
- // 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|.
+ // Fulfill the read with an end-of-stream buffer. Doing so should change our
+ // buffering state so playback resumes.
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
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));
+ // Consume all remaining data. We shouldn't have signal ended yet.
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(ended());
- // Verify that we can still Flush() before entering the rebuffering state.
- DeliverEndOfStream();
- Flush();
+ // Ended should trigger on next render call.
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(ended());
}
-TEST_F(AudioRendererImplTest, Underflow_EndOfStream) {
+TEST_F(AudioRendererImplTest, Underflow) {
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());
+ StartTicking();
// Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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 the next FillBuffer() call triggers a buffering state change
+ // update.
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
- // Deliver a little bit of data.
- SatisfyPendingRead(kDataSize);
- WaitForPendingRead();
+ // Verify we're still not getting audio data.
+ EXPECT_EQ(0, frames_buffered().value);
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
- // 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);
+ // Deliver enough data to have enough for buffering.
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ DeliverRemainingAudio();
- // 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();
+ // Verify we're getting audio data.
+ EXPECT_TRUE(ConsumeBufferedData(OutputFrames(1)));
}
-TEST_F(AudioRendererImplTest, Underflow_ResumeFromCallback) {
+TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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));
+ OutputFrames initial_capacity = buffer_capacity();
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
- // 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 underflowing.
+ EXPECT_GT(buffer_capacity().value, initial_capacity.value);
- // Deliver data, we should get non-muted audio.
- DeliverRemainingAudio();
- EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
- EXPECT_FALSE(muted);
+ // Verify that the buffer capacity is restored to the |initial_capacity|.
+ FlushDuringPendingRead();
+ EXPECT_EQ(buffer_capacity().value, initial_capacity.value);
}
-TEST_F(AudioRendererImplTest, Underflow_SetPlaybackRate) {
+TEST_F(AudioRendererImplTest, Underflow_Flush) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
- // Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ // Force underflow.
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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));
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
WaitForPendingRead();
+ StopTicking();
- 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());
+ // We shouldn't expect another buffering state change when flushing.
+ FlushDuringPendingRead();
}
TEST_F(AudioRendererImplTest, PendingRead_Flush) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Partially drain internal buffer so we get a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
+ EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
WaitForPendingRead();
- StopRendering();
+ StopTicking();
EXPECT_TRUE(IsReadPending());
- // Start flushing.
- WaitableMessageLoopEvent flush_event;
- renderer_->Flush(flush_event.GetClosure());
-
- SatisfyPendingRead(kDataSize);
-
- flush_event.RunAndWait();
-
- EXPECT_FALSE(IsReadPending());
+ // Flush and expect to be notified that we have nothing.
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ FlushDuringPendingRead();
// Preroll again to a different timestamp and verify it completed normally.
Preroll(1000, PIPELINE_OK);
}
-TEST_F(AudioRendererImplTest, PendingRead_Stop) {
+TEST_F(AudioRendererImplTest, PendingRead_Destroy) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Partially drain internal buffer so we get a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
+ EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
WaitForPendingRead();
- StopRendering();
+ StopTicking();
EXPECT_TRUE(IsReadPending());
- WaitableMessageLoopEvent stop_event;
- renderer_->Stop(stop_event.GetClosure());
- needs_stop_ = false;
-
- SatisfyPendingRead(kDataSize);
-
- stop_event.RunAndWait();
-
- EXPECT_FALSE(IsReadPending());
+ renderer_.reset();
}
-TEST_F(AudioRendererImplTest, PendingFlush_Stop) {
+TEST_F(AudioRendererImplTest, PendingFlush_Destroy) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Partially drain internal buffer so we get a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL));
+ EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
WaitForPendingRead();
- StopRendering();
+ StopTicking();
EXPECT_TRUE(IsReadPending());
WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure());
- SatisfyPendingRead(kDataSize);
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ SatisfyPendingRead(InputFrames(256));
- WaitableMessageLoopEvent event;
- renderer_->Stop(event.GetClosure());
- event.RunAndWait();
- needs_stop_ = false;
+ renderer_.reset();
}
-TEST_F(AudioRendererImplTest, InitializeThenStop) {
- InitializeAndStop();
+TEST_F(AudioRendererImplTest, InitializeThenDestroy) {
+ InitializeAndDestroy();
}
-TEST_F(AudioRendererImplTest, InitializeThenStopDuringDecoderInit) {
- InitializeAndStopDuringDecoderInit();
+TEST_F(AudioRendererImplTest, InitializeThenDestroyDuringDecoderInit) {
+ InitializeAndDestroyDuringDecoderInit();
}
TEST_F(AudioRendererImplTest, ConfigChangeDrainsConverter) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
// Drain internal buffer, we should have a pending read.
- EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
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);
+ SatisfyPendingRead(InputFrames(2053));
EXPECT_FALSE(splicer_has_next_buffer());
- EXPECT_GT(converter_input_frames_left(), 0);
+ EXPECT_GT(converter_input_frames_left().value, 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());
+ EXPECT_EQ(0, converter_input_frames_left().value);
}
TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond);
EXPECT_EQ(kNoTimestamp(), last_time_update());
+ EXPECT_EQ(kNoTimestamp(), last_max_time());
// 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));
+ OutputFrames frames_to_consume(frames_buffered().value / 2);
+ EXPECT_TRUE(ConsumeBufferedData(frames_to_consume));
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());
+ timestamp_helper.AddFrames(frames_to_consume.value);
+ EXPECT_EQ(base::TimeDelta(), last_time_update());
+ EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
// 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());
+ EXPECT_TRUE(ConsumeBufferedData(frames_to_consume));
+ EXPECT_EQ(base::TimeDelta(), last_time_update());
+ EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
+ // Now the times should be updated.
base::RunLoop().RunUntilIdle();
- timestamp_helper.AddFrames(frames_to_consume);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update());
+ timestamp_helper.AddFrames(frames_to_consume.value);
+ EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
}
TEST_F(AudioRendererImplTest, ImmediateEndOfStream) {
Initialize();
{
SCOPED_TRACE("Preroll()");
- WaitableMessageLoopEvent event;
- renderer_->Preroll(base::TimeDelta(), event.GetPipelineStatusCB());
+ renderer_->StartPlaying();
WaitForPendingRead();
+ EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DeliverEndOfStream();
- event.RunAndWaitForStatus(PIPELINE_OK);
}
- StartRendering();
+ StartTicking();
// Read a single frame. We shouldn't be able to satisfy it.
- EXPECT_FALSE(ConsumeBufferedData(1, NULL));
- WaitForEnded();
+ EXPECT_FALSE(ended());
+ EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(ended());
}
TEST_F(AudioRendererImplTest, OnRenderErrorCausesDecodeError) {
Initialize();
Preroll();
- StartRendering();
+ StartTicking();
EXPECT_CALL(*this, OnError(PIPELINE_ERROR_DECODE));
sink_->OnRenderError();
+ base::RunLoop().RunUntilIdle();
}
// Test for AudioRendererImpl calling Pause()/Play() on the sink when the
// Rendering has started with non-zero rate. Rate changes will affect sink
// state.
- renderer_->StartRendering();
+ renderer_->StartTicking();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
renderer_->SetPlaybackRate(0.0f);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Rendering has stopped. Sink should be paused.
- renderer_->StopRendering();
+ renderer_->StopTicking();
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();
+ renderer_->StartTicking();
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
renderer_->SetPlaybackRate(1.0f);
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());