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

Unified Diff: media/filters/video_renderer_base.cc

Issue 8417019: Simplify VideoDecodeEngine interface by making everything synchronous. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix for CaptureVideoDecoder Created 9 years, 1 month 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 fca51ca80f41cdd234075373512b8549d613c038..62f7b1750a11112dcf79ce55984a7db958010f46 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),
+ read_cb_(base::Bind(&VideoRendererBase::FrameReady,
+ base::Unretained(this))) {
}
VideoRendererBase::~VideoRendererBase() {
@@ -61,30 +63,29 @@ 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) {
- base::PlatformThreadHandle old_thread_handle = base::kNullThreadHandle;
+ base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle;
{
base::AutoLock auto_lock(lock_);
state_ = kStopped;
if (!pending_paint_ && !pending_paint_with_last_available_)
- DoStopOrErrorFlush_Locked();
+ DoStopOrError_Locked();
// Clean up our thread if present.
- if (thread_) {
+ 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();
- old_thread_handle = thread_;
+ thread_to_join = thread_;
thread_ = base::kNullThreadHandle;
}
}
- if (old_thread_handle)
- base::PlatformThread::Join(old_thread_handle);
+ if (thread_to_join != base::kNullThreadHandle)
+ base::PlatformThread::Join(thread_to_join);
// Signal the subclass we're stopping.
OnStop(callback);
@@ -96,35 +97,17 @@ void VideoRendererBase::SetPlaybackRate(float playback_rate) {
}
void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
- bool run_callback = false;
-
{
base::AutoLock auto_lock(lock_);
- // There is a race condition between filters to receive SeekTask().
- // It turns out we could receive buffer from decoder before seek()
- // is called on us. so we do the following:
- // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and
- // kSeeking => ( Receive enough buffers) => kPrerolled. )
- DCHECK(state_ == kPrerolled || state_ == kFlushed || state_ == kSeeking);
+ DCHECK_EQ(state_, kFlushed) << "Must flush prior to seeking.";
DCHECK(!cb.is_null());
DCHECK(seek_cb_.is_null());
- if (state_ == kPrerolled) {
- // Already get enough buffers from decoder.
- run_callback = true;
- } else {
- // Otherwise we are either kFlushed or kSeeking, but without enough
- // buffers we should save the callback function and call it later.
- state_ = kSeeking;
- seek_cb_ = cb;
- }
-
+ state_ = kSeeking;
+ seek_cb_ = cb;
seek_timestamp_ = time;
- ScheduleRead_Locked();
+ AttemptRead_Locked();
}
-
- if (run_callback)
- cb.Run(PIPELINE_OK);
}
void VideoRendererBase::Initialize(VideoDecoder* decoder,
@@ -139,10 +122,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());
@@ -150,7 +129,8 @@ void VideoRendererBase::Initialize(VideoDecoder* decoder,
// TODO(scherkus): do we trust subclasses not to do something silly while
// we're holding the lock?
if (!OnInitialize(decoder)) {
- EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ state_ = kError;
+ host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
callback.Run();
return;
}
@@ -164,7 +144,8 @@ void VideoRendererBase::Initialize(VideoDecoder* decoder,
// Create our video thread.
if (!base::PlatformThread::Create(0, this, &thread_)) {
NOTREACHED() << "Video thread creation failed";
- EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ state_ = kError;
+ host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
callback.Run();
return;
}
@@ -269,7 +250,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 +271,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 +285,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 +299,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 +342,104 @@ 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);
- 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);
+ DCHECK_NE(state_, kFlushed);
+ CHECK(pending_read_);
- 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));
-
- // 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();
+ pending_read_ = false;
+ 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;
- 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;
+ // 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();
- // 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);
- }
- }
- } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) {
- OnFlushDone_Locked();
+ 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 (new_frame_available) {
- base::AutoUnlock auto_unlock(lock_);
- OnFrameAvailable();
- }
-}
+ // If we're at capacity or end of stream while seeking we need to transition
+ // to prerolled.
+ if (state_ == kSeeking) {
+ 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();
+ }
-void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) {
- // We should never return empty frames or EOS frame.
- DCHECK(frame && !frame->IsEndOfStream());
+ // ...and we're done seeking!
+ DCHECK(!seek_cb_.is_null());
+ ResetAndRunCB(&seek_cb_, PIPELINE_OK);
- decoder_->ProduceVideoFrame(frame);
- ++pending_reads_;
+ base::AutoUnlock ul(lock_);
+ OnFrameAvailable();
+ }
}
-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(read_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_) {
+ state_ = kFlushed;
+ current_frame_ = NULL;
ResetAndRunCB(&flush_callback_);
-
- state_ = kFlushed;
+ }
}
base::TimeDelta VideoRendererBase::CalculateSleepDuration(
@@ -550,59 +471,12 @@ base::TimeDelta VideoRendererBase::CalculateSleepDuration(
static_cast<int64>(sleep.InMicroseconds() / playback_rate));
}
-void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) {
- lock_.AssertAcquired();
-
- base::Closure callback;
- State old_state = state_;
- state_ = kError;
-
- // 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();
-
- switch (old_state) {
- case kUninitialized:
- case kPrerolled:
- case kPaused:
- case kFlushed:
- case kEnded:
- case kPlaying:
- break;
-
- case kFlushing:
- CHECK(!flush_callback_.is_null());
- std::swap(callback, flush_callback_);
- break;
-
- case kSeeking:
- CHECK(!seek_cb_.is_null());
- ResetAndRunCB(&seek_cb_, status);
- return;
- break;
-
- case kStopped:
- NOTREACHED() << "Should not error if stopped.";
- return;
-
- case kError:
- return;
- }
-
- host()->SetError(status);
-
- if (!callback.is_null())
- 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_);
}
} // 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