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

Unified Diff: media/filters/video_renderer_base.cc

Issue 12262058: Revert r180578, r180591, and r180604 from 1410 branch. (Closed) Base URL: svn://svn.chromium.org/chrome/branches/1410/src/
Patch Set: Created 7 years, 10 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
===================================================================
--- media/filters/video_renderer_base.cc (revision 182591)
+++ media/filters/video_renderer_base.cc (working copy)
@@ -25,30 +25,24 @@
VideoRendererBase::VideoRendererBase(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
const SetDecryptorReadyCB& set_decryptor_ready_cb,
- const PaintCB& paint_cb,
+ const base::Closure& paint_cb,
const SetOpaqueCB& set_opaque_cb,
bool drop_frames)
: message_loop_(message_loop),
- weak_factory_(this),
set_decryptor_ready_cb_(set_decryptor_ready_cb),
frame_available_(&lock_),
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),
- last_timestamp_(kNoTimestamp()) {
+ set_opaque_cb_(set_opaque_cb) {
DCHECK(!paint_cb_.is_null());
}
-VideoRendererBase::~VideoRendererBase() {
- base::AutoLock auto_lock(lock_);
- CHECK(state_ == kUninitialized || state_ == kStopped) << state_;
- CHECK_EQ(thread_, base::kNullThreadHandle);
-}
-
void VideoRendererBase::Play(const base::Closure& callback) {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
@@ -74,55 +68,51 @@
if (decrypting_demuxer_stream_) {
decrypting_demuxer_stream_->Reset(base::Bind(
- &VideoRendererBase::ResetDecoder, weak_this_));
+ &VideoRendererBase::ResetDecoder, this));
return;
}
- decoder_->Reset(base::Bind(
- &VideoRendererBase::OnDecoderResetDone, weak_this_));
+ decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderResetDone, this));
}
void VideoRendererBase::ResetDecoder() {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
- decoder_->Reset(base::Bind(
- &VideoRendererBase::OnDecoderResetDone, weak_this_));
+ decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderResetDone, this));
}
void VideoRendererBase::Stop(const base::Closure& callback) {
DCHECK(message_loop_->BelongsToCurrentThread());
- base::AutoLock auto_lock(lock_);
if (state_ == kUninitialized || state_ == kStopped) {
callback.Run();
return;
}
- // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
- // task-running guards that check |state_| with DCHECK().
+ base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle;
+ {
+ base::AutoLock auto_lock(lock_);
+ state_ = kStopped;
- state_ = kStopped;
+ statistics_cb_.Reset();
+ max_time_cb_.Reset();
+ if (!pending_paint_ && !pending_paint_with_last_available_)
+ DoStopOrError_Locked();
- statistics_cb_.Reset();
- max_time_cb_.Reset();
- DoStopOrError_Locked();
-
- // Clean up our thread if present.
- base::PlatformThreadHandle thread_to_join = 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);
+ // 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_to_join != base::kNullThreadHandle) {
- base::AutoUnlock auto_unlock(lock_);
+ if (thread_to_join != base::kNullThreadHandle)
base::PlatformThread::Join(thread_to_join);
- }
if (decrypting_demuxer_stream_) {
decrypting_demuxer_stream_->Reset(base::Bind(
- &VideoRendererBase::StopDecoder, weak_this_, callback));
+ &VideoRendererBase::StopDecoder, this, callback));
return;
}
@@ -180,7 +170,6 @@
DCHECK(!get_duration_cb.is_null());
DCHECK_EQ(kUninitialized, state_);
- weak_this_ = weak_factory_.GetWeakPtr();
init_cb_ = init_cb;
statistics_cb_ = statistics_cb;
max_time_cb_ = max_time_cb;
@@ -202,7 +191,7 @@
decoder_selector_ptr->SelectVideoDecoder(
stream,
statistics_cb,
- base::Bind(&VideoRendererBase::OnDecoderSelected, weak_this_,
+ base::Bind(&VideoRendererBase::OnDecoderSelected, this,
base::Passed(&decoder_selector)));
}
@@ -263,6 +252,8 @@
const base::TimeDelta kIdleTimeDelta =
base::TimeDelta::FromMilliseconds(10);
+ uint32 frames_dropped = 0;
+
for (;;) {
base::AutoLock auto_lock(lock_);
@@ -270,6 +261,14 @@
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);
@@ -282,16 +281,25 @@
continue;
}
- // We've reached the end!
- if (ready_frames_.front()->IsEndOfStream()) {
- state_ = kEnded;
- ready_frames_.clear();
- ended_cb_.Run();
+ // 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();
- // No need to sleep here as we idle when |state_ != kPlaying|.
+ // No need to sleep here as we idle when |state_ != kPlaying|.
+ continue;
+ }
+
+ frame_available_.TimedWait(kIdleTimeDelta);
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_);
@@ -303,70 +311,152 @@
continue;
}
- // Deadline is defined as the midpoint between this frame and the next
- // frame, using the delta between this frame and the previous frame as the
- // assumption for frame duration.
+
+ // We're almost there!
//
- // TODO(scherkus): An improvement over midpoint might be selecting the
- // minimum and/or maximum between the midpoint and some constants. As a
- // thought experiment, consider what would be better than the midpoint
- // for both the 1fps case and 120fps case.
+ // 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.
//
- // 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;
+ // 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();
- if (now > deadline) {
- DropNextReadyFrame_Locked();
- continue;
+ // 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));
}
+ // Continue waiting for the current paint to finish.
+ frame_available_.TimedWait(kIdleTimeDelta);
+ continue;
}
+
// Congratulations! You've made it past the video frame timing gauntlet.
//
- // At this point enough time has passed that the next frame that ready for
- // rendering.
- PaintNextReadyFrame_Locked();
+ // 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();
}
}
-void VideoRendererBase::PaintNextReadyFrame_Locked() {
- lock_.AssertAcquired();
-
- scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
+void VideoRendererBase::SetCurrentFrameToNextReadyFrame() {
+ current_frame_ = ready_frames_.front();
ready_frames_.pop_front();
- last_timestamp_ = next_frame->GetTimestamp();
-
- const gfx::Size& natural_size = next_frame->natural_size();
+ // Notify the pipeline of natural_size() changes.
+ const gfx::Size& natural_size = current_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);
}
+}
- paint_cb_.Run(next_frame);
+void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(!pending_paint_ && !pending_paint_with_last_available_);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &VideoRendererBase::AttemptRead, weak_this_));
+ if ((!current_frame_ || current_frame_->IsEndOfStream()) &&
+ (!last_available_frame_ || last_available_frame_->IsEndOfStream())) {
+ *frame_out = NULL;
+ return;
+ }
+
+ // 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;
+ }
}
-void VideoRendererBase::DropNextReadyFrame_Locked() {
- lock_.AssertAcquired();
+void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) {
+ base::AutoLock auto_lock(lock_);
- last_timestamp_ = ready_frames_.front()->GetTimestamp();
- ready_frames_.pop_front();
+ // 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);
+ }
- PipelineStatistics statistics;
- statistics.video_frames_dropped = 1;
- statistics_cb_.Run(statistics);
+ // 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;
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &VideoRendererBase::AttemptRead, weak_this_));
+ if (state_ == kFlushing) {
+ AttemptFlush_Locked();
+ return;
+ }
+
+ if (state_ == kError || state_ == kStopped) {
+ DoStopOrError_Locked();
+ }
}
+VideoRendererBase::~VideoRendererBase() {
+ base::AutoLock auto_lock(lock_);
+ CHECK(state_ == kUninitialized || state_ == kStopped) << state_;
+ CHECK_EQ(thread_, base::kNullThreadHandle);
+}
+
void VideoRendererBase::FrameReady(VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame) {
base::AutoLock auto_lock(lock_);
@@ -443,18 +533,23 @@
// If we're at capacity or end of stream while prerolling we need to
// transition to prerolled.
- if (state_ != kPrerolling)
- return;
+ if (state_ == kPrerolling) {
+ DCHECK(!current_frame_);
+ state_ = kPrerolled;
- state_ = kPrerolled;
+ // 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();
- // 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();
+ // ...and we're done prerolling!
+ DCHECK(!preroll_cb_.is_null());
+ base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
- base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
+ base::AutoUnlock ul(lock_);
+ paint_cb_.Run();
+ }
}
void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) {
@@ -470,9 +565,10 @@
end_timestamp = std::min(
duration,
ready_frames_.back()->GetTimestamp() + kMaxLastFrameDuration());
- } else if (last_timestamp_ != kNoTimestamp()) {
+ } else if (current_frame_) {
end_timestamp =
- std::min(duration, last_timestamp_ + kMaxLastFrameDuration());
+ std::min(duration,
+ current_frame_->GetTimestamp() + kMaxLastFrameDuration());
}
frame->SetTimestamp(end_timestamp);
} else if (frame->GetTimestamp() > duration) {
@@ -511,7 +607,7 @@
case kPrerolling:
case kPlaying:
pending_read_ = true;
- decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, weak_this_));
+ decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this));
return;
case kUninitialized:
@@ -541,12 +637,11 @@
prerolling_delayed_frame_ = NULL;
ready_frames_.clear();
- if (pending_read_)
- return;
-
- state_ = kFlushed;
- last_timestamp_ = kNoTimestamp();
- base::ResetAndReturn(&flush_cb_).Run();
+ if (!pending_paint_ && !pending_read_) {
+ state_ = kFlushed;
+ current_frame_ = NULL;
+ base::ResetAndReturn(&flush_cb_).Run();
+ }
}
base::TimeDelta VideoRendererBase::CalculateSleepDuration(
@@ -563,14 +658,20 @@
}
void VideoRendererBase::DoStopOrError_Locked() {
+ DCHECK(!pending_paint_);
+ DCHECK(!pending_paint_with_last_available_);
lock_.AssertAcquired();
- last_timestamp_ = kNoTimestamp();
+ current_frame_ = NULL;
+ last_available_frame_ = NULL;
ready_frames_.clear();
}
int VideoRendererBase::NumFrames_Locked() const {
lock_.AssertAcquired();
- return ready_frames_.size();
+ 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;
}
} // 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