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

Unified Diff: media/filters/ffmpeg_video_decoder.cc

Issue 8417019: Simplify VideoDecodeEngine interface by making everything synchronous. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: recycling: vanquished Created 9 years, 2 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
Index: media/filters/ffmpeg_video_decoder.cc
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index deb0bf375df950b221ca36766ca7ef817fbfd4a3..a54f95177b82d3dec554d1aa1e3002d20ccb0cd5 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -5,12 +5,11 @@
#include "media/filters/ffmpeg_video_decoder.h"
#include "base/bind.h"
-#include "base/callback.h"
#include "base/message_loop.h"
-#include "base/task.h"
#include "media/base/demuxer_stream.h"
#include "media/base/filter_host.h"
#include "media/base/limits.h"
+#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/video/ffmpeg_video_decode_engine.h"
@@ -19,7 +18,7 @@ namespace media {
FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop)
: message_loop_(message_loop),
- state_(kUnInitialized),
+ state_(kUninitialized),
decode_engine_(new FFmpegVideoDecodeEngine()) {
}
@@ -29,17 +28,14 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
const base::Closure& callback,
const StatisticsCallback& stats_callback) {
if (MessageLoop::current() != message_loop_) {
- message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&FFmpegVideoDecoder::Initialize, this,
- make_scoped_refptr(demuxer_stream),
- callback, stats_callback));
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &FFmpegVideoDecoder::Initialize, this,
+ make_scoped_refptr(demuxer_stream), callback, stats_callback));
return;
}
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK(!demuxer_stream_);
- DCHECK(initialize_callback_.is_null());
if (!demuxer_stream) {
host()->SetError(PIPELINE_ERROR_DECODE);
@@ -48,7 +44,6 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
}
demuxer_stream_ = demuxer_stream;
- initialize_callback_ = callback;
statistics_callback_ = stats_callback;
const VideoDecoderConfig& config = demuxer_stream->video_decoder_config();
@@ -59,24 +54,19 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
if (natural_size_.width() > Limits::kMaxDimension ||
natural_size_.height() > Limits::kMaxDimension ||
natural_size_.GetArea() > Limits::kMaxCanvas) {
- OnInitializeComplete(false);
+ host()->SetError(PIPELINE_ERROR_DECODE);
+ callback.Run();
return;
}
- state_ = kInitializing;
- decode_engine_->Initialize(this, config);
-}
-
-void FFmpegVideoDecoder::OnInitializeComplete(bool success) {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(!initialize_callback_.is_null());
-
- if (success) {
- state_ = kNormal;
- } else {
+ if (!decode_engine_->Initialize(config)) {
host()->SetError(PIPELINE_ERROR_DECODE);
+ callback.Run();
+ return;
}
- ResetAndRunCB(&initialize_callback_);
+
+ state_ = kNormal;
+ callback.Run();
}
void FFmpegVideoDecoder::Stop(const base::Closure& callback) {
@@ -87,22 +77,23 @@ void FFmpegVideoDecoder::Stop(const base::Closure& callback) {
}
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(uninitialize_callback_.is_null());
- uninitialize_callback_ = callback;
- if (state_ != kUnInitialized)
- decode_engine_->Uninitialize();
- else
- OnUninitializeComplete();
+ decode_engine_->Uninitialize();
+ state_ = kUninitialized;
+ callback.Run();
}
-void FFmpegVideoDecoder::OnUninitializeComplete() {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(!uninitialize_callback_.is_null());
+void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &FFmpegVideoDecoder::Seek, this, time, cb));
+ return;
+ }
- state_ = kStopped;
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
- ResetAndRunCB(&uninitialize_callback_);
+ pts_stream_.Seek(time);
+ cb.Run(PIPELINE_OK);
}
void FFmpegVideoDecoder::Pause(const base::Closure& callback) {
@@ -112,7 +103,6 @@ void FFmpegVideoDecoder::Pause(const base::Closure& callback) {
return;
}
- state_ = kPausing;
callback.Run();
}
@@ -124,66 +114,18 @@ void FFmpegVideoDecoder::Flush(const base::Closure& callback) {
}
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(flush_callback_.is_null());
-
- state_ = kFlushing;
-
- FlushBuffers();
-
- flush_callback_ = callback;
decode_engine_->Flush();
-}
-
-void FFmpegVideoDecoder::OnFlushComplete() {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(!flush_callback_.is_null());
-
- // Everything in the presentation time queue is invalid, clear the queue.
pts_stream_.Flush();
-
- // Mark flush operation had been done.
state_ = kNormal;
-
- ResetAndRunCB(&flush_callback_);
-}
-
-void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
- if (MessageLoop::current() != message_loop_) {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&FFmpegVideoDecoder::Seek, this,
- time, cb));
- return;
- }
-
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(seek_cb_.is_null());
-
- pts_stream_.Seek(time);
- seek_cb_ = cb;
- decode_engine_->Seek();
-}
-
-void FFmpegVideoDecoder::OnSeekComplete() {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(!seek_cb_.is_null());
-
- ResetAndRunCB(&seek_cb_, PIPELINE_OK);
-}
-
-void FFmpegVideoDecoder::OnError() {
- VideoFrameReady(NULL);
-}
-
-void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) {
- scoped_refptr<Buffer> buffer(buffer_in);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer));
+ callback.Run();
}
-void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) {
+void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) {
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 Why have this be a separate function? It makes De
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK_NE(state_, kStopped); // because of Flush() before Stop().
+ DCHECK_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kDecodeFinished);
+ DCHECK(!frame_ready_cb_.is_null());
// During decode, because reads are issued asynchronously, it is possible to
// receive multiple end of stream buffers since each read is acked. When the
@@ -208,7 +150,7 @@ void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) {
// kFlushCodec -> kDecodeFinished:
// When avcodec_decode_video2() returns 0 data or errors out.
// (any state) -> kNormal:
- // Any time buffer->IsDiscontinuous() is true.
+ // Any time Flush() is called.
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 I'm surprised a Flush() can recover from a "catast
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
// Transition to kFlushCodec on the first end of stream buffer.
if (state_ == kNormal && buffer->IsEndOfStream()) {
@@ -222,99 +164,95 @@ void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) {
pts_stream_.EnqueuePts(buffer.get());
}
- // Otherwise, attempt to decode a single frame.
- decode_engine_->ConsumeVideoSample(buffer);
-}
-
-void FFmpegVideoDecoder::ProduceVideoFrame(
- scoped_refptr<VideoFrame> video_frame) {
- if (MessageLoop::current() != message_loop_) {
- if (state_ != kStopped) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegVideoDecoder::ProduceVideoFrame, this, video_frame));
- }
+ scoped_refptr<VideoFrame> video_frame;
+ if (!decode_engine_->Decode(buffer, &video_frame)) {
+ state_ = kDecodeFinished;
+ host()->SetError(PIPELINE_ERROR_DECODE);
return;
}
- DCHECK_EQ(MessageLoop::current(), message_loop_);
-
- // Synchronized flushing before stop should prevent this.
- 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.
- VideoFrameReady(VideoFrame::CreateEmptyFrame());
-
- // Fall through, because we still need to keep record of this frame.
+ // Any successful decode counts!
+ if (buffer->GetDataSize()) {
+ PipelineStatistics statistics;
+ statistics.video_bytes_decoded = buffer->GetDataSize();
+ statistics_callback_.Run(statistics);
}
- // Notify decode engine the available of new frame.
- decode_engine_->ProduceVideoFrame(video_frame);
-}
-
-void FFmpegVideoDecoder::ConsumeVideoFrame(
- scoped_refptr<VideoFrame> video_frame,
- const PipelineStatistics& statistics) {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK_NE(state_, kStopped);
-
- statistics_callback_.Run(statistics);
-
- if (video_frame.get()) {
- if (kPausing == state_ || kFlushing == state_) {
- frame_queue_flushed_.push_back(video_frame);
- if (kFlushing == state_)
- FlushBuffers();
+ // If we didn't get a frame then we've either completely finished decoding or
+ // we need more data.
+ if (!video_frame) {
+ if (state_ == kFlushCodec) {
+ state_ = kDecodeFinished;
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
return;
}
- // If we actually got data back, enqueue a frame.
- pts_stream_.UpdatePtsAndDuration(video_frame.get());
+ ReadFromDemuxerStream();
+ return;
+ }
- video_frame->SetTimestamp(pts_stream_.current_pts());
- video_frame->SetDuration(pts_stream_.current_duration());
+ // If we got a frame make sure its timestamp is correct before sending it off.
+ pts_stream_.UpdatePtsAndDuration(video_frame.get());
+ video_frame->SetTimestamp(pts_stream_.current_pts());
+ video_frame->SetDuration(pts_stream_.current_duration());
- VideoFrameReady(video_frame);
- } else {
- // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
- // is taken as a signal to stop decoding.
- if (state_ == kFlushCodec) {
- state_ = kDecodeFinished;
+ DeliverFrame(video_frame);
+}
- // Signal VideoRenderer the end of the stream event.
- VideoFrameReady(VideoFrame::CreateEmptyFrame());
- }
+void FFmpegVideoDecoder::Read(const FrameReadyCB& callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &FFmpegVideoDecoder::Read, this, callback));
+ return;
}
-}
-void FFmpegVideoDecoder::ProduceVideoSample(
- scoped_refptr<Buffer> buffer) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK_NE(state_, kStopped);
+ CHECK(!callback.is_null());
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 Here and below s/CHECK/DCHECK/?
scherkus (not reviewing) 2011/11/03 04:55:59 maybe... I feel like this is programmer error and
+ CHECK(frame_ready_cb_.is_null()) << "Overlapping decodes are not supported.";
+
+ // This can happen during shutdown after Stop() has been called.
+ if (state_ == kUninitialized) {
+ // XXXXXXX
+ // just drop the callback? do we want to honour every callback no matter
+ // what?
scherkus (not reviewing) 2011/11/01 04:18:26 FYI
acolwell GONE FROM CHROMIUM 2011/11/01 18:31:03 Will the caller ever block progress if it doesn't
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 I say drop the callback.
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
+ return;
+ }
+
+ // Return empty frames if decoding has finished.
+ if (state_ == kDecodeFinished) {
+ callback.Run(VideoFrame::CreateEmptyFrame());
+ return;
+ }
- demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete,
- this));
+ frame_ready_cb_ = callback;
+ ReadFromDemuxerStream();
}
gfx::Size FFmpegVideoDecoder::natural_size() {
return natural_size_;
}
-void FFmpegVideoDecoder::FlushBuffers() {
- while (!frame_queue_flushed_.empty()) {
- scoped_refptr<VideoFrame> video_frame;
- video_frame = frame_queue_flushed_.front();
- frame_queue_flushed_.pop_front();
+void FFmpegVideoDecoder::ReadFromDemuxerStream() {
+ DCHECK_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kDecodeFinished);
+ DCHECK(!frame_ready_cb_.is_null());
- // Return frames back to the decode engine.
- decode_engine_->ProduceVideoFrame(video_frame);
- }
+ demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this));
+}
+
+void FFmpegVideoDecoder::DecodeBuffer(Buffer* buffer) {
+ // TODO(scherkus): change DemuxerStream::Read() to use scoped_refptr<> for
acolwell GONE FROM CHROMIUM 2011/11/01 18:31:03 This TODO should be in demuxer_stream.h
scherkus (not reviewing) 2011/11/03 04:55:59 TODO was fixed in r108178
+ // callback.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &FFmpegVideoDecoder::DoDecodeBuffer, this, make_scoped_refptr(buffer)));
}
-void FFmpegVideoDecoder::SetVideoDecodeEngineForTest(
- VideoDecodeEngine* engine) {
- decode_engine_.reset(engine);
+void FFmpegVideoDecoder::DeliverFrame(
+ const scoped_refptr<VideoFrame>& video_frame) {
+ // Reset the callback before running to protect against reentrancy.
+ FrameReadyCB frame_ready_cb = frame_ready_cb_;
+ frame_ready_cb_.Reset();
+ frame_ready_cb.Run(video_frame);
}
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698