Index: media/filters/video_renderer_base.cc |
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc |
index 5a17940771120c543902274a13cf15f3011e43c5..3964f1a3dda11af716d8efea3700de2353ea3d7e 100644 |
--- a/media/filters/video_renderer_base.cc |
+++ b/media/filters/video_renderer_base.cc |
@@ -25,7 +25,7 @@ base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { |
VideoRendererBase::VideoRendererBase( |
const scoped_refptr<base::MessageLoopProxy>& message_loop, |
const SetDecryptorReadyCB& set_decryptor_ready_cb, |
- const base::Closure& paint_cb, |
+ const PaintCB& paint_cb, |
const SetOpaqueCB& set_opaque_cb, |
bool drop_frames) |
: message_loop_(message_loop), |
@@ -34,12 +34,11 @@ VideoRendererBase::VideoRendererBase( |
state_(kUninitialized), |
thread_(base::kNullThreadHandle), |
pending_read_(false), |
- pending_paint_(false), |
- pending_paint_with_last_available_(false), |
drop_frames_(drop_frames), |
playback_rate_(0), |
paint_cb_(paint_cb), |
- set_opaque_cb_(set_opaque_cb) { |
+ set_opaque_cb_(set_opaque_cb), |
+ last_timestamp_(kNoTimestamp()) { |
DCHECK(!paint_cb_.is_null()); |
} |
@@ -83,32 +82,31 @@ void VideoRendererBase::ResetDecoder() { |
void VideoRendererBase::Stop(const base::Closure& callback) { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
+ base::AutoLock auto_lock(lock_); |
if (state_ == kUninitialized || state_ == kStopped) { |
callback.Run(); |
return; |
} |
+ state_ = kStopped; |
+ |
+ statistics_cb_.Reset(); |
+ max_time_cb_.Reset(); |
+ DoStopOrError_Locked(); |
+ |
+ // Clean up our thread if present. |
base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle; |
- { |
- base::AutoLock auto_lock(lock_); |
- state_ = kStopped; |
- |
- statistics_cb_.Reset(); |
- max_time_cb_.Reset(); |
- if (!pending_paint_ && !pending_paint_with_last_available_) |
- DoStopOrError_Locked(); |
- |
- // Clean up our thread if present. |
- if (thread_ != base::kNullThreadHandle) { |
- // Signal the thread since it's possible to get stopped with the video |
- // thread waiting for a read to complete. |
- frame_available_.Signal(); |
- thread_to_join = thread_; |
- thread_ = base::kNullThreadHandle; |
- } |
+ if (thread_ != base::kNullThreadHandle) { |
+ // Signal the thread since it's possible to get stopped with the video |
+ // thread waiting for a read to complete. |
+ frame_available_.Signal(); |
+ std::swap(thread_, thread_to_join); |
} |
- if (thread_to_join != base::kNullThreadHandle) |
+ |
+ if (thread_to_join != base::kNullThreadHandle) { |
+ base::AutoUnlock auto_unlock(lock_); |
base::PlatformThread::Join(thread_to_join); |
+ } |
if (decrypting_demuxer_stream_) { |
decrypting_demuxer_stream_->Reset(base::Bind( |
@@ -252,8 +250,6 @@ void VideoRendererBase::ThreadMain() { |
const base::TimeDelta kIdleTimeDelta = |
base::TimeDelta::FromMilliseconds(10); |
- uint32 frames_dropped = 0; |
- |
for (;;) { |
base::AutoLock auto_lock(lock_); |
@@ -261,14 +257,6 @@ void VideoRendererBase::ThreadMain() { |
if (state_ == kStopped) |
return; |
- if (frames_dropped > 0) { |
- PipelineStatistics statistics; |
- statistics.video_frames_dropped = frames_dropped; |
- statistics_cb_.Run(statistics); |
- |
- frames_dropped = 0; |
- } |
- |
// Remain idle as long as we're not playing. |
if (state_ != kPlaying || playback_rate_ == 0) { |
frame_available_.TimedWait(kIdleTimeDelta); |
@@ -281,25 +269,15 @@ void VideoRendererBase::ThreadMain() { |
continue; |
} |
- // Remain idle until we've initialized |current_frame_| via prerolling. |
- if (!current_frame_) { |
- // This can happen if our preroll only contains end of stream frames. |
- if (ready_frames_.front()->IsEndOfStream()) { |
- state_ = kEnded; |
- ended_cb_.Run(); |
- ready_frames_.clear(); |
acolwell GONE FROM CHROMIUM
2013/02/04 17:35:15
Shouldn't we keep this line?
scherkus (not reviewing)
2013/02/05 00:29:39
Done.
|
- |
- // No need to sleep here as we idle when |state_ != kPlaying|. |
- continue; |
- } |
+ // We've reached the end! |
+ if (ready_frames_.front()->IsEndOfStream()) { |
+ state_ = kEnded; |
+ ended_cb_.Run(); |
- frame_available_.TimedWait(kIdleTimeDelta); |
+ // No need to sleep here as we idle when |state_ != kPlaying|. |
continue; |
} |
- // Calculate how long until we should advance the frame, which is |
- // typically negative but for playback rates < 1.0f may be long enough |
- // that it makes more sense to idle and check again. |
base::TimeDelta remaining_time = |
CalculateSleepDuration(ready_frames_.front(), playback_rate_); |
@@ -311,144 +289,63 @@ void VideoRendererBase::ThreadMain() { |
continue; |
} |
- |
- // We're almost there! |
- // |
- // At this point we've rendered |current_frame_| for the proper amount |
- // of time and also have the next frame that ready for rendering. |
- |
- |
- // If the next frame is end of stream then we are truly at the end of the |
- // video stream. |
+ // Deadline is defined as the midpoint between this frame and the next |
scherkus (not reviewing)
2013/02/01 22:45:25
The old code's deadline was defined as:
remaining_
scherkus (not reviewing)
2013/02/05 00:29:39
Expanded this comment w/ more ideas.
|
+ // frame, using the delta between this frame and the previous frame as the |
+ // assumption for frame duration. |
// |
- // TODO(scherkus): deduplicate this end of stream check after we get rid of |
- // |current_frame_|. |
- if (ready_frames_.front()->IsEndOfStream()) { |
- state_ = kEnded; |
- ended_cb_.Run(); |
- ready_frames_.clear(); |
- |
- // No need to sleep here as we idle when |state_ != kPlaying|. |
- continue; |
- } |
- |
- // We cannot update |current_frame_| until we've completed the pending |
- // paint. Furthermore, the pending paint might be really slow: check to |
- // see if we have any ready frames that we can drop if they've already |
- // expired. |
- if (pending_paint_) { |
- while (!ready_frames_.empty()) { |
- // Can't drop anything if we're at the end. |
- if (ready_frames_.front()->IsEndOfStream()) |
- break; |
- |
- base::TimeDelta remaining_time = |
- ready_frames_.front()->GetTimestamp() - get_time_cb_.Run(); |
- |
- // Still a chance we can render the frame! |
- if (remaining_time.InMicroseconds() > 0) |
- break; |
- |
- if (!drop_frames_) |
- break; |
- |
- // Frame dropped: read again. |
- ++frames_dropped; |
- ready_frames_.pop_front(); |
- message_loop_->PostTask(FROM_HERE, base::Bind( |
- &VideoRendererBase::AttemptRead, this)); |
+ // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
+ // the accuracy of our frame timing code. http://crbug.com/149829 |
+ if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { |
+ base::TimeDelta now = get_time_cb_.Run(); |
+ base::TimeDelta deadline = ready_frames_.front()->GetTimestamp() + |
+ (ready_frames_.front()->GetTimestamp() - last_timestamp_) / 2; |
+ |
+ if (now > deadline) { |
+ DropNextReadyFrame_Locked(); |
+ continue; |
} |
- // Continue waiting for the current paint to finish. |
- frame_available_.TimedWait(kIdleTimeDelta); |
- continue; |
} |
- |
// Congratulations! You've made it past the video frame timing gauntlet. |
// |
- // We can now safely update the current frame, request another frame, and |
- // signal to the client that a new frame is available. |
- DCHECK(!pending_paint_); |
- DCHECK(!ready_frames_.empty()); |
- SetCurrentFrameToNextReadyFrame(); |
- message_loop_->PostTask(FROM_HERE, base::Bind( |
- &VideoRendererBase::AttemptRead, this)); |
- |
- base::AutoUnlock auto_unlock(lock_); |
- paint_cb_.Run(); |
+ // At this point enough time has passed that the next frame that ready for |
+ // rendering. |
+ PaintNextReadyFrame_Locked(); |
} |
} |
-void VideoRendererBase::SetCurrentFrameToNextReadyFrame() { |
- current_frame_ = ready_frames_.front(); |
+void VideoRendererBase::PaintNextReadyFrame_Locked() { |
+ lock_.AssertAcquired(); |
+ |
+ scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); |
ready_frames_.pop_front(); |
- // Notify the pipeline of natural_size() changes. |
- const gfx::Size& natural_size = current_frame_->natural_size(); |
+ last_timestamp_ = next_frame->GetTimestamp(); |
+ |
+ const gfx::Size& natural_size = next_frame->natural_size(); |
if (natural_size != last_natural_size_) { |
- size_changed_cb_.Run(natural_size); |
last_natural_size_ = natural_size; |
+ size_changed_cb_.Run(natural_size); |
} |
-} |
- |
-void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
- base::AutoLock auto_lock(lock_); |
- DCHECK(!pending_paint_ && !pending_paint_with_last_available_); |
- if ((!current_frame_ || current_frame_->IsEndOfStream()) && |
- (!last_available_frame_ || last_available_frame_->IsEndOfStream())) { |
- *frame_out = NULL; |
- return; |
- } |
+ paint_cb_.Run(next_frame); |
- // We should have initialized and have the current frame. |
- DCHECK_NE(state_, kUninitialized); |
- DCHECK_NE(state_, kStopped); |
- DCHECK_NE(state_, kError); |
- |
- if (current_frame_) { |
- *frame_out = current_frame_; |
- last_available_frame_ = current_frame_; |
- pending_paint_ = true; |
- } else { |
- DCHECK(last_available_frame_); |
- *frame_out = last_available_frame_; |
- pending_paint_with_last_available_ = true; |
- } |
+ message_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoRendererBase::AttemptRead, this)); |
} |
-void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { |
- base::AutoLock auto_lock(lock_); |
- |
- // Note that we do not claim |pending_paint_| when we return NULL frame, in |
- // that case, |current_frame_| could be changed before PutCurrentFrame. |
- if (pending_paint_) { |
- DCHECK_EQ(current_frame_, frame); |
- DCHECK(!pending_paint_with_last_available_); |
- pending_paint_ = false; |
- } else if (pending_paint_with_last_available_) { |
- DCHECK_EQ(last_available_frame_, frame); |
- DCHECK(!pending_paint_); |
- pending_paint_with_last_available_ = false; |
- } else { |
- DCHECK(!frame); |
- } |
+void VideoRendererBase::DropNextReadyFrame_Locked() { |
+ lock_.AssertAcquired(); |
- // We had cleared the |pending_paint_| flag, there are chances that current |
- // frame is timed-out. We will wake up our main thread to advance the current |
- // frame when this is true. |
- frame_available_.Signal(); |
- if (state_ == kFlushingDecoder) |
- return; |
+ last_timestamp_ = ready_frames_.front()->GetTimestamp(); |
+ ready_frames_.pop_front(); |
- if (state_ == kFlushing) { |
- AttemptFlush_Locked(); |
- return; |
- } |
+ PipelineStatistics statistics; |
+ statistics.video_frames_dropped = 1; |
+ statistics_cb_.Run(statistics); |
- if (state_ == kError || state_ == kStopped) { |
- DoStopOrError_Locked(); |
- } |
+ message_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoRendererBase::AttemptRead, this)); |
} |
VideoRendererBase::~VideoRendererBase() { |
@@ -532,23 +429,18 @@ void VideoRendererBase::FrameReady(VideoDecoder::Status status, |
// If we're at capacity or end of stream while prerolling we need to |
// transition to prerolled. |
- if (state_ == kPrerolling) { |
- DCHECK(!current_frame_); |
- state_ = kPrerolled; |
+ if (state_ != kPrerolling) |
+ return; |
- // Because we might remain in the prerolled state for an undetermined amount |
- // of time (i.e., we were not playing before we started prerolling), we'll |
- // manually update the current frame and notify the subclass below. |
- if (!ready_frames_.front()->IsEndOfStream()) |
- SetCurrentFrameToNextReadyFrame(); |
+ state_ = kPrerolled; |
- // ...and we're done prerolling! |
- DCHECK(!preroll_cb_.is_null()); |
- base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
+ // 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_.front()->IsEndOfStream()) |
+ PaintNextReadyFrame_Locked(); |
- base::AutoUnlock ul(lock_); |
- paint_cb_.Run(); |
- } |
+ base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
} |
void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) { |
@@ -564,10 +456,9 @@ void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) { |
end_timestamp = std::min( |
duration, |
ready_frames_.back()->GetTimestamp() + kMaxLastFrameDuration()); |
- } else if (current_frame_) { |
+ } else if (last_timestamp_ != kNoTimestamp()) { |
end_timestamp = |
- std::min(duration, |
- current_frame_->GetTimestamp() + kMaxLastFrameDuration()); |
+ std::min(duration, last_timestamp_ + kMaxLastFrameDuration()); |
} |
frame->SetTimestamp(end_timestamp); |
} else if (frame->GetTimestamp() > duration) { |
@@ -636,11 +527,12 @@ void VideoRendererBase::AttemptFlush_Locked() { |
prerolling_delayed_frame_ = NULL; |
ready_frames_.clear(); |
- if (!pending_paint_ && !pending_read_) { |
- state_ = kFlushed; |
- current_frame_ = NULL; |
- base::ResetAndReturn(&flush_cb_).Run(); |
- } |
+ if (pending_read_) |
+ return; |
+ |
+ state_ = kFlushed; |
+ last_timestamp_ = kNoTimestamp(); |
+ base::ResetAndReturn(&flush_cb_).Run(); |
} |
base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
@@ -657,20 +549,14 @@ base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
} |
void VideoRendererBase::DoStopOrError_Locked() { |
- DCHECK(!pending_paint_); |
- DCHECK(!pending_paint_with_last_available_); |
lock_.AssertAcquired(); |
- current_frame_ = NULL; |
- last_available_frame_ = NULL; |
+ last_timestamp_ = kNoTimestamp(); |
ready_frames_.clear(); |
} |
int VideoRendererBase::NumFrames_Locked() const { |
lock_.AssertAcquired(); |
- int outstanding_frames = |
- (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + |
- (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); |
- return ready_frames_.size() + outstanding_frames; |
+ return ready_frames_.size(); |
} |
} // namespace media |