| Index: media/filters/audio_renderer_impl_unittest.cc
|
| diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc
|
| deleted file mode 100644
|
| index 3d8226835d2fb31779fd5612ee36d0f0743268d4..0000000000000000000000000000000000000000
|
| --- a/media/filters/audio_renderer_impl_unittest.cc
|
| +++ /dev/null
|
| @@ -1,747 +0,0 @@
|
| -// 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/format_macros.h"
|
| -#include "base/run_loop.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "media/base/audio_buffer_converter.h"
|
| -#include "media/base/audio_hardware_config.h"
|
| -#include "media/base/audio_splicer.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::TimeDelta;
|
| -using ::testing::_;
|
| -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);
|
| -
|
| -// 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);
|
| -}
|
| -
|
| -class AudioRendererImplTest : public ::testing::Test {
|
| - public:
|
| - // Give the decoder some non-garbage media properties.
|
| - AudioRendererImplTest()
|
| - : hardware_config_(AudioParameters(), AudioParameters()),
|
| - demuxer_stream_(DemuxerStream::AUDIO),
|
| - decoder_(new MockAudioDecoder()),
|
| - ended_(false) {
|
| - AudioDecoderConfig audio_config(kCodec,
|
| - kSampleFormat,
|
| - kChannelLayout,
|
| - kInputSamplesPerSecond,
|
| - 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_.get(),
|
| - decoders.Pass(),
|
| - hardware_config_,
|
| - new MediaLog()));
|
| - }
|
| -
|
| - virtual ~AudioRendererImplTest() {
|
| - SCOPED_TRACE("~AudioRendererImplTest()");
|
| - }
|
| -
|
| - 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_METHOD1(OnBufferingStateChange, void(BufferingState));
|
| - MOCK_METHOD1(OnError, void(PipelineStatus));
|
| -
|
| - void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) {
|
| - renderer_->Initialize(
|
| - &demuxer_stream_, pipeline_status_cb, SetDecryptorReadyCB(),
|
| - base::Bind(&AudioRendererImplTest::OnStatistics,
|
| - base::Unretained(this)),
|
| - base::Bind(&AudioRendererImplTest::OnBufferingStateChange,
|
| - base::Unretained(this)),
|
| - base::Bind(&AudioRendererImplTest::OnEnded, base::Unretained(this)),
|
| - base::Bind(&AudioRendererImplTest::OnError, base::Unretained(this)));
|
| - }
|
| -
|
| - void Initialize() {
|
| - EXPECT_CALL(*decoder_, Initialize(_, _, _))
|
| - .WillOnce(DoAll(SaveArg<2>(&output_cb_),
|
| - RunCallback<1>(PIPELINE_OK)));
|
| - InitializeWithStatus(PIPELINE_OK);
|
| -
|
| - next_timestamp_.reset(new AudioTimestampHelper(kInputSamplesPerSecond));
|
| - }
|
| -
|
| - 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 InitializeAndDestroy() {
|
| - EXPECT_CALL(*decoder_, Initialize(_, _, _))
|
| - .WillOnce(RunCallback<1>(PIPELINE_OK));
|
| -
|
| - WaitableMessageLoopEvent event;
|
| - InitializeRenderer(event.GetPipelineStatusCB());
|
| -
|
| - // 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);
|
| - }
|
| -
|
| - void InitializeAndDestroyDuringDecoderInit() {
|
| - EXPECT_CALL(*decoder_, Initialize(_, _, _))
|
| - .WillOnce(EnterPendingDecoderInitStateAction(this));
|
| -
|
| - WaitableMessageLoopEvent event;
|
| - InitializeRenderer(event.GetPipelineStatusCB());
|
| - base::RunLoop().RunUntilIdle();
|
| - DCHECK(!init_decoder_cb_.is_null());
|
| -
|
| - renderer_.reset();
|
| - event.RunAndWaitForStatus(PIPELINE_ERROR_ABORT);
|
| - }
|
| -
|
| - void EnterPendingDecoderInitState(PipelineStatusCB cb) {
|
| - init_decoder_cb_ = cb;
|
| - }
|
| -
|
| - void FlushDuringPendingRead() {
|
| - SCOPED_TRACE("FlushDuringPendingRead()");
|
| - WaitableMessageLoopEvent flush_event;
|
| - renderer_->Flush(flush_event.GetClosure());
|
| - SatisfyPendingRead(InputFrames(256));
|
| - flush_event.RunAndWait();
|
| -
|
| - EXPECT_FALSE(IsReadPending());
|
| - }
|
| -
|
| - void Preroll() {
|
| - Preroll(base::TimeDelta(), base::TimeDelta(), PIPELINE_OK);
|
| - }
|
| -
|
| - void Preroll(base::TimeDelta start_timestamp,
|
| - base::TimeDelta first_timestamp,
|
| - PipelineStatus expected) {
|
| - SCOPED_TRACE(base::StringPrintf("Preroll(%" PRId64 ", %d)",
|
| - first_timestamp.InMilliseconds(),
|
| - expected));
|
| - next_timestamp_->SetBaseTimestamp(first_timestamp);
|
| -
|
| - // Fill entire buffer to complete prerolling.
|
| - renderer_->SetMediaTime(start_timestamp);
|
| - renderer_->StartPlaying();
|
| - WaitForPendingRead();
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
|
| - DeliverRemainingAudio();
|
| - }
|
| -
|
| - void StartTicking() {
|
| - renderer_->StartTicking();
|
| - renderer_->SetPlaybackRate(1.0f);
|
| - }
|
| -
|
| - void StopTicking() { renderer_->StopTicking(); }
|
| -
|
| - 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 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,
|
| - kInputSamplesPerSecond,
|
| - 1.0f,
|
| - 0.0f,
|
| - frames.value,
|
| - next_timestamp_->GetTimestamp());
|
| - next_timestamp_->AddFrames(frames.value);
|
| -
|
| - 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));
|
| -
|
| - base::RunLoop().RunUntilIdle();
|
| - }
|
| -
|
| - // Delivers frames until |renderer_|'s internal buffer is full and no longer
|
| - // has pending reads.
|
| - void DeliverRemainingAudio() {
|
| - while (frames_remaining_in_buffer().value > 0) {
|
| - SatisfyPendingRead(InputFrames(256));
|
| - }
|
| - }
|
| -
|
| - // Attempts to consume |requested_frames| frames from |renderer_|'s internal
|
| - // 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, requested_frames.value);
|
| - int frames_read = 0;
|
| - EXPECT_TRUE(sink_->Render(bus.get(), 0, &frames_read));
|
| - return frames_read == requested_frames.value;
|
| - }
|
| -
|
| - OutputFrames frames_buffered() {
|
| - return OutputFrames(renderer_->algorithm_->frames_buffered());
|
| - }
|
| -
|
| - OutputFrames buffer_capacity() {
|
| - return OutputFrames(renderer_->algorithm_->QueueCapacity());
|
| - }
|
| -
|
| - 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().value > buffer_capacity().value) {
|
| - return OutputFrames(0);
|
| - }
|
| - return OutputFrames(buffer_capacity().value - frames_buffered().value);
|
| - }
|
| -
|
| - void force_config_change() {
|
| - renderer_->OnConfigChange();
|
| - }
|
| -
|
| - InputFrames converter_input_frames_left() const {
|
| - return InputFrames(
|
| - renderer_->buffer_converter_->input_frames_left_for_testing());
|
| - }
|
| -
|
| - bool splicer_has_next_buffer() const {
|
| - return renderer_->splicer_->HasNextBuffer();
|
| - }
|
| -
|
| - base::TimeDelta CurrentMediaTime() {
|
| - return renderer_->CurrentMediaTime();
|
| - }
|
| -
|
| - bool ended() const { return ended_; }
|
| -
|
| - // Fixture members.
|
| - base::MessageLoop message_loop_;
|
| - scoped_ptr<AudioRendererImpl> renderer_;
|
| - scoped_refptr<FakeAudioRendererSink> sink_;
|
| - AudioHardwareConfig hardware_config_;
|
| -
|
| - private:
|
| - void DecodeDecoder(const scoped_refptr<DecoderBuffer>& buffer,
|
| - const AudioDecoder::DecodeCB& decode_cb) {
|
| - // 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.get() && !buffer->end_of_stream())
|
| - output_cb_.Run(buffer);
|
| - base::ResetAndReturn(&decode_cb_).Run(status);
|
| -
|
| - if (!reset_cb_.is_null())
|
| - base::ResetAndReturn(&reset_cb_).Run();
|
| -
|
| - base::RunLoop().RunUntilIdle();
|
| - }
|
| -
|
| - void OnEnded() {
|
| - CHECK(!ended_);
|
| - ended_ = true;
|
| - }
|
| -
|
| - MockDemuxerStream demuxer_stream_;
|
| - MockAudioDecoder* decoder_;
|
| -
|
| - // Used for satisfying reads.
|
| - AudioDecoder::OutputCB output_cb_;
|
| - AudioDecoder::DecodeCB decode_cb_;
|
| - base::Closure reset_cb_;
|
| - scoped_ptr<AudioTimestampHelper> next_timestamp_;
|
| -
|
| - // Run during DecodeDecoder() to unblock WaitForPendingRead().
|
| - base::Closure wait_for_pending_decode_cb_;
|
| -
|
| - PipelineStatusCB init_decoder_cb_;
|
| - bool ended_;
|
| -
|
| - 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, StartTicking) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Drain internal buffer, we should have a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
|
| - WaitForPendingRead();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, EndOfStream) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Drain internal buffer, we should have a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
|
| - WaitForPendingRead();
|
| -
|
| - // Forcefully trigger underflow.
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| -
|
| - // 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();
|
| -
|
| - // Consume all remaining data. We shouldn't have signal ended yet.
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(ended());
|
| -
|
| - // Ended should trigger on next render call.
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_TRUE(ended());
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, Underflow) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Drain internal buffer, we should have a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
|
| - WaitForPendingRead();
|
| -
|
| - // Verify the next FillBuffer() call triggers a buffering state change
|
| - // update.
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| -
|
| - // Verify we're still not getting audio data.
|
| - EXPECT_EQ(0, frames_buffered().value);
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| -
|
| - // Deliver enough data to have enough for buffering.
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
|
| - DeliverRemainingAudio();
|
| -
|
| - // Verify we're getting audio data.
|
| - EXPECT_TRUE(ConsumeBufferedData(OutputFrames(1)));
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Drain internal buffer, we should have a pending read.
|
| - 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.
|
| - OutputFrames initial_capacity = buffer_capacity();
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| -
|
| - // Verify that the buffer capacity increased as a result of underflowing.
|
| - EXPECT_GT(buffer_capacity().value, initial_capacity.value);
|
| -
|
| - // Verify that the buffer capacity is restored to the |initial_capacity|.
|
| - FlushDuringPendingRead();
|
| - EXPECT_EQ(buffer_capacity().value, initial_capacity.value);
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, Underflow_Flush) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Force underflow.
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_buffered()));
|
| - WaitForPendingRead();
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| - WaitForPendingRead();
|
| - StopTicking();
|
| -
|
| - // We shouldn't expect another buffering state change when flushing.
|
| - FlushDuringPendingRead();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, PendingRead_Flush) {
|
| - Initialize();
|
| -
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Partially drain internal buffer so we get a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
|
| - WaitForPendingRead();
|
| -
|
| - StopTicking();
|
| -
|
| - EXPECT_TRUE(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.
|
| - const base::TimeDelta seek_timestamp =
|
| - base::TimeDelta::FromMilliseconds(1000);
|
| - Preroll(seek_timestamp, seek_timestamp, PIPELINE_OK);
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, PendingRead_Destroy) {
|
| - Initialize();
|
| -
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Partially drain internal buffer so we get a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
|
| - WaitForPendingRead();
|
| -
|
| - StopTicking();
|
| -
|
| - EXPECT_TRUE(IsReadPending());
|
| -
|
| - renderer_.reset();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, PendingFlush_Destroy) {
|
| - Initialize();
|
| -
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Partially drain internal buffer so we get a pending read.
|
| - EXPECT_TRUE(ConsumeBufferedData(OutputFrames(256)));
|
| - WaitForPendingRead();
|
| -
|
| - StopTicking();
|
| -
|
| - EXPECT_TRUE(IsReadPending());
|
| -
|
| - // Start flushing.
|
| - WaitableMessageLoopEvent flush_event;
|
| - renderer_->Flush(flush_event.GetClosure());
|
| -
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - SatisfyPendingRead(InputFrames(256));
|
| -
|
| - renderer_.reset();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, InitializeThenDestroy) {
|
| - InitializeAndDestroy();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, InitializeThenDestroyDuringDecoderInit) {
|
| - InitializeAndDestroyDuringDecoderInit();
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, ConfigChangeDrainsConverter) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - // Drain internal buffer, we should have a pending read.
|
| - 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(InputFrames(2053));
|
| - EXPECT_FALSE(splicer_has_next_buffer());
|
| - 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().value);
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond);
|
| - timestamp_helper.SetBaseTimestamp(base::TimeDelta());
|
| -
|
| - // Time should be the starting timestamp as nothing's been consumed yet.
|
| - EXPECT_EQ(timestamp_helper.GetTimestamp(), CurrentMediaTime());
|
| -
|
| - // Consume some audio data.
|
| - OutputFrames frames_to_consume(frames_buffered().value / 2);
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_to_consume));
|
| - WaitForPendingRead();
|
| -
|
| - // Time shouldn't change just yet because we've only sent the initial audio
|
| - // data to the hardware.
|
| - EXPECT_EQ(timestamp_helper.GetTimestamp(), CurrentMediaTime());
|
| -
|
| - // Consume some more audio data.
|
| - frames_to_consume = frames_buffered();
|
| - EXPECT_TRUE(ConsumeBufferedData(frames_to_consume));
|
| -
|
| - // Now time should change now that the audio hardware has called back.
|
| - timestamp_helper.AddFrames(frames_to_consume.value);
|
| - EXPECT_EQ(timestamp_helper.GetTimestamp(), CurrentMediaTime());
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, RenderingDelayedForEarlyStartTime) {
|
| - Initialize();
|
| -
|
| - // Choose a first timestamp a few buffers into the future, which ends halfway
|
| - // through the desired output buffer; this allows for maximum test coverage.
|
| - const double kBuffers = 4.5;
|
| - const base::TimeDelta first_timestamp = base::TimeDelta::FromSecondsD(
|
| - hardware_config_.GetOutputBufferSize() * kBuffers /
|
| - hardware_config_.GetOutputSampleRate());
|
| -
|
| - Preroll(base::TimeDelta(), first_timestamp, PIPELINE_OK);
|
| - StartTicking();
|
| -
|
| - // Verify the first few buffers are silent.
|
| - scoped_ptr<AudioBus> bus =
|
| - AudioBus::Create(hardware_config_.GetOutputConfig());
|
| - int frames_read = 0;
|
| - for (int i = 0; i < std::floor(kBuffers); ++i) {
|
| - EXPECT_TRUE(sink_->Render(bus.get(), 0, &frames_read));
|
| - EXPECT_EQ(frames_read, bus->frames());
|
| - for (int j = 0; j < bus->frames(); ++j)
|
| - ASSERT_FLOAT_EQ(0.0f, bus->channel(0)[j]);
|
| - WaitForPendingRead();
|
| - DeliverRemainingAudio();
|
| - }
|
| -
|
| - // Verify the last buffer is half silence and half real data.
|
| - EXPECT_TRUE(sink_->Render(bus.get(), 0, &frames_read));
|
| - EXPECT_EQ(frames_read, bus->frames());
|
| - const int zero_frames =
|
| - bus->frames() * (kBuffers - static_cast<int>(kBuffers));
|
| -
|
| - for (int i = 0; i < zero_frames; ++i)
|
| - ASSERT_FLOAT_EQ(0.0f, bus->channel(0)[i]);
|
| - for (int i = zero_frames; i < bus->frames(); ++i)
|
| - ASSERT_NE(0.0f, bus->channel(0)[i]);
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, ImmediateEndOfStream) {
|
| - Initialize();
|
| - {
|
| - SCOPED_TRACE("Preroll()");
|
| - renderer_->StartPlaying();
|
| - WaitForPendingRead();
|
| - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
|
| - DeliverEndOfStream();
|
| - }
|
| - StartTicking();
|
| -
|
| - // Read a single frame. We shouldn't be able to satisfy it.
|
| - EXPECT_FALSE(ended());
|
| - EXPECT_FALSE(ConsumeBufferedData(OutputFrames(1)));
|
| - base::RunLoop().RunUntilIdle();
|
| - EXPECT_TRUE(ended());
|
| -}
|
| -
|
| -TEST_F(AudioRendererImplTest, OnRenderErrorCausesDecodeError) {
|
| - Initialize();
|
| - Preroll();
|
| - StartTicking();
|
| -
|
| - EXPECT_CALL(*this, OnError(PIPELINE_ERROR_DECODE));
|
| - sink_->OnRenderError();
|
| - base::RunLoop().RunUntilIdle();
|
| -}
|
| -
|
| -// 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_->StartTicking();
|
| - 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_->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_->StartTicking();
|
| - EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
|
| - renderer_->SetPlaybackRate(1.0f);
|
| - EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
|
| -}
|
| -
|
| -} // namespace media
|
|
|