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

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: 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/ffmpeg_video_decoder.h ('k') | media/filters/ffmpeg_video_decoder_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/ffmpeg_video_decoder.cc
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index 9be1247a2ed6d98e8f7fb1ffd871d20a897a4b43..0254f6352149110c9c49ad388df92ce9587a69e4 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,13 @@ 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,35 +43,45 @@ 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();
- pts_stream_.Initialize(GetFrameDuration(config));
+ // TODO(scherkus): this check should go in PipelineImpl prior to creating
+ // decoder objects.
+ if (!config.IsValidConfig()) {
+ DLOG(ERROR) << "Invalid video stream -"
+ << " codec: " << config.codec()
+ << " format: " << config.format()
+ << " coded size: [" << config.coded_size().width()
+ << "," << config.coded_size().height() << "]"
+ << " visible rect: [" << config.visible_rect().x()
+ << "," << config.visible_rect().y()
+ << "," << config.visible_rect().width()
+ << "," << config.visible_rect().height() << "]"
+ << " natural size: [" << config.natural_size().width()
+ << "," << config.natural_size().height() << "]"
+ << " frame rate: " << config.frame_rate_numerator()
+ << "/" << config.frame_rate_denominator()
+ << " aspect ratio: " << config.aspect_ratio_numerator()
+ << "/" << config.aspect_ratio_denominator();
- natural_size_ = config.natural_size();
- 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());
+ pts_stream_.Initialize(GetFrameDuration(config));
+ natural_size_ = config.natural_size();
- 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) {
@@ -86,23 +91,20 @@ void FFmpegVideoDecoder::Stop(const base::Closure& callback) {
return;
}
- 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());
-
- state_ = kStopped;
+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;
+ }
- ResetAndRunCB(&uninitialize_callback_);
+ pts_stream_.Seek(time);
+ cb.Run(PIPELINE_OK);
}
void FFmpegVideoDecoder::Pause(const base::Closure& callback) {
@@ -112,7 +114,6 @@ void FFmpegVideoDecoder::Pause(const base::Closure& callback) {
return;
}
- state_ = kPausing;
callback.Run();
}
@@ -123,67 +124,64 @@ void FFmpegVideoDecoder::Flush(const base::Closure& callback) {
return;
}
- 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;
+ callback.Run();
+}
- ResetAndRunCB(&flush_callback_);
+void FFmpegVideoDecoder::Read(const ReadCB& callback) {
+ // TODO(scherkus): forced task post since VideoRendererBase::FrameReady() will
+ // call Read() on FFmpegVideoDecoder's thread as we executed |read_cb_|.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &FFmpegVideoDecoder::DoRead, this, 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;
- }
+const gfx::Size& FFmpegVideoDecoder::natural_size() {
+ return natural_size_;
+}
+void FFmpegVideoDecoder::DoRead(const ReadCB& callback) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(seek_cb_.is_null());
+ CHECK(!callback.is_null());
+ CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
- pts_stream_.Seek(time);
- seek_cb_ = cb;
- decode_engine_->Seek();
-}
+ // This can happen during shutdown after Stop() has been called.
+ if (state_ == kUninitialized) {
+ return;
+ }
-void FFmpegVideoDecoder::OnSeekComplete() {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK(!seek_cb_.is_null());
+ // Return empty frames if decoding has finished.
+ if (state_ == kDecodeFinished) {
+ callback.Run(VideoFrame::CreateEmptyFrame());
+ return;
+ }
- ResetAndRunCB(&seek_cb_, PIPELINE_OK);
+ read_cb_ = callback;
+ ReadFromDemuxerStream();
}
-void FFmpegVideoDecoder::OnError() {
- VideoFrameReady(NULL);
+
+void FFmpegVideoDecoder::ReadFromDemuxerStream() {
+ DCHECK_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kDecodeFinished);
+ DCHECK(!read_cb_.is_null());
+
+ demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this));
}
-void FFmpegVideoDecoder::OnReadComplete(const scoped_refptr<Buffer>& buffer) {
+void FFmpegVideoDecoder::DecodeBuffer(const scoped_refptr<Buffer>& buffer) {
+ // TODO(scherkus): forced task post since FFmpegDemuxerStream::Read() can
+ // immediately execute our callback on FFmpegVideoDecoder's thread.
message_loop_->PostTask(FROM_HERE, base::Bind(
- &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer));
+ &FFmpegVideoDecoder::DoDecodeBuffer, this, buffer));
}
-void FFmpegVideoDecoder::OnReadCompleteTask(
- const scoped_refptr<Buffer>& buffer) {
+void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK_NE(state_, kStopped); // because of Flush() before Stop().
+ DCHECK_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kDecodeFinished);
+ DCHECK(!read_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
@@ -204,11 +202,11 @@ void FFmpegVideoDecoder::OnReadCompleteTask(
// kNormal -> kFlushCodec:
// When buffer->IsEndOfStream() is first true.
// kNormal -> kDecodeFinished:
- // A catastrophic failure occurs, and decoding needs to stop.
+ // A decoding error occurs and decoding needs to stop.
// 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.
// Transition to kFlushCodec on the first end of stream buffer.
if (state_ == kNormal && buffer->IsEndOfStream()) {
@@ -222,98 +220,48 @@ void FFmpegVideoDecoder::OnReadCompleteTask(
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;
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
+ 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();
- return;
- }
-
- // If we actually got data back, enqueue a frame.
- 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 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;
-
- // Signal VideoRenderer the end of the stream event.
- VideoFrameReady(VideoFrame::CreateEmptyFrame());
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
+ return;
}
- }
-}
-
-void FFmpegVideoDecoder::ProduceVideoSample(
- scoped_refptr<Buffer> buffer) {
- DCHECK_EQ(MessageLoop::current(), message_loop_);
- DCHECK_NE(state_, kStopped);
-
- demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete, this));
-}
-gfx::Size FFmpegVideoDecoder::natural_size() {
- return natural_size_;
-}
+ ReadFromDemuxerStream();
+ return;
+ }
-void FFmpegVideoDecoder::FlushBuffers() {
- while (!frame_queue_flushed_.empty()) {
- scoped_refptr<VideoFrame> video_frame;
- video_frame = frame_queue_flushed_.front();
- frame_queue_flushed_.pop_front();
+ // 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());
- // Return frames back to the decode engine.
- decode_engine_->ProduceVideoFrame(video_frame);
- }
+ DeliverFrame(video_frame);
}
-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.
+ ReadCB read_cb = read_cb_;
+ read_cb_.Reset();
+ read_cb.Run(video_frame);
}
} // namespace media
« no previous file with comments | « media/filters/ffmpeg_video_decoder.h ('k') | media/filters/ffmpeg_video_decoder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698