Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / media / filters / audio_renderer_impl_unittest.cc
index ef00769..98d5e7f 100644 (file)
@@ -4,17 +4,11 @@
 
 #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);
@@ -59,14 +59,15 @@ class AudioRendererImplTest : public ::testing::Test {
   // 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);
@@ -98,19 +99,10 @@ class AudioRendererImplTest : public ::testing::Test {
                                           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() {
@@ -120,12 +112,13 @@ class AudioRendererImplTest : public ::testing::Test {
   }
 
   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) {
@@ -134,11 +127,12 @@ class AudioRendererImplTest : public ::testing::Test {
         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)));
   }
@@ -147,11 +141,9 @@ class AudioRendererImplTest : public ::testing::Test {
     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) {
@@ -165,49 +157,42 @@ class AudioRendererImplTest : public ::testing::Test {
     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());
@@ -224,32 +209,19 @@ class AudioRendererImplTest : public ::testing::Test {
     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();
@@ -270,21 +242,21 @@ class AudioRendererImplTest : public ::testing::Test {
     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);
   }
@@ -308,132 +280,52 @@ class AudioRendererImplTest : public ::testing::Test {
         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 {
@@ -444,27 +336,19 @@ class AudioRendererImplTest : public ::testing::Test {
     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(
@@ -502,30 +386,30 @@ class AudioRendererImplTest : public ::testing::Test {
     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);
 };
@@ -544,314 +428,161 @@ TEST_F(AudioRendererImplTest, Preroll) {
   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());
 
@@ -859,99 +590,104 @@ TEST_F(AudioRendererImplTest, PendingFlush_Stop) {
   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
@@ -969,7 +705,7 @@ TEST_F(AudioRendererImplTest, SetPlaybackRate) {
 
   // 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());
@@ -977,13 +713,13 @@ TEST_F(AudioRendererImplTest, SetPlaybackRate) {
   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());