Commit 4b6e4a9e authored by acolwell@chromium.org's avatar acolwell@chromium.org

Allow Preroll() call on VideoRendererImpl without a Flush().

This is the first patch in a sequence of changes to enable Pipeline
to properly manage underflows. This change allows the VideoRendererImpl
to be prerolled in non-seek situations.

BUG=144683
TEST=VideoRendererImplTest.Rebuffer, VideoRendererImplTest.Rebuffer_AlreadyHaveEnoughFrames

Review URL: https://codereview.chromium.org/89673002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238513 0039d316-1c4b-4281-b951-d872f2087c98
parent c401712b
...@@ -74,10 +74,13 @@ class MEDIA_EXPORT VideoRenderer { ...@@ -74,10 +74,13 @@ class MEDIA_EXPORT VideoRenderer {
// Discard any video data, executing |callback| when completed. // Discard any video data, executing |callback| when completed.
virtual void Flush(const base::Closure& callback) = 0; virtual void Flush(const base::Closure& callback) = 0;
// Start prerolling video data for samples starting at |time|, executing // Start prerolling video data. If |time| equals kNoTimestamp() then all
// |callback| when completed. // 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.
// //
// Only valid to call after a successful Initialize() or Flush(). // Only valid to call after a successful Initialize(), Pause(), or Flush().
virtual void Preroll(base::TimeDelta time, virtual void Preroll(base::TimeDelta time,
const PipelineStatusCB& callback) = 0; const PipelineStatusCB& callback) = 0;
......
...@@ -128,13 +128,27 @@ void VideoRendererImpl::Preroll(base::TimeDelta time, ...@@ -128,13 +128,27 @@ void VideoRendererImpl::Preroll(base::TimeDelta time,
const PipelineStatusCB& cb) { const PipelineStatusCB& cb) {
DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling.";
DCHECK(!cb.is_null()); DCHECK(!cb.is_null());
DCHECK(preroll_cb_.is_null()); DCHECK(preroll_cb_.is_null());
DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_;
if (state_ == kFlushed) {
DCHECK(time != kNoTimestamp());
DCHECK(!pending_read_);
DCHECK(ready_frames_.empty());
} else {
DCHECK(time == kNoTimestamp());
}
state_ = kPrerolling; state_ = kPrerolling;
preroll_cb_ = cb; preroll_cb_ = cb;
preroll_timestamp_ = time; preroll_timestamp_ = time;
if (ShouldTransitionToPrerolled_Locked()) {
TransitionToPrerolled_Locked();
return;
}
AttemptRead_Locked(); AttemptRead_Locked();
} }
...@@ -388,18 +402,15 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, ...@@ -388,18 +402,15 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
// Maintain the latest frame decoded so the correct frame is displayed after // Maintain the latest frame decoded so the correct frame is displayed after
// prerolling has completed. // prerolling has completed.
if (state_ == kPrerolling && frame->GetTimestamp() <= preroll_timestamp_) { if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() &&
frame->GetTimestamp() <= preroll_timestamp_) {
ready_frames_.clear(); ready_frames_.clear();
} }
AddReadyFrame_Locked(frame); AddReadyFrame_Locked(frame);
if (state_ == kPrerolling) { if (ShouldTransitionToPrerolled_Locked())
if (!video_frame_stream_.CanReadWithoutStalling() || TransitionToPrerolled_Locked();
ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames)) {
TransitionToPrerolled_Locked();
}
}
// Always request more decoded video if we have capacity. This serves two // Always request more decoded video if we have capacity. This serves two
// purposes: // purposes:
...@@ -408,6 +419,12 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, ...@@ -408,6 +419,12 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
AttemptRead_Locked(); AttemptRead_Locked();
} }
bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
return state_ == kPrerolling &&
(!video_frame_stream_.CanReadWithoutStalling() ||
ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames));
}
void VideoRendererImpl::AddReadyFrame_Locked( void VideoRendererImpl::AddReadyFrame_Locked(
const scoped_refptr<VideoFrame>& frame) { const scoped_refptr<VideoFrame>& frame) {
lock_.AssertAcquired(); lock_.AssertAcquired();
......
...@@ -129,6 +129,10 @@ class MEDIA_EXPORT VideoRendererImpl ...@@ -129,6 +129,10 @@ class MEDIA_EXPORT VideoRendererImpl
void TransitionToPrerolled_Locked(); void TransitionToPrerolled_Locked();
// Returns true of all conditions have been met to transition from
// kPrerolling to kPrerolled.
bool ShouldTransitionToPrerolled_Locked();
// Runs |statistics_cb_| with |frames_decoded_| and |frames_dropped_|, resets // Runs |statistics_cb_| with |frames_decoded_| and |frames_dropped_|, resets
// them to 0, and then waits on |frame_available_| for up to the // them to 0, and then waits on |frame_available_| for up to the
// |wait_duration|. // |wait_duration|.
......
...@@ -530,6 +530,57 @@ TEST_F(VideoRendererImplTest, PlayAfterPreroll) { ...@@ -530,6 +530,57 @@ TEST_F(VideoRendererImplTest, PlayAfterPreroll) {
Shutdown(); Shutdown();
} }
TEST_F(VideoRendererImplTest, Rebuffer) {
Initialize();
Play();
// Advance time past prerolled time drain the ready frame queue.
AdvanceTimeInMs(5 * kFrameDurationInMs);
WaitForPendingRead();
// Simulate a Pause/Preroll/Play rebuffer sequence.
Pause();
WaitableMessageLoopEvent event;
renderer_->Preroll(kNoTimestamp(),
event.GetPipelineStatusCB());
// Queue enough frames to satisfy preroll.
for (int i = 0; i < limits::kMaxVideoFrames; ++i)
QueueNextFrame();
SatisfyPendingRead();
event.RunAndWaitForStatus(PIPELINE_OK);
Play();
Shutdown();
}
TEST_F(VideoRendererImplTest, Rebuffer_AlreadyHaveEnoughFrames) {
Initialize();
// Queue an extra frame so that we'll have enough frames to satisfy
// preroll even after the first frame is painted.
QueueNextFrame();
Play();
// Simulate a Pause/Preroll/Play rebuffer sequence.
Pause();
WaitableMessageLoopEvent event;
renderer_->Preroll(kNoTimestamp(),
event.GetPipelineStatusCB());
event.RunAndWaitForStatus(PIPELINE_OK);
Play();
Shutdown();
}
TEST_F(VideoRendererImplTest, GetCurrentFrame_Initialized) { TEST_F(VideoRendererImplTest, GetCurrentFrame_Initialized) {
Initialize(); Initialize();
EXPECT_TRUE(GetCurrentFrame().get()); // Due to prerolling. EXPECT_TRUE(GetCurrentFrame().get()); // Due to prerolling.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment