Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Unified Diff: media/filters/video_renderer_base.cc

Issue 12096081: Replace VideoRendererBase Get/PutCurrentFrame() with a VideoFrame-containing callback. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/video_renderer_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « media/filters/video_renderer_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698