Index: media/filters/ffmpeg_video_decoder.cc |
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc |
index 8b673dc868971d296b5f1e144953f024e60cb3fe..46e4036005e293e7f148ca63acdffa1f8a340552 100644 |
--- a/media/filters/ffmpeg_video_decoder.cc |
+++ b/media/filters/ffmpeg_video_decoder.cc |
@@ -26,9 +26,7 @@ FFmpegVideoDecoder::FFmpegVideoDecoder(VideoDecodeEngine* engine) |
height_(0), |
time_base_(new AVRational()), |
state_(kUnInitialized), |
- decode_engine_(engine), |
- pending_reads_(0), |
- pending_requests_(0) { |
+ decode_engine_(engine) { |
memset(&info_, 0, sizeof(info_)); |
} |
@@ -150,6 +148,19 @@ void FFmpegVideoDecoder::OnUninitializeComplete() { |
state_ = kStopped; |
} |
+void FFmpegVideoDecoder::Pause(FilterCallback* callback) { |
+ if (MessageLoop::current() != message_loop()) { |
+ message_loop()->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, |
+ &FFmpegVideoDecoder::Pause, |
+ callback)); |
+ return; |
+ } |
+ |
+ AutoCallbackRunner done_runner(callback); |
+ state_ = kPausing; |
+} |
+ |
void FFmpegVideoDecoder::Flush(FilterCallback* callback) { |
if (MessageLoop::current() != message_loop()) { |
message_loop()->PostTask(FROM_HERE, |
@@ -162,11 +173,11 @@ void FFmpegVideoDecoder::Flush(FilterCallback* callback) { |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
DCHECK(!flush_callback_.get()); |
- flush_callback_.reset(callback); |
+ state_ = kFlushing; |
- // Everything in the presentation time queue is invalid, clear the queue. |
- while (!pts_heap_.IsEmpty()) |
- pts_heap_.Pop(); |
+ FlushBuffers(); |
+ |
+ flush_callback_.reset(callback); |
decode_engine_->Flush(); |
} |
@@ -176,6 +187,13 @@ void FFmpegVideoDecoder::OnFlushComplete() { |
DCHECK(flush_callback_.get()); |
AutoCallbackRunner done_runner(flush_callback_.release()); |
+ |
+ // Everything in the presentation time queue is invalid, clear the queue. |
+ while (!pts_heap_.IsEmpty()) |
+ pts_heap_.Pop(); |
+ |
+ // Mark flush operation had been done. |
+ state_ = kNormal; |
} |
void FFmpegVideoDecoder::Seek(base::TimeDelta time, |
@@ -192,10 +210,6 @@ void FFmpegVideoDecoder::Seek(base::TimeDelta time, |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
DCHECK(!seek_callback_.get()); |
- // TODO(jiesun): when we move to parallel Flush, we should remove this. |
- DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; |
- DCHECK_EQ(0u, pending_requests_) << "Pending requests should be empty"; |
- |
seek_callback_.reset(callback); |
decode_engine_->Seek(); |
} |
@@ -205,7 +219,6 @@ void FFmpegVideoDecoder::OnSeekComplete() { |
DCHECK(seek_callback_.get()); |
AutoCallbackRunner done_runner(seek_callback_.release()); |
- state_ = kNormal; |
} |
void FFmpegVideoDecoder::OnError() { |
@@ -227,9 +240,7 @@ void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) { |
void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) { |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
- DCHECK_GT(pending_reads_, 0u); |
- |
- --pending_reads_; |
+ DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). |
// During decode, because reads are issued asynchronously, it is possible to |
// receive multiple end of stream buffers since each read is acked. When the |
@@ -255,18 +266,6 @@ void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) { |
// When avcodec_decode_video2() returns 0 data or errors out. |
// (any state) -> kNormal: |
// Any time buffer->IsDiscontinuous() is true. |
- // |
- // If the decoding is finished, we just always return empty frames. |
- if (state_ == kDecodeFinished || state_ == kStopped) { |
- DCHECK(buffer->IsEndOfStream()); |
- |
- --pending_requests_; |
- // Signal VideoRenderer the end of the stream event. |
- scoped_refptr<VideoFrame> video_frame; |
- VideoFrame::CreateEmptyFrame(&video_frame); |
- fill_buffer_done_callback()->Run(video_frame); |
- return; |
- } |
// Transition to kFlushCodec on the first end of stream buffer. |
if (state_ == kNormal && buffer->IsEndOfStream()) { |
@@ -302,23 +301,35 @@ void FFmpegVideoDecoder::FillThisBuffer( |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
// Synchronized flushing before stop should prevent this. |
- if (state_ == kStopped) |
- return; // Discard the video frame. |
+ DCHECK_NE(state_, kStopped); |
+ |
+ // If the decoding is finished, we just always return empty frames. |
+ if (state_ == kDecodeFinished) { |
+ // Signal VideoRenderer the end of the stream event. |
+ scoped_refptr<VideoFrame> empty_frame; |
+ VideoFrame::CreateEmptyFrame(&empty_frame); |
+ fill_buffer_done_callback()->Run(empty_frame); |
+ |
+ // Fall through, because we still need to keep record of this frame. |
+ } |
// Notify decode engine the available of new frame. |
- ++pending_requests_; |
decode_engine_->FillThisBuffer(video_frame); |
} |
void FFmpegVideoDecoder::OnFillBufferCallback( |
scoped_refptr<VideoFrame> video_frame) { |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
- |
- // TODO(jiesun): Flush before stop will prevent this from happening. |
- if (state_ == kStopped) |
- return; // Discard the video frame. |
+ DCHECK_NE(state_, kStopped); |
if (video_frame.get()) { |
+ if (kPausing == state_ || kFlushing == state_) { |
+ frame_queue_flushed_.push_back(video_frame); |
+ if (kFlushing == state_) |
+ FlushBuffers(); |
+ return; |
+ } |
+ |
// If we actually got data back, enqueue a frame. |
last_pts_ = FindPtsAndDuration(*time_base_, &pts_heap_, last_pts_, |
video_frame.get()); |
@@ -326,8 +337,6 @@ void FFmpegVideoDecoder::OnFillBufferCallback( |
video_frame->SetTimestamp(last_pts_.timestamp); |
video_frame->SetDuration(last_pts_.duration); |
- // Deliver this frame to VideoRenderer. |
- --pending_requests_; |
fill_buffer_done_callback()->Run(video_frame); |
} else { |
// When in kFlushCodec, any errored decode, or a 0-lengthed frame, |
@@ -335,7 +344,6 @@ void FFmpegVideoDecoder::OnFillBufferCallback( |
if (state_ == kFlushCodec) { |
state_ = kDecodeFinished; |
- --pending_requests_; |
// Signal VideoRenderer the end of the stream event. |
scoped_refptr<VideoFrame> video_frame; |
VideoFrame::CreateEmptyFrame(&video_frame); |
@@ -347,11 +355,10 @@ void FFmpegVideoDecoder::OnFillBufferCallback( |
void FFmpegVideoDecoder::OnEmptyBufferCallback( |
scoped_refptr<Buffer> buffer) { |
DCHECK_EQ(MessageLoop::current(), message_loop()); |
- DCHECK_LE(pending_reads_, pending_requests_); |
+ DCHECK_NE(state_, kStopped); |
demuxer_stream_->Read( |
NewCallback(this, &FFmpegVideoDecoder::OnReadComplete)); |
- ++pending_reads_; |
} |
FFmpegVideoDecoder::TimeTuple FFmpegVideoDecoder::FindPtsAndDuration( |
@@ -408,6 +415,21 @@ bool FFmpegVideoDecoder::ProvidesBuffer() { |
return info_.provides_buffers_; |
} |
+void FFmpegVideoDecoder::FlushBuffers() { |
+ while (!frame_queue_flushed_.empty()) { |
+ scoped_refptr<VideoFrame> video_frame; |
+ video_frame = frame_queue_flushed_.front(); |
+ frame_queue_flushed_.pop_front(); |
+ |
+ // Depends on who own the buffers, we either return it to the renderer |
+ // or return it to the decode engine. |
+ if (ProvidesBuffer()) |
+ decode_engine_->FillThisBuffer(video_frame); |
+ else |
+ fill_buffer_done_callback()->Run(video_frame); |
+ } |
+} |
+ |
void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( |
VideoDecodeEngine* engine) { |
decode_engine_ = engine; |