summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-01 19:39:17 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-01 19:39:17 +0000
commit28bc175c395aafc54d5d37e5b1b876ccc2c5c94d (patch)
tree0ae0709d349ff7e5cda84dd486df197cea22c646
parent9242805d40f7da2b289ded36cf36fec28bca952b (diff)
downloadchromium_src-28bc175c395aafc54d5d37e5b1b876ccc2c5c94d.zip
chromium_src-28bc175c395aafc54d5d37e5b1b876ccc2c5c94d.tar.gz
chromium_src-28bc175c395aafc54d5d37e5b1b876ccc2c5c94d.tar.bz2
Update VideoRenderer API to fire changes in BufferingState.
This is the video equivalent to r280140. Pipeline is now completely in charge of prerolling and underflow/rebuffering. BUG=144683 Review URL: https://codereview.chromium.org/288373002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280867 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/audio_renderer.h3
-rw-r--r--media/base/mock_filters.h24
-rw-r--r--media/base/pipeline.cc48
-rw-r--r--media/base/pipeline_unittest.cc83
-rw-r--r--media/base/video_renderer.h24
-rw-r--r--media/filters/video_renderer_impl.cc156
-rw-r--r--media/filters/video_renderer_impl.h62
-rw-r--r--media/filters/video_renderer_impl_unittest.cc193
8 files changed, 230 insertions, 363 deletions
diff --git a/media/base/audio_renderer.h b/media/base/audio_renderer.h
index fd98664..48ab684 100644
--- a/media/base/audio_renderer.h
+++ b/media/base/audio_renderer.h
@@ -22,9 +22,6 @@ class MEDIA_EXPORT AudioRenderer {
// Second parameter is the maximum time value that the clock cannot exceed.
typedef base::Callback<void(base::TimeDelta, base::TimeDelta)> TimeCB;
- // Used to indicate changes in the buffering state of this audio renderer.
- typedef base::Callback<void(BufferingState)> BufferingStateCB;
-
AudioRenderer();
virtual ~AudioRenderer();
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 76c56ca..be5062b 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -115,18 +115,18 @@ class MockVideoRenderer : public VideoRenderer {
virtual ~MockVideoRenderer();
// VideoRenderer implementation.
- MOCK_METHOD9(Initialize, void(DemuxerStream* stream,
- bool low_delay,
- const PipelineStatusCB& init_cb,
- const StatisticsCB& statistics_cb,
- const TimeCB& time_cb,
- const base::Closure& ended_cb,
- const PipelineStatusCB& error_cb,
- const TimeDeltaCB& get_time_cb,
- const TimeDeltaCB& get_duration_cb));
- MOCK_METHOD1(Play, void(const base::Closure& callback));
+ MOCK_METHOD10(Initialize, void(DemuxerStream* stream,
+ bool low_delay,
+ const PipelineStatusCB& init_cb,
+ const StatisticsCB& statistics_cb,
+ const TimeCB& time_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const TimeDeltaCB& get_time_cb,
+ const TimeDeltaCB& get_duration_cb));
MOCK_METHOD1(Flush, void(const base::Closure& callback));
- MOCK_METHOD2(Preroll, void(base::TimeDelta time, const PipelineStatusCB& cb));
+ MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta timestamp));
MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
@@ -152,7 +152,7 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD1(Flush, void(const base::Closure& callback));
MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta time));
+ MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta timestamp));
MOCK_METHOD1(SetVolume, void(float volume));
MOCK_METHOD0(ResumeAfterUnderflow, void());
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index b8b18db..a81e758 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -404,22 +404,11 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
if (audio_renderer_)
audio_renderer_->StartPlayingFrom(start_timestamp_);
+ if (video_renderer_)
+ video_renderer_->StartPlayingFrom(start_timestamp_);
PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume());
-
- // We enter this state from either kInitPrerolling or kSeeking. As of now
- // both those states call Preroll(), which means by time we enter this
- // state we've already buffered enough data. Forcefully update the
- // buffering state, which start the clock and renderers and transition
- // into kPlaying state.
- //
- // TODO(scherkus): Remove after renderers are taught to fire buffering
- // state callbacks http://crbug.com/144683
- if (video_renderer_) {
- DCHECK(WaitingForEnoughData());
- BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
- }
return;
case kStopping:
@@ -442,20 +431,7 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
DCHECK(!pending_callbacks_.get());
SerialRunner::Queue bound_fns;
- const base::TimeDelta seek_timestamp = base::TimeDelta();
-
// Preroll renderers.
- if (video_renderer_) {
- bound_fns.Push(base::Bind(
- &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
- seek_timestamp));
-
- // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
- // state callbacks http://crbug.com/144683
- bound_fns.Push(base::Bind(&VideoRenderer::Play,
- base::Unretained(video_renderer_.get())));
- }
-
if (text_renderer_) {
bound_fns.Push(base::Bind(
&TextRenderer::Play, base::Unretained(text_renderer_.get())));
@@ -494,13 +470,6 @@ void Pipeline::DoSeek(
if (video_renderer_) {
bound_fns.Push(base::Bind(
&VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
-
- // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
- // state callbacks http://crbug.com/144683
- bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
- base::Unretained(this),
- &video_buffering_state_,
- BUFFERING_HAVE_NOTHING));
}
#if DCHECK_IS_ON
@@ -520,17 +489,6 @@ void Pipeline::DoSeek(
&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
// Preroll renderers.
- if (video_renderer_) {
- bound_fns.Push(base::Bind(
- &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
- seek_timestamp));
-
- // TODO(scherkus): Remove after renderers are taught to fire buffering
- // state callbacks http://crbug.com/144683
- bound_fns.Push(base::Bind(&VideoRenderer::Play,
- base::Unretained(video_renderer_.get())));
- }
-
if (text_renderer_) {
bound_fns.Push(base::Bind(
&TextRenderer::Play, base::Unretained(text_renderer_.get())));
@@ -864,6 +822,8 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
done_cb,
base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)),
+ base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this),
+ &video_buffering_state_),
base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)),
base::Bind(&Pipeline::SetError, base::Unretained(this)),
base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)),
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index d9f2f1f..0c0e474 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -170,15 +170,9 @@ class PipelineTest : public ::testing::Test {
// Sets up expectations to allow the video renderer to initialize.
void InitializeVideoRenderer(DemuxerStream* stream) {
- EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _))
- .WillOnce(RunCallback<2>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
-
- // Startup sequence.
- EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
+ EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_),
+ RunCallback<2>(PIPELINE_OK)));
}
// Sets up expectations to allow the audio renderer to initialize.
@@ -212,6 +206,14 @@ class PipelineTest : public ::testing::Test {
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*audio_renderer_, StartRendering());
}
+
+ if (video_stream_) {
+ EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
+ EXPECT_CALL(*video_renderer_, StartPlayingFrom(base::TimeDelta()))
+ .WillOnce(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_ENOUGH));
+ }
+
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
}
@@ -277,12 +279,13 @@ class PipelineTest : public ::testing::Test {
if (video_stream_) {
EXPECT_CALL(*video_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Preroll(seek_time, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
+ .WillOnce(DoAll(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
+ EXPECT_CALL(*video_renderer_, StartPlayingFrom(seek_time))
+ .WillOnce(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*video_renderer_, SetPlaybackRate(_));
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
}
// We expect a successful seek callback followed by a buffering update.
@@ -337,7 +340,8 @@ class PipelineTest : public ::testing::Test {
scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_;
scoped_ptr<FakeTextTrackStream> text_stream_;
AudioRenderer::TimeCB audio_time_cb_;
- AudioRenderer::BufferingStateCB audio_buffering_state_cb_;
+ BufferingStateCB audio_buffering_state_cb_;
+ BufferingStateCB video_buffering_state_cb_;
VideoDecoderConfig video_decoder_config_;
PipelineMetadata metadata_;
@@ -859,7 +863,6 @@ class PipelineTeardownTest : public PipelineTest {
kInitVideoRenderer,
kFlushing,
kSeeking,
- kPrerolling,
kPlaying,
};
@@ -882,7 +885,6 @@ class PipelineTeardownTest : public PipelineTest {
case kFlushing:
case kSeeking:
- case kPrerolling:
DoInitialize(state, stop_or_error);
DoSeek(state, stop_or_error);
break;
@@ -968,13 +970,13 @@ class PipelineTeardownTest : public PipelineTest {
if (state == kInitVideoRenderer) {
if (stop_or_error == kStop) {
- EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _))
+ EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<2>(PIPELINE_OK)));
EXPECT_CALL(callbacks_, OnStop());
} else {
status = PIPELINE_ERROR_INITIALIZATION_FAILED;
- EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _))
+ EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
.WillOnce(RunCallback<2>(status));
}
@@ -984,8 +986,9 @@ class PipelineTeardownTest : public PipelineTest {
return status;
}
- EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _))
- .WillOnce(RunCallback<2>(PIPELINE_OK));
+ EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_),
+ RunCallback<2>(PIPELINE_OK)));
EXPECT_CALL(callbacks_, OnMetadata(_));
@@ -993,16 +996,14 @@ class PipelineTeardownTest : public PipelineTest {
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta()))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_ENOUGH));
- EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
+ EXPECT_CALL(*video_renderer_, StartPlayingFrom(base::TimeDelta()))
+ .WillOnce(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
-
EXPECT_CALL(*audio_renderer_, StartRendering());
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
if (status == PIPELINE_OK)
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
@@ -1059,7 +1060,10 @@ class PipelineTeardownTest : public PipelineTest {
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
- EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>());
+ EXPECT_CALL(*video_renderer_, Flush(_))
+ .WillOnce(DoAll(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
if (state == kSeeking) {
if (stop_or_error == kStop) {
@@ -1075,29 +1079,6 @@ class PipelineTeardownTest : public PipelineTest {
return status;
}
- EXPECT_CALL(*demuxer_, Seek(_, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
-
- if (state == kPrerolling) {
- if (stop_or_error == kStop) {
- EXPECT_CALL(*video_renderer_, Preroll(_, _))
- .WillOnce(Stop(pipeline_.get(), stop_cb));
- } else {
- status = PIPELINE_ERROR_READ;
- EXPECT_CALL(*video_renderer_, Preroll(_, _))
- .WillOnce(RunCallback<1>(status));
- }
-
- return status;
- }
-
- EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_));
-
- // Playback rate and volume are updated prior to starting.
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
- EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
-
NOTREACHED() << "State not supported: " << state;
return status;
}
@@ -1145,7 +1126,6 @@ INSTANTIATE_TEARDOWN_TEST(Stop, InitAudioRenderer);
INSTANTIATE_TEARDOWN_TEST(Stop, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Stop, Flushing);
INSTANTIATE_TEARDOWN_TEST(Stop, Seeking);
-INSTANTIATE_TEARDOWN_TEST(Stop, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Stop, Playing);
INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer);
@@ -1153,7 +1133,6 @@ INSTANTIATE_TEARDOWN_TEST(Error, InitAudioRenderer);
INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Error, Flushing);
INSTANTIATE_TEARDOWN_TEST(Error, Seeking);
-INSTANTIATE_TEARDOWN_TEST(Error, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Error, Playing);
INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing);
diff --git a/media/base/video_renderer.h b/media/base/video_renderer.h
index b4154a0..e92eca8 100644
--- a/media/base/video_renderer.h
+++ b/media/base/video_renderer.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
+#include "media/base/buffering_state.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
@@ -36,6 +37,9 @@ class MEDIA_EXPORT VideoRenderer {
//
// |time_cb| is executed whenever time has advanced by way of video rendering.
//
+ // |buffering_state_cb| is executed when video rendering has either run out of
+ // data or has enough data to continue playback.
+ //
// |ended_cb| is executed when video rendering has reached the end of stream.
//
// |error_cb| is executed if an error was encountered.
@@ -48,28 +52,24 @@ class MEDIA_EXPORT VideoRenderer {
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const TimeCB& time_cb,
+ const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb,
const TimeDeltaCB& get_time_cb,
const TimeDeltaCB& get_duration_cb) = 0;
- // Start audio decoding and rendering at the current playback rate, executing
- // |callback| when playback is underway.
- virtual void Play(const base::Closure& callback) = 0;
-
// Discard any video data and stop reading from |stream|, executing |callback|
// when completed.
+ //
+ // Clients should expect |buffering_state_cb| to be called with
+ // BUFFERING_HAVE_NOTHING while flushing is in progress.
virtual void Flush(const base::Closure& callback) = 0;
- // Start prerolling video data. If |time| equals kNoTimestamp() then all
- // samples delivered to the renderer are used to complete preroll. If |time|
- // does not equal kNoTimestamp(), then any samples delivered to the renderer
- // with timestamps less than |time| are silently dropped and not used to
- // satisfy preroll. |callback| is executed when preroll has completed.
+ // Starts playback by reading from |stream| and decoding and rendering video.
+ // |timestamp| is the media timestamp playback should start rendering from.
//
- // Only valid to call after a successful Initialize(), Pause(), or Flush().
- virtual void Preroll(base::TimeDelta time,
- const PipelineStatusCB& callback) = 0;
+ // Only valid to call after a successful Initialize() or Flush().
+ virtual void StartPlayingFrom(base::TimeDelta timestamp) = 0;
// Stop all operations in preparation for being deleted, executing |callback|
// when complete.
diff --git a/media/filters/video_renderer_impl.cc b/media/filters/video_renderer_impl.cc
index 736a91f..8735a0a 100644
--- a/media/filters/video_renderer_impl.cc
+++ b/media/filters/video_renderer_impl.cc
@@ -35,6 +35,7 @@ VideoRendererImpl::VideoRendererImpl(
pending_read_(false),
drop_frames_(drop_frames),
playback_rate_(0),
+ buffering_state_(BUFFERING_HAVE_NOTHING),
paint_cb_(paint_cb),
last_timestamp_(kNoTimestamp()),
frames_decoded_(0),
@@ -49,26 +50,23 @@ VideoRendererImpl::~VideoRendererImpl() {
CHECK(thread_.is_null());
}
-void VideoRendererImpl::Play(const base::Closure& callback) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- base::AutoLock auto_lock(lock_);
- DCHECK_EQ(kPrerolled, state_);
- state_ = kPlaying;
- callback.Run();
-}
-
void VideoRendererImpl::Flush(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
- DCHECK_NE(state_, kUninitialized);
+ DCHECK_EQ(state_, kPlaying);
flush_cb_ = callback;
state_ = kFlushing;
// This is necessary if the |video_frame_stream_| has already seen an end of
// stream and needs to drain it before flushing it.
ready_frames_.clear();
+ if (buffering_state_ != BUFFERING_HAVE_NOTHING) {
+ buffering_state_ = BUFFERING_HAVE_NOTHING;
+ buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
+ }
received_end_of_stream_ = false;
rendered_end_of_stream_ = false;
+
video_frame_stream_.Reset(
base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
weak_factory_.GetWeakPtr()));
@@ -114,31 +112,16 @@ void VideoRendererImpl::SetPlaybackRate(float playback_rate) {
playback_rate_ = playback_rate;
}
-void VideoRendererImpl::Preroll(base::TimeDelta time,
- const PipelineStatusCB& cb) {
+void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
- DCHECK(!cb.is_null());
- DCHECK(preroll_cb_.is_null());
- DCHECK(state_ == kFlushed || state_ == kPlaying) << "state_ " << state_;
-
- if (state_ == kFlushed) {
- DCHECK(time != kNoTimestamp());
- DCHECK(!pending_read_);
- DCHECK(ready_frames_.empty());
- } else {
- DCHECK(time == kNoTimestamp());
- }
-
- state_ = kPrerolling;
- preroll_cb_ = cb;
- preroll_timestamp_ = time;
-
- if (ShouldTransitionToPrerolled_Locked()) {
- TransitionToPrerolled_Locked();
- return;
- }
+ DCHECK_EQ(state_, kFlushed);
+ DCHECK(!pending_read_);
+ DCHECK(ready_frames_.empty());
+ DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
+ state_ = kPlaying;
+ start_timestamp_ = timestamp;
AttemptRead_Locked();
}
@@ -147,6 +130,7 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const TimeCB& max_time_cb,
+ const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb,
const TimeDeltaCB& get_time_cb,
@@ -158,6 +142,7 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream,
DCHECK(!init_cb.is_null());
DCHECK(!statistics_cb.is_null());
DCHECK(!max_time_cb.is_null());
+ DCHECK(!buffering_state_cb.is_null());
DCHECK(!ended_cb.is_null());
DCHECK(!get_time_cb.is_null());
DCHECK(!get_duration_cb.is_null());
@@ -168,6 +153,7 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream,
init_cb_ = init_cb;
statistics_cb_ = statistics_cb;
max_time_cb_ = max_time_cb;
+ buffering_state_cb_ = buffering_state_cb;
ended_cb_ = ended_cb;
error_cb_ = error_cb;
get_time_cb_ = get_time_cb;
@@ -235,7 +221,8 @@ void VideoRendererImpl::ThreadMain() {
return;
// Remain idle as long as we're not playing.
- if (state_ != kPlaying || playback_rate_ == 0) {
+ if (state_ != kPlaying || playback_rate_ == 0 ||
+ buffering_state_ != BUFFERING_HAVE_ENOUGH) {
UpdateStatsAndWait_Locked(kIdleTimeDelta);
continue;
}
@@ -338,12 +325,6 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
PipelineStatus error = PIPELINE_ERROR_DECODE;
if (status == VideoFrameStream::DECRYPT_ERROR)
error = PIPELINE_ERROR_DECRYPT;
-
- if (!preroll_cb_.is_null()) {
- base::ResetAndReturn(&preroll_cb_).Run(error);
- return;
- }
-
error_cb_.Run(error);
return;
}
@@ -353,38 +334,28 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
if (state_ == kStopped || state_ == kFlushing)
return;
- if (!frame.get()) {
- // Abort preroll early for a NULL frame because we won't get more frames.
- // A new preroll will be requested after this one completes so there is no
- // point trying to collect more frames.
- if (state_ == kPrerolling)
- TransitionToPrerolled_Locked();
+ DCHECK_EQ(state_, kPlaying);
+ // Can happen when demuxers are preparing for a new Seek().
+ if (!frame) {
+ DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
return;
}
if (frame->end_of_stream()) {
DCHECK(!received_end_of_stream_);
received_end_of_stream_ = true;
- max_time_cb_.Run(get_duration_cb_.Run());
-
- if (state_ == kPrerolling)
- TransitionToPrerolled_Locked();
-
- return;
- }
-
- // Maintain the latest frame decoded so the correct frame is displayed after
- // prerolling has completed.
- if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() &&
- frame->timestamp() <= preroll_timestamp_) {
- ready_frames_.clear();
+ } else {
+ // Maintain the latest frame decoded so the correct frame is displayed after
+ // prerolling has completed.
+ if (frame->timestamp() <= start_timestamp_)
+ ready_frames_.clear();
+ AddReadyFrame_Locked(frame);
}
- AddReadyFrame_Locked(frame);
-
- if (ShouldTransitionToPrerolled_Locked())
- TransitionToPrerolled_Locked();
+ // Signal buffering state if we've met our conditions for having enough data.
+ if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked())
+ TransitionToHaveEnough_Locked();
// Always request more decoded video if we have capacity. This serves two
// purposes:
@@ -393,11 +364,39 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
AttemptRead_Locked();
}
-bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
- return state_ == kPrerolling &&
- (!video_frame_stream_.CanReadWithoutStalling() ||
- ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) ||
- (low_delay_ && ready_frames_.size() > 0));
+bool VideoRendererImpl::HaveEnoughData_Locked() {
+ DCHECK_EQ(state_, kPlaying);
+ return received_end_of_stream_ ||
+ !video_frame_stream_.CanReadWithoutStalling() ||
+ ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) ||
+ (low_delay_ && ready_frames_.size() > 0);
+}
+
+void VideoRendererImpl::TransitionToHaveEnough_Locked() {
+ DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
+
+ if (received_end_of_stream_)
+ max_time_cb_.Run(get_duration_cb_.Run());
+
+ if (!ready_frames_.empty()) {
+ // Max time isn't reported while we're in a have nothing state as we could
+ // be discarding frames to find |start_timestamp_|.
+ if (!received_end_of_stream_) {
+ base::TimeDelta max_timestamp = ready_frames_[0]->timestamp();
+ for (size_t i = 1; i < ready_frames_.size(); ++i) {
+ if (ready_frames_[i]->timestamp() > max_timestamp)
+ max_timestamp = ready_frames_[i]->timestamp();
+ }
+ max_time_cb_.Run(max_timestamp);
+ }
+
+ // Because the clock might remain paused in for an undetermined amount
+ // of time (e.g., seeking while paused), paint the first frame.
+ PaintNextReadyFrame_Locked();
+ }
+
+ buffering_state_ = BUFFERING_HAVE_ENOUGH;
+ buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
}
void VideoRendererImpl::AddReadyFrame_Locked(
@@ -419,7 +418,12 @@ void VideoRendererImpl::AddReadyFrame_Locked(
DCHECK_LE(ready_frames_.size(),
static_cast<size_t>(limits::kMaxVideoFrames));
- max_time_cb_.Run(frame->timestamp());
+ // FrameReady() may add frames but discard them when we're decoding frames to
+ // reach |start_timestamp_|. In this case we'll only want to update the max
+ // time when we know we've reached |start_timestamp_| and have buffered enough
+ // frames to being playback.
+ if (buffering_state_ == BUFFERING_HAVE_ENOUGH)
+ max_time_cb_.Run(frame->timestamp());
// Avoid needlessly waking up |thread_| unless playing.
if (state_ == kPlaying)
@@ -441,8 +445,6 @@ void VideoRendererImpl::AttemptRead_Locked() {
}
switch (state_) {
- case kPrerolling:
- case kPrerolled:
case kPlaying:
pending_read_ = true;
video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady,
@@ -468,6 +470,7 @@ void VideoRendererImpl::OnVideoFrameStreamResetDone() {
DCHECK(ready_frames_.empty());
DCHECK(!received_end_of_stream_);
DCHECK(!rendered_end_of_stream_);
+ DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
state_ = kFlushed;
last_timestamp_ = kNoTimestamp();
@@ -493,21 +496,6 @@ void VideoRendererImpl::DoStopOrError_Locked() {
ready_frames_.clear();
}
-void VideoRendererImpl::TransitionToPrerolled_Locked() {
- lock_.AssertAcquired();
- DCHECK_EQ(state_, kPrerolling);
-
- state_ = kPrerolled;
-
- // Because we might remain in the prerolled state for an undetermined amount
- // of time (e.g., we seeked while paused), we'll paint the first prerolled
- // frame.
- if (!ready_frames_.empty())
- PaintNextReadyFrame_Locked();
-
- base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
-}
-
void VideoRendererImpl::UpdateStatsAndWait_Locked(
base::TimeDelta wait_duration) {
lock_.AssertAcquired();
diff --git a/media/filters/video_renderer_impl.h b/media/filters/video_renderer_impl.h
index 4fef25d..bcba124 100644
--- a/media/filters/video_renderer_impl.h
+++ b/media/filters/video_renderer_impl.h
@@ -61,14 +61,13 @@ class MEDIA_EXPORT VideoRendererImpl
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const TimeCB& max_time_cb,
+ const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb,
const TimeDeltaCB& get_time_cb,
const TimeDeltaCB& get_duration_cb) OVERRIDE;
- virtual void Play(const base::Closure& callback) OVERRIDE;
virtual void Flush(const base::Closure& callback) OVERRIDE;
- virtual void Preroll(base::TimeDelta time,
- const PipelineStatusCB& cb) OVERRIDE;
+ virtual void StartPlayingFrom(base::TimeDelta timestamp) OVERRIDE;
virtual void Stop(const base::Closure& callback) OVERRIDE;
virtual void SetPlaybackRate(float playback_rate) OVERRIDE;
@@ -117,11 +116,10 @@ class MEDIA_EXPORT VideoRendererImpl
// A read is scheduled to replace the frame.
void DropNextReadyFrame_Locked();
- void TransitionToPrerolled_Locked();
-
- // Returns true of all conditions have been met to transition from
- // kPrerolling to kPrerolled.
- bool ShouldTransitionToPrerolled_Locked();
+ // Returns true if the renderer has enough data for playback purposes.
+ // Note that having enough data may be due to reaching end of stream.
+ bool HaveEnoughData_Locked();
+ void TransitionToHaveEnough_Locked();
// Runs |statistics_cb_| with |frames_decoded_| and |frames_dropped_|, resets
// them to 0, and then waits on |frame_available_| for up to the
@@ -152,36 +150,28 @@ class MEDIA_EXPORT VideoRendererImpl
// always check |state_| to see if it was set to STOPPED after waking up!
base::ConditionVariable frame_available_;
- // State transition Diagram of this class:
- // [kUninitialized]
- // |
- // | Initialize()
- // [kInitializing]
- // |
- // V
- // +------[kFlushed]<---------------OnVideoFrameStreamResetDone()
- // | | Preroll() or upon ^
- // | V got first frame [kFlushing]
- // | [kPrerolling] ^
- // | | |
- // | V Got enough frames |
- // | [kPrerolled]--------------------------|
- // | | Flush() ^
- // | V Play() |
- // | [kPlaying]---------------------------|
- // | Flush() ^ Flush()
- // | |
- // +-----> [kStopped] [Any state other than]
- // [ kUninitialized ]
-
- // Simple state tracking variable.
+ // Important detail: being in kPlaying doesn't imply that video is being
+ // rendered. Rather, it means that the renderer is ready to go. The actual
+ // rendering of video is controlled by time advancing via |time_cb_|.
+ //
+ // kUninitialized
+ // | Initialize()
+ // |
+ // V
+ // kInitializing
+ // | Decoders initialized
+ // |
+ // V Decoders reset
+ // kFlushed <------------------ kFlushing
+ // | StartPlayingFrom() ^
+ // | |
+ // | | Flush()
+ // `---------> kPlaying --------'
enum State {
kUninitialized,
kInitializing,
- kPrerolled,
kFlushing,
kFlushed,
- kPrerolling,
kPlaying,
kStopped,
};
@@ -198,20 +188,22 @@ class MEDIA_EXPORT VideoRendererImpl
float playback_rate_;
+ BufferingState buffering_state_;
+
// Playback operation callbacks.
base::Closure flush_cb_;
- PipelineStatusCB preroll_cb_;
// Event callbacks.
PipelineStatusCB init_cb_;
StatisticsCB statistics_cb_;
TimeCB max_time_cb_;
+ BufferingStateCB buffering_state_cb_;
base::Closure ended_cb_;
PipelineStatusCB error_cb_;
TimeDeltaCB get_time_cb_;
TimeDeltaCB get_duration_cb_;
- base::TimeDelta preroll_timestamp_;
+ base::TimeDelta start_timestamp_;
// Embedder callback for notifying a new frame is available for painting.
PaintCB paint_cb_;
diff --git a/media/filters/video_renderer_impl_unittest.cc b/media/filters/video_renderer_impl_unittest.cc
index 355d875..c7eb556 100644
--- a/media/filters/video_renderer_impl_unittest.cc
+++ b/media/filters/video_renderer_impl_unittest.cc
@@ -57,13 +57,12 @@ class VideoRendererImplTest : public ::testing::Test {
ScopedVector<VideoDecoder> decoders;
decoders.push_back(decoder_);
- renderer_.reset(
- new VideoRendererImpl(message_loop_.message_loop_proxy(),
- decoders.Pass(),
- media::SetDecryptorReadyCB(),
- base::Bind(&StrictMock<MockDisplayCB>::Display,
- base::Unretained(&mock_display_cb_)),
- true));
+ renderer_.reset(new VideoRendererImpl(
+ message_loop_.message_loop_proxy(),
+ decoders.Pass(),
+ media::SetDecryptorReadyCB(),
+ base::Bind(&StrictMock<MockCB>::Display, base::Unretained(&mock_cb_)),
+ true));
demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal());
@@ -125,6 +124,8 @@ class VideoRendererImplTest : public ::testing::Test {
base::Unretained(&statistics_cb_object_)),
base::Bind(&VideoRendererImplTest::OnTimeUpdate,
base::Unretained(this)),
+ base::Bind(&StrictMock<MockCB>::BufferingStateChange,
+ base::Unretained(&mock_cb_)),
ended_event_.GetClosure(),
error_event_.GetPipelineStatusCB(),
base::Bind(&VideoRendererImplTest::GetTime, base::Unretained(this)),
@@ -132,20 +133,11 @@ class VideoRendererImplTest : public ::testing::Test {
base::Unretained(this)));
}
- void Play() {
- SCOPED_TRACE("Play()");
- WaitableMessageLoopEvent event;
- renderer_->Play(event.GetClosure());
- event.RunAndWait();
- }
-
- void Preroll(int timestamp_ms, PipelineStatus expected) {
- SCOPED_TRACE(base::StringPrintf("Preroll(%d, %d)", timestamp_ms, expected));
- WaitableMessageLoopEvent event;
- renderer_->Preroll(
- base::TimeDelta::FromMilliseconds(timestamp_ms),
- event.GetPipelineStatusCB());
- event.RunAndWaitForStatus(expected);
+ void StartPlayingFrom(int timestamp_ms) {
+ SCOPED_TRACE(base::StringPrintf("StartPlayingFrom(%d)", timestamp_ms));
+ renderer_->StartPlayingFrom(
+ base::TimeDelta::FromMilliseconds(timestamp_ms));
+ message_loop_.RunUntilIdle();
}
void Flush() {
@@ -163,7 +155,6 @@ class VideoRendererImplTest : public ::testing::Test {
}
void Shutdown() {
- Flush();
Stop();
}
@@ -290,12 +281,13 @@ class VideoRendererImplTest : public ::testing::Test {
NiceMock<MockDemuxerStream> demuxer_stream_;
MockStatisticsCB statistics_cb_object_;
- // Use StrictMock<T> to catch missing/extra display callbacks.
- class MockDisplayCB {
+ // Use StrictMock<T> to catch missing/extra callbacks.
+ class MockCB {
public:
MOCK_METHOD1(Display, void(const scoped_refptr<VideoFrame>&));
+ MOCK_METHOD1(BufferingStateChange, void(BufferingState));
};
- StrictMock<MockDisplayCB> mock_display_cb_;
+ StrictMock<MockCB> mock_cb_;
private:
base::TimeDelta GetTime() {
@@ -380,11 +372,12 @@ TEST_F(VideoRendererImplTest, Initialize) {
Shutdown();
}
-TEST_F(VideoRendererImplTest, InitializeAndPreroll) {
+TEST_F(VideoRendererImplTest, InitializeAndStartPlayingFrom) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
Shutdown();
}
@@ -402,6 +395,11 @@ TEST_F(VideoRendererImplTest, StopWhileInitializing) {
TEST_F(VideoRendererImplTest, StopWhileFlushing) {
Initialize();
+ QueueFrames("0 10 20 30");
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING));
renderer_->Flush(base::Bind(&ExpectNotCalled, PIPELINE_OK));
Stop();
@@ -411,18 +409,28 @@ TEST_F(VideoRendererImplTest, StopWhileFlushing) {
TEST_F(VideoRendererImplTest, Play) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
+ Shutdown();
+}
+
+TEST_F(VideoRendererImplTest, FlushWithNothingBuffered) {
+ Initialize();
+ StartPlayingFrom(0);
+
+ // We shouldn't expect a buffering state change since we never reached
+ // BUFFERING_HAVE_ENOUGH.
+ Flush();
Shutdown();
}
TEST_F(VideoRendererImplTest, EndOfStream_ClipDuration) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
// Next frame has timestamp way past duration. Its timestamp will be adjusted
// to match the duration of the video.
@@ -433,7 +441,7 @@ TEST_F(VideoRendererImplTest, EndOfStream_ClipDuration) {
// Queue the end of stream frame and wait for the last frame to be rendered.
SatisfyPendingReadWithEndOfStream();
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(kVideoDurationInMs)));
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(kVideoDurationInMs)));
AdvanceTimeInMs(kVideoDurationInMs);
WaitForEnded();
@@ -443,9 +451,9 @@ TEST_F(VideoRendererImplTest, EndOfStream_ClipDuration) {
TEST_F(VideoRendererImplTest, DecodeError_Playing) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
QueueFrames("error");
SatisfyPendingRead();
@@ -453,54 +461,57 @@ TEST_F(VideoRendererImplTest, DecodeError_Playing) {
Shutdown();
}
-TEST_F(VideoRendererImplTest, DecodeError_DuringPreroll) {
+TEST_F(VideoRendererImplTest, DecodeError_DuringStartPlayingFrom) {
Initialize();
QueueFrames("error");
- Preroll(0, PIPELINE_ERROR_DECODE);
+ StartPlayingFrom(0);
Shutdown();
}
-TEST_F(VideoRendererImplTest, Preroll_Exact) {
+TEST_F(VideoRendererImplTest, StartPlayingFrom_Exact) {
Initialize();
QueueFrames("50 60 70 80 90");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(60)));
- Preroll(60, PIPELINE_OK);
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(60)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(60);
Shutdown();
}
-TEST_F(VideoRendererImplTest, Preroll_RightBefore) {
+TEST_F(VideoRendererImplTest, StartPlayingFrom_RightBefore) {
Initialize();
QueueFrames("50 60 70 80 90");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(50)));
- Preroll(59, PIPELINE_OK);
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(50)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(59);
Shutdown();
}
-TEST_F(VideoRendererImplTest, Preroll_RightAfter) {
+TEST_F(VideoRendererImplTest, StartPlayingFrom_RightAfter) {
Initialize();
QueueFrames("50 60 70 80 90");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(60)));
- Preroll(61, PIPELINE_OK);
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(60)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(61);
Shutdown();
}
-TEST_F(VideoRendererImplTest, Preroll_LowDelay) {
+TEST_F(VideoRendererImplTest, StartPlayingFrom_LowDelay) {
// In low-delay mode only one frame is required to finish preroll.
InitializeWithLowDelay(true);
QueueFrames("0");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
QueueFrames("10");
SatisfyPendingRead();
WaitableMessageLoopEvent event;
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(10)))
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(10)))
.WillOnce(RunClosure(event.GetClosure()));
AdvanceTimeInMs(10);
event.RunAndWait();
@@ -508,12 +519,12 @@ TEST_F(VideoRendererImplTest, Preroll_LowDelay) {
Shutdown();
}
-TEST_F(VideoRendererImplTest, PlayAfterPreroll) {
+TEST_F(VideoRendererImplTest, PlayAfterStartPlayingFrom) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
// Check that there is an outstanding Read() request.
EXPECT_TRUE(IsReadPending());
@@ -521,73 +532,13 @@ TEST_F(VideoRendererImplTest, PlayAfterPreroll) {
Shutdown();
}
-TEST_F(VideoRendererImplTest, Rebuffer) {
- Initialize();
- QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
-
- // Advance time past prerolled time drain the ready frame queue.
- AdvanceTimeInMs(50);
- WaitForPendingRead();
-
- // Simulate a Preroll/Play rebuffer sequence.
- WaitableMessageLoopEvent event;
- renderer_->Preroll(kNoTimestamp(),
- event.GetPipelineStatusCB());
-
- // Queue enough frames to satisfy preroll.
- QueueFrames("40 50 60 70");
- SatisfyPendingRead();
-
- // TODO(scherkus): We shouldn't display the next ready frame in a rebuffer
- // situation, see http://crbug.com/365516
- EXPECT_CALL(mock_display_cb_, Display(_)).Times(AtLeast(1));
-
- event.RunAndWaitForStatus(PIPELINE_OK);
-
- Play();
-
- Shutdown();
-}
-
-TEST_F(VideoRendererImplTest, Rebuffer_AlreadyHaveEnoughFrames) {
- Initialize();
- QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
-
- // Queue an extra frame so that we'll have enough frames to satisfy
- // preroll even after the first frame is painted.
- QueueFrames("40");
- SatisfyPendingRead();
- Play();
-
- // Simulate a Preroll/Play rebuffer sequence.
- //
- // TODO(scherkus): We shouldn't display the next ready frame in a rebuffer
- // situation, see http://crbug.com/365516
- EXPECT_CALL(mock_display_cb_, Display(_)).Times(AtLeast(1));
-
- WaitableMessageLoopEvent event;
- renderer_->Preroll(kNoTimestamp(),
- event.GetPipelineStatusCB());
-
- event.RunAndWaitForStatus(PIPELINE_OK);
-
- Play();
-
- Shutdown();
-}
-
// Verify that a late decoder response doesn't break invariants in the renderer.
TEST_F(VideoRendererImplTest, StopDuringOutstandingRead) {
Initialize();
QueueFrames("0 10 20 30");
- EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0)));
- Preroll(0, PIPELINE_OK);
- Play();
+ EXPECT_CALL(mock_cb_, Display(HasTimestamp(0)));
+ EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ StartPlayingFrom(0);
// Check that there is an outstanding Read() request.
EXPECT_TRUE(IsReadPending());