Index: media/filters/video_renderer_base.cc |
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc |
index fca51ca80f41cdd234075373512b8549d613c038..3752e080a850007e4323df1e859d276a04de67ac 100644 |
--- a/media/filters/video_renderer_base.cc |
+++ b/media/filters/video_renderer_base.cc |
@@ -30,10 +30,12 @@ VideoRendererBase::VideoRendererBase() |
: frame_available_(&lock_), |
state_(kUninitialized), |
thread_(base::kNullThreadHandle), |
- pending_reads_(0), |
+ pending_read_(false), |
pending_paint_(false), |
pending_paint_with_last_available_(false), |
- playback_rate_(0) { |
+ playback_rate_(0), |
+ frame_ready_cb_(base::Bind(&VideoRendererBase::FrameReady, |
+ base::Unretained(this))) { |
} |
VideoRendererBase::~VideoRendererBase() { |
@@ -61,8 +63,7 @@ void VideoRendererBase::Flush(const base::Closure& callback) { |
flush_callback_ = callback; |
state_ = kFlushing; |
- if (!pending_paint_) |
- FlushBuffers_Locked(); |
+ AttemptFlush_Locked(); |
} |
void VideoRendererBase::Stop(const base::Closure& callback) { |
@@ -72,7 +73,7 @@ void VideoRendererBase::Stop(const base::Closure& callback) { |
state_ = kStopped; |
if (!pending_paint_ && !pending_paint_with_last_available_) |
- DoStopOrErrorFlush_Locked(); |
+ DoStopOrError_Locked(); |
// Clean up our thread if present. |
if (thread_) { |
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Given the code below shouldn't this be (thread_ !=
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
|
@@ -120,7 +121,7 @@ void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
} |
seek_timestamp_ = time; |
- ScheduleRead_Locked(); |
+ AttemptRead_Locked(); |
} |
if (run_callback) |
@@ -139,10 +140,6 @@ void VideoRendererBase::Initialize(VideoDecoder* decoder, |
statistics_callback_ = stats_callback; |
- decoder_->set_consume_video_frame_callback( |
- base::Bind(&VideoRendererBase::ConsumeVideoFrame, |
- base::Unretained(this))); |
- |
// Notify the pipeline of the video dimensions. |
host()->SetNaturalVideoSize(decoder_->natural_size()); |
@@ -269,7 +266,7 @@ void VideoRendererBase::ThreadMain() { |
// If we got here then: |
// 1. next frame's timestamp is already current; or |
- // 2. we do not have any current frame yet anyway; or |
+ // 2. we do not have a current frame yet; or |
// 3. a special case when the stream is badly formatted and |
// we got a frame with timestamp greater than overall duration. |
// In this case we should proceed anyway and try to obtain the |
@@ -290,11 +287,10 @@ void VideoRendererBase::ThreadMain() { |
if (remaining_time.InMicroseconds() > 0) |
break; |
- // Frame dropped: transfer ready frame into done queue and read again. |
- frames_queue_done_.push_back(frames_queue_ready_.front()); |
- frames_queue_ready_.pop_front(); |
- ScheduleRead_Locked(); |
+ // Frame dropped: read again. |
++frames_dropped; |
+ frames_queue_ready_.pop_front(); |
+ AttemptRead_Locked(); |
} |
// Continue waiting for the current paint to finish. |
@@ -305,10 +301,9 @@ void VideoRendererBase::ThreadMain() { |
// |
// We can now safely update the current frame, request another frame, and |
// signal to the client that a new frame is available. |
- frames_queue_done_.push_back(current_frame_); |
current_frame_ = frames_queue_ready_.front(); |
frames_queue_ready_.pop_front(); |
- ScheduleRead_Locked(); |
+ AttemptRead_Locked(); |
base::AutoUnlock auto_unlock(lock_); |
OnFrameAvailable(); |
@@ -320,8 +315,7 @@ void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
DCHECK(!pending_paint_ && !pending_paint_with_last_available_); |
if ((!current_frame_ || current_frame_->IsEndOfStream()) && |
- (!last_available_frame_ || |
- last_available_frame_->IsEndOfStream())) { |
+ (!last_available_frame_ || last_available_frame_->IsEndOfStream())) { |
*frame_out = NULL; |
return; |
} |
@@ -364,161 +358,120 @@ void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { |
// frame when this is true. |
frame_available_.Signal(); |
if (state_ == kFlushing) { |
- FlushBuffers_Locked(); |
+ AttemptFlush_Locked(); |
} else if (state_ == kError || state_ == kStopped) { |
- DoStopOrErrorFlush_Locked(); |
+ DoStopOrError_Locked(); |
} |
} |
-void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { |
- if (frame) { |
- PipelineStatistics statistics; |
- statistics.video_frames_decoded = 1; |
- statistics_callback_.Run(statistics); |
- } |
+void VideoRendererBase::FrameReady(scoped_refptr<VideoFrame> frame) { |
+ DCHECK(frame); |
base::AutoLock auto_lock(lock_); |
- |
- if (!frame) { |
- EnterErrorState_Locked(PIPELINE_ERROR_DECODE); |
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Why not keep this?
|
- return; |
- } |
- |
- // Decoder could reach seek state before our Seek() get called. |
- // We will enter kSeeking |
- if (state_ == kFlushed) |
- state_ = kSeeking; |
- |
- // Synchronous flush between filters should prevent this from happening. |
- DCHECK_NE(state_, kStopped); |
- if (frame && !frame->IsEndOfStream()) |
- --pending_reads_; |
- |
DCHECK_NE(state_, kUninitialized); |
DCHECK_NE(state_, kStopped); |
DCHECK_NE(state_, kError); |
+ CHECK(pending_read_); |
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
DCHECK?
scherkus (not reviewing)
2011/11/03 04:55:59
Again this one of those things where a callback wa
|
- if (state_ == kPaused || state_ == kFlushing) { |
- // Decoder are flushing rubbish video frame, we will not display them. |
- if (frame && !frame->IsEndOfStream()) |
- frames_queue_done_.push_back(frame); |
- DCHECK_LE(frames_queue_done_.size(), |
- static_cast<size_t>(Limits::kMaxVideoFrames)); |
+ pending_read_ = false; |
- // Excluding kPause here, because in pause state, we will never |
- // transfer out-bounding buffer. We do not flush buffer when Compositor |
- // hold reference to our video frame either. |
- if (state_ == kFlushing && pending_paint_ == false) |
- FlushBuffers_Locked(); |
+ // Decoder could reach seek state before our Seek() get called. |
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
s/get/got/
|
+ // We will enter kSeeking |
+ if (state_ == kFlushed) { |
+ CHECK(false) << "I don't think this is possible"; |
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
CHECK(false) is always better spelled LOG(FATAL)
B
scherkus (not reviewing)
2011/11/03 04:55:59
Left these CHECK()s in for local testing -- they w
|
+ //state_ = kSeeking; |
+ return; |
+ } |
+ if (state_ == kPaused) { |
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Can't this happen based on the "Prerolling while p
scherkus (not reviewing)
2011/11/03 04:55:59
I believe it can:
T1: VRB holds lock inside FrameR
|
+ CHECK(false) << "I don't think this is possible"; |
+ //frame_queue_ready_.push_back(video_frame); |
+ return; |
+ } |
+ |
+ if (state_ == kFlushing) { |
+ AttemptFlush_Locked(); |
return; |
} |
// Discard frames until we reach our desired seek timestamp. |
if (state_ == kSeeking && !frame->IsEndOfStream() && |
(frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { |
- frames_queue_done_.push_back(frame); |
- ScheduleRead_Locked(); |
- } else { |
- frames_queue_ready_.push_back(frame); |
- DCHECK_LE(frames_queue_ready_.size(), |
- static_cast<size_t>(Limits::kMaxVideoFrames)); |
- frame_available_.Signal(); |
+ AttemptRead_Locked(); |
+ return; |
} |
- // Check for our preroll complete condition. |
- bool new_frame_available = false; |
+ // This one's a keeper! Place it in the ready queue. |
+ frames_queue_ready_.push_back(frame); |
+ DCHECK_LE(frames_queue_ready_.size(), |
+ static_cast<size_t>(Limits::kMaxVideoFrames)); |
+ frame_available_.Signal(); |
+ |
+ PipelineStatistics statistics; |
+ statistics.video_frames_decoded = 1; |
+ statistics_callback_.Run(statistics); |
+ |
+ // Always request more decoded video if we have capacity. This serves two |
+ // purposes: |
+ // 1) Prerolling while paused |
+ // 2) Keeps decoding going if video rendering thread starts falling behind |
+ if (frames_queue_ready_.size() < Limits::kMaxVideoFrames && |
+ !frame->IsEndOfStream()) { |
+ AttemptRead_Locked(); |
+ return; |
+ } |
+ |
+ // If we're at capacity or end of stream while seeking we need to transition |
+ // to prerolled. |
if (state_ == kSeeking) { |
- if (frames_queue_ready_.size() == Limits::kMaxVideoFrames || |
- frame->IsEndOfStream()) { |
- // We're paused, so make sure we update |current_frame_| to represent |
- // our new location. |
- state_ = kPrerolled; |
- |
- // Because we might remain paused (i.e., we were not playing before we |
- // received a seek), we can't rely on ThreadMain() to notify the subclass |
- // the frame has been updated. |
- scoped_refptr<VideoFrame> first_frame; |
- first_frame = frames_queue_ready_.front(); |
- if (!first_frame->IsEndOfStream()) { |
- frames_queue_ready_.pop_front(); |
- current_frame_ = first_frame; |
- } |
- new_frame_available = true; |
+ state_ = kPrerolled; |
+ |
+ // Because we might remain in the prerolled state for an undetermined amount |
+ // of time (i.e., we were not playing before we received a seek), we'll |
+ // manually update the current frame and notify the subclass below. |
+ if (!frames_queue_ready_.front()->IsEndOfStream()) { |
+ current_frame_ = frames_queue_ready_.front(); |
+ frames_queue_ready_.pop_front(); |
+ } |
- // If we reach prerolled state before Seek() is called by pipeline, |
- // |seek_callback_| is not set, we will return immediately during |
- // when Seek() is eventually called. |
- if (!seek_cb_.is_null()) { |
- ResetAndRunCB(&seek_cb_, PIPELINE_OK); |
- } |
+ // If we reach prerolled state before Seek() is called by pipeline, |
+ // |seek_callback_| is not set, we will return immediately during |
+ // when Seek() is eventually called. |
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
English.
scherkus (not reviewing)
2011/11/03 04:55:59
this and the comments up in Seek() are crazy bizar
|
+ if (!seek_cb_.is_null()) { |
+ ResetAndRunCB(&seek_cb_, PIPELINE_OK); |
} |
- } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { |
- OnFlushDone_Locked(); |
- } |
- if (new_frame_available) { |
- base::AutoUnlock auto_unlock(lock_); |
+ base::AutoUnlock ul(lock_); |
OnFrameAvailable(); |
} |
} |
-void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { |
- // We should never return empty frames or EOS frame. |
- DCHECK(frame && !frame->IsEndOfStream()); |
- |
- decoder_->ProduceVideoFrame(frame); |
- ++pending_reads_; |
-} |
- |
-void VideoRendererBase::ScheduleRead_Locked() { |
+void VideoRendererBase::AttemptRead_Locked() { |
lock_.AssertAcquired(); |
DCHECK_NE(kEnded, state_); |
- // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to |
- // provide buffer pools. In the future, we may want to implement real |
- // buffer pool to recycle buffers. |
- while (!frames_queue_done_.empty()) { |
- scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); |
- frames_queue_done_.pop_front(); |
- ReadInput(video_frame); |
+ |
+ if (pending_read_ || frames_queue_ready_.size() == Limits::kMaxVideoFrames) { |
+ return; |
} |
+ |
+ pending_read_ = true; |
+ decoder_->Read(frame_ready_cb_); |
} |
-void VideoRendererBase::FlushBuffers_Locked() { |
+void VideoRendererBase::AttemptFlush_Locked() { |
lock_.AssertAcquired(); |
- DCHECK(!pending_paint_); |
+ DCHECK_EQ(kFlushing, state_); |
- // We should never put EOF frame into "done queue". |
+ // Get rid of any ready frames. |
while (!frames_queue_ready_.empty()) { |
- scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); |
- if (!video_frame->IsEndOfStream()) { |
- frames_queue_done_.push_back(video_frame); |
- } |
frames_queue_ready_.pop_front(); |
} |
- if (current_frame_ && !current_frame_->IsEndOfStream()) { |
- frames_queue_done_.push_back(current_frame_); |
- } |
- current_frame_ = NULL; |
- |
- // Flush all buffers out to decoder. |
- ScheduleRead_Locked(); |
- |
- if (pending_reads_ == 0 && state_ == kFlushing) |
- OnFlushDone_Locked(); |
-} |
- |
-void VideoRendererBase::OnFlushDone_Locked() { |
- lock_.AssertAcquired(); |
- // Check all buffers are returned to owners. |
- DCHECK_EQ(frames_queue_done_.size(), 0u); |
- DCHECK(!current_frame_); |
- DCHECK(frames_queue_ready_.empty()); |
- if (!flush_callback_.is_null()) // This ensures callback is invoked once. |
+ if (!pending_paint_ && !pending_read_) { |
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
I think this may be where you can get into trouble
scherkus (not reviewing)
2011/11/03 04:55:59
Correct -- fixed + updated unit tests
|
+ state_ = kFlushed; |
+ current_frame_ = NULL; |
ResetAndRunCB(&flush_callback_); |
- |
- state_ = kFlushed; |
+ } |
} |
base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
@@ -560,7 +513,7 @@ void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { |
// Flush frames if we aren't in the middle of a paint. If we |
// are painting then flushing will happen when the paint completes. |
if (!pending_paint_ && !pending_paint_with_last_available_) |
- DoStopOrErrorFlush_Locked(); |
+ DoStopOrError_Locked(); |
switch (old_state) { |
case kUninitialized: |
@@ -596,13 +549,12 @@ void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { |
callback.Run(); |
} |
-void VideoRendererBase::DoStopOrErrorFlush_Locked() { |
+void VideoRendererBase::DoStopOrError_Locked() { |
DCHECK(!pending_paint_); |
DCHECK(!pending_paint_with_last_available_); |
lock_.AssertAcquired(); |
- FlushBuffers_Locked(); |
last_available_frame_ = NULL; |
- DCHECK_EQ(pending_reads_, 0); |
+ DCHECK(!pending_read_); |
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Looking at the code this doesn't seem like a safe
scherkus (not reviewing)
2011/11/03 04:55:59
I really don't know what this method does but the
|
} |
} // namespace media |