diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-28 03:12:46 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-28 03:12:46 +0000 |
commit | 8d1e8fb039043ccbf80e6cc3d81d814ebb10d799 (patch) | |
tree | b1f99c6b0960ee7706eefd24772d0cf4b96c05e0 /media | |
parent | 237a14858315c4ba7e9d72ad532df50a07e1a1d1 (diff) | |
download | chromium_src-8d1e8fb039043ccbf80e6cc3d81d814ebb10d799.zip chromium_src-8d1e8fb039043ccbf80e6cc3d81d814ebb10d799.tar.gz chromium_src-8d1e8fb039043ccbf80e6cc3d81d814ebb10d799.tar.bz2 |
Report VideoDecoder status through ReadCB instead of through FilterHost.
BUG=108340
TEST=media_unittests
Review URL: http://codereview.chromium.org/10248002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134435 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/filters.cc | 7 | ||||
-rw-r--r-- | media/base/filters.h | 27 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 14 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder_unittest.cc | 103 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.cc | 23 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.h | 3 | ||||
-rw-r--r-- | media/filters/video_frame_generator.cc | 2 | ||||
-rw-r--r-- | media/filters/video_renderer_base.cc | 21 | ||||
-rw-r--r-- | media/filters/video_renderer_base.h | 6 | ||||
-rw-r--r-- | media/filters/video_renderer_base_unittest.cc | 37 |
10 files changed, 168 insertions, 75 deletions
diff --git a/media/base/filters.cc b/media/base/filters.cc index 910f1e7..5682be3 100644 --- a/media/base/filters.cc +++ b/media/base/filters.cc @@ -61,6 +61,8 @@ VideoDecoder::VideoDecoder() {} VideoDecoder::~VideoDecoder() {} +// TODO(xhwang): Remove the following four functions when VideoDecoder is not a +// Filter any more. See bug: http://crbug.com/108340 void VideoDecoder::Play(const base::Closure& /* callback */) { LOG(FATAL) << "VideoDecoder::Play is not supposed to be called."; } @@ -74,6 +76,11 @@ void VideoDecoder::Seek(base::TimeDelta /* time */, LOG(FATAL) << "VideoDecoder::Seek is not supposed to be called."; } +FilterHost* VideoDecoder::host() { + LOG(FATAL) << "VideoDecoder::host is not supposed to be called."; + return NULL; +} + bool VideoDecoder::HasAlpha() const { return false; } diff --git a/media/base/filters.h b/media/base/filters.h index b32234f..0126995 100644 --- a/media/base/filters.h +++ b/media/base/filters.h @@ -109,6 +109,13 @@ class MEDIA_EXPORT Filter : public base::RefCountedThreadSafe<Filter> { class MEDIA_EXPORT VideoDecoder : public Filter { public: + // Status codes for read operations on VideoDecoder. + enum DecoderStatus { + kOk, // Everything went as planned. + kDecodeError, // Decoding error happened. + kDecryptError // Decrypting error happened. + }; + // Initialize a VideoDecoder with the given DemuxerStream, executing the // callback upon completion. // statistics_cb is used to update global pipeline statistics. @@ -116,16 +123,21 @@ class MEDIA_EXPORT VideoDecoder : public Filter { const PipelineStatusCB& status_cb, const StatisticsCB& statistics_cb) = 0; - // Request a frame to be decoded and returned via the provided callback. - // Only one read may be in flight at any given time. + // Requests a frame to be decoded. The status of the decoder and decoded frame + // are returned via the provided callback. Only one read may be in flight at + // any given time. // // Implementations guarantee that the callback will not be called from within // this method. // - // Non-NULL frames contain decoded video data or may indicate the end of - // the stream. NULL video frames indicate an aborted read. This can happen if - // the DemuxerStream gets flushed and doesn't have any more data to return. - typedef base::Callback<void(scoped_refptr<VideoFrame>)> ReadCB; + // If the returned status is not kOk, some error has occurred in the video + // decoder. In this case, the returned frame should always be NULL. + // + // Otherwise, the video decoder is in good shape. In this case, Non-NULL + // frames contain decoded video data or may indicate the end of the stream. + // NULL video frames indicate an aborted read. This can happen if the + // DemuxerStream gets flushed and doesn't have any more data to return. + typedef base::Callback<void(DecoderStatus, scoped_refptr<VideoFrame>)> ReadCB; virtual void Read(const ReadCB& read_cb) = 0; // Returns the natural width and height of decoded video in pixels. @@ -158,11 +170,12 @@ class MEDIA_EXPORT VideoDecoder : public Filter { // These functions will be removed later. Declare here to make sure they are // not called from VideoDecoder interface anymore. // TODO(xhwang): Remove them when VideoDecoder is not a Filter any more. - // See bug: crbug.com/108340 + // See bug: http://crbug.com/108340 virtual void Play(const base::Closure& callback) OVERRIDE; virtual void Pause(const base::Closure& callback) OVERRIDE; virtual void Seek(base::TimeDelta time, const PipelineStatusCB& callback) OVERRIDE; + virtual FilterHost* host() OVERRIDE; }; class MEDIA_EXPORT VideoRenderer : public Filter { diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index 93dfbfb..e61affb 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -5,11 +5,11 @@ #include "media/filters/ffmpeg_video_decoder.h" #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/message_loop.h" #include "base/string_number_conversions.h" #include "media/base/demuxer_stream.h" -#include "media/base/filter_host.h" #include "media/base/limits.h" #include "media/base/media_switches.h" #include "media/base/pipeline.h" @@ -212,7 +212,7 @@ void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { // Return empty frames if decoding has finished. if (state_ == kDecodeFinished) { - read_cb.Run(VideoFrame::CreateEmptyFrame()); + read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); return; } @@ -288,8 +288,7 @@ void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) { unencrypted_buffer = decryptor_.Decrypt(buffer); if (!unencrypted_buffer || !unencrypted_buffer->GetDataSize()) { state_ = kDecodeFinished; - DeliverFrame(VideoFrame::CreateEmptyFrame()); - host()->SetError(PIPELINE_ERROR_DECRYPT); + base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); return; } } @@ -297,8 +296,7 @@ void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) { scoped_refptr<VideoFrame> video_frame; if (!Decode(unencrypted_buffer, &video_frame)) { state_ = kDecodeFinished; - DeliverFrame(VideoFrame::CreateEmptyFrame()); - host()->SetError(PIPELINE_ERROR_DECODE); + base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); return; } @@ -423,9 +421,7 @@ bool FFmpegVideoDecoder::Decode( 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); + base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); } void FFmpegVideoDecoder::ReleaseFFmpegResources() { diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index b111ac3..cb674d5 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -13,7 +13,6 @@ #include "media/base/filters.h" #include "media/base/limits.h" #include "media/base/mock_callback.h" -#include "media/base/mock_filter_host.h" #include "media/base/mock_filters.h" #include "media/base/test_data_util.h" #include "media/base/video_frame.h" @@ -56,8 +55,6 @@ class FFmpegVideoDecoderTest : public testing::Test { base::Unretained(this))) { CHECK(FFmpegGlue::GetInstance()); - decoder_->set_host(&host_); - // Initialize various test buffers. frame_buffer_.reset(new uint8[kCodedSize.GetArea()]); end_of_stream_buffer_ = new DataBuffer(0); @@ -119,9 +116,11 @@ class FFmpegVideoDecoderTest : public testing::Test { // Sets up expectations and actions to put FFmpegVideoDecoder in an active // decoding state. void EnterDecodingState() { + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - DecodeSingleFrame(i_frame_buffer_, &video_frame); + DecodeSingleFrame(i_frame_buffer_, &status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kOk); ASSERT_TRUE(video_frame); EXPECT_FALSE(video_frame->IsEndOfStream()); } @@ -130,7 +129,9 @@ class FFmpegVideoDecoderTest : public testing::Test { // of stream state. void EnterEndOfStreamState() { scoped_refptr<VideoFrame> video_frame; - Read(&video_frame); + VideoDecoder::DecoderStatus status; + Read(&status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kOk); ASSERT_TRUE(video_frame); EXPECT_TRUE(video_frame->IsEndOfStream()); } @@ -140,14 +141,15 @@ class FFmpegVideoDecoderTest : public testing::Test { // and multithreaded decoders. End of stream buffers are used to trigger // the frame to be returned in the multithreaded decoder case. void DecodeSingleFrame(const scoped_refptr<Buffer>& buffer, - scoped_refptr<VideoFrame>* video_frame) { + VideoDecoder::DecoderStatus* status, + scoped_refptr<VideoFrame>* video_frame) { EXPECT_CALL(*demuxer_, Read(_)) .WillOnce(ReturnBuffer(buffer)) .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_)); EXPECT_CALL(statistics_cb_, OnStatistics(_)); - Read(video_frame); + Read(status, video_frame); } // Decodes |i_frame_buffer_| and then decodes the data contained in @@ -158,6 +160,8 @@ class FFmpegVideoDecoderTest : public testing::Test { size_t expected_height) { Initialize(); + VideoDecoder::DecoderStatus status_a; + VideoDecoder::DecoderStatus status_b; scoped_refptr<VideoFrame> video_frame_a; scoped_refptr<VideoFrame> video_frame_b; @@ -172,12 +176,14 @@ class FFmpegVideoDecoderTest : public testing::Test { EXPECT_CALL(statistics_cb_, OnStatistics(_)) .Times(2); - Read(&video_frame_a); - Read(&video_frame_b); + Read(&status_a, &video_frame_a); + Read(&status_b, &video_frame_b); size_t original_width = static_cast<size_t>(kVisibleRect.width()); size_t original_height = static_cast<size_t>(kVisibleRect.height()); + EXPECT_EQ(status_a, VideoDecoder::kOk); + EXPECT_EQ(status_b, VideoDecoder::kOk); ASSERT_TRUE(video_frame_a); ASSERT_TRUE(video_frame_b); EXPECT_EQ(original_width, video_frame_a->width()); @@ -186,22 +192,23 @@ class FFmpegVideoDecoderTest : public testing::Test { EXPECT_EQ(expected_height, video_frame_b->height()); } - void Read(scoped_refptr<VideoFrame>* video_frame) { - EXPECT_CALL(*this, FrameReady(_)) - .WillOnce(SaveArg<0>(video_frame)); + void Read(VideoDecoder::DecoderStatus* status, + scoped_refptr<VideoFrame>* video_frame) { + EXPECT_CALL(*this, FrameReady(_, _)) + .WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame))); decoder_->Read(read_cb_); message_loop_.RunAllPending(); } - MOCK_METHOD1(FrameReady, void(scoped_refptr<VideoFrame>)); + MOCK_METHOD2(FrameReady, void(VideoDecoder::DecoderStatus, + scoped_refptr<VideoFrame>)); MessageLoop message_loop_; scoped_refptr<FFmpegVideoDecoder> decoder_; scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_; MockStatisticsCB statistics_cb_; - StrictMock<MockFilterHost> host_; VideoDecoderConfig config_; VideoDecoder::ReadCB read_cb_; @@ -261,9 +268,11 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) { Initialize(); // Simulate decoding a single frame. + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - DecodeSingleFrame(i_frame_buffer_, &video_frame); + DecodeSingleFrame(i_frame_buffer_, &status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kOk); ASSERT_TRUE(video_frame); EXPECT_FALSE(video_frame->IsEndOfStream()); } @@ -275,6 +284,9 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) { scoped_refptr<DataBuffer> zero_byte_buffer = new DataBuffer(1); + VideoDecoder::DecoderStatus status_a; + VideoDecoder::DecoderStatus status_b; + VideoDecoder::DecoderStatus status_c; scoped_refptr<VideoFrame> video_frame_a; scoped_refptr<VideoFrame> video_frame_b; scoped_refptr<VideoFrame> video_frame_c; @@ -288,9 +300,13 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) { EXPECT_CALL(statistics_cb_, OnStatistics(_)) .Times(2); - Read(&video_frame_a); - Read(&video_frame_b); - Read(&video_frame_c); + Read(&status_a, &video_frame_a); + Read(&status_b, &video_frame_b); + Read(&status_c, &video_frame_c); + + EXPECT_EQ(status_a, VideoDecoder::kOk); + EXPECT_EQ(status_b, VideoDecoder::kOk); + EXPECT_EQ(status_c, VideoDecoder::kOk); ASSERT_TRUE(video_frame_a); ASSERT_TRUE(video_frame_b); @@ -312,14 +328,14 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) { // least one successful decode but we don't expect FrameReady() to be // executed as an error is raised instead. EXPECT_CALL(statistics_cb_, OnStatistics(_)); - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE)); // Our read should still get satisfied with end of stream frame during an // error. + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - Read(&video_frame); - ASSERT_TRUE(video_frame); - EXPECT_TRUE(video_frame->IsEndOfStream()); + Read(&status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kDecodeError); + EXPECT_FALSE(video_frame); message_loop_.RunAllPending(); } @@ -332,9 +348,11 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) { TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) { Initialize(); + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - DecodeSingleFrame(corrupt_i_frame_buffer_, &video_frame); + DecodeSingleFrame(corrupt_i_frame_buffer_, &status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kOk); ASSERT_TRUE(video_frame); EXPECT_TRUE(video_frame->IsEndOfStream()); } @@ -371,9 +389,11 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_Normal) { // Simulate decoding a single encrypted frame. encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>( new DecryptConfig(kKeyId, arraysize(kKeyId) - 1))); + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - DecodeSingleFrame(encrypted_i_frame_buffer_, &video_frame); + DecodeSingleFrame(encrypted_i_frame_buffer_, &status, &video_frame); + EXPECT_EQ(status, VideoDecoder::kOk); ASSERT_TRUE(video_frame); EXPECT_FALSE(video_frame->IsEndOfStream()); } @@ -388,14 +408,14 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_NoKey) { EXPECT_CALL(*demuxer_, Read(_)) .WillRepeatedly(ReturnBuffer(encrypted_i_frame_buffer_)); - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECRYPT)); // Our read should still get satisfied with end of stream frame during an // error. + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - Read(&video_frame); - ASSERT_TRUE(video_frame); - EXPECT_TRUE(video_frame->IsEndOfStream()); + Read(&status, &video_frame); + EXPECT_EQ(VideoDecoder::kDecryptError, status); + EXPECT_FALSE(video_frame); message_loop_.RunAllPending(); } @@ -416,17 +436,19 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_WrongKey) { // attempts to decode the content, however we're unable to distinguish between // the two (see http://crbug.com/124434). EXPECT_CALL(statistics_cb_, OnStatistics(_)); - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE)); -#else - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECRYPT)); #endif // Our read should still get satisfied with end of stream frame during an // error. + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - Read(&video_frame); - ASSERT_TRUE(video_frame); - EXPECT_TRUE(video_frame->IsEndOfStream()); + Read(&status, &video_frame); +#if defined(OS_LINUX) + EXPECT_EQ(VideoDecoder::kDecodeError, status); +#else + EXPECT_EQ(VideoDecoder::kDecryptError, status); +#endif + EXPECT_FALSE(video_frame); message_loop_.RunAllPending(); } @@ -491,7 +513,8 @@ TEST_F(FFmpegVideoDecoderTest, Flush_DuringPendingRead) { // Flush the decoder. Flush(); - EXPECT_CALL(*this, FrameReady(scoped_refptr<VideoFrame>())); + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, + scoped_refptr<VideoFrame>())); read_cb.Run(i_frame_buffer_); message_loop_.RunAllPending(); @@ -548,8 +571,12 @@ TEST_F(FFmpegVideoDecoderTest, AbortPendingRead) { EXPECT_CALL(*demuxer_, Read(_)) .WillOnce(ReturnBuffer(scoped_refptr<Buffer>())); + VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; - Read(&video_frame); + + Read(&status, &video_frame); + + EXPECT_EQ(status, VideoDecoder::kOk); EXPECT_FALSE(video_frame); } @@ -576,8 +603,8 @@ TEST_F(FFmpegVideoDecoderTest, AbortPendingReadDuringFlush) { // Make sure we get a NULL video frame returned. scoped_refptr<VideoFrame> video_frame; - EXPECT_CALL(*this, FrameReady(_)) - .WillOnce(SaveArg<0>(&video_frame)); + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, _)) + .WillOnce(SaveArg<1>(&video_frame)); message_loop_.RunAllPending(); EXPECT_FALSE(video_frame); diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index 7ce3df7..547d361 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc @@ -53,7 +53,8 @@ GpuVideoDecoder::GpuVideoDecoder( decoder_texture_target_(0), next_picture_buffer_id_(0), next_bitstream_buffer_id_(0), - shutting_down_(false) { + shutting_down_(false), + error_occured_(false) { DCHECK(gvd_loop_proxy_ && factories_); } @@ -192,8 +193,13 @@ void GpuVideoDecoder::Read(const ReadCB& read_cb) { return; } + if (error_occured_) { + read_cb.Run(kDecodeError, NULL); + return; + } + if (!vda_) { - read_cb.Run(VideoFrame::CreateEmptyFrame()); + read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); return; } @@ -233,7 +239,7 @@ void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { return; gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - pending_read_cb_, scoped_refptr<VideoFrame>())); + pending_read_cb_, kOk, scoped_refptr<VideoFrame>())); pending_read_cb_.Reset(); return; } @@ -415,7 +421,7 @@ void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( return; gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( - pending_read_cb_, ready_video_frames_.front())); + pending_read_cb_, kOk, ready_video_frames_.front())); pending_read_cb_.Reset(); ready_video_frames_.pop_front(); } @@ -536,8 +542,13 @@ void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { return; vda_ = NULL; DLOG(ERROR) << "VDA Error: " << error; - if (host()) - host()->SetError(PIPELINE_ERROR_DECODE); + + error_occured_ = true; + + if (!pending_read_cb_.is_null()) { + base::ResetAndReturn(&pending_read_cb_).Run(kDecodeError, NULL); + return; + } } } // namespace media diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h index c578f5a..7e48980 100644 --- a/media/filters/gpu_video_decoder.h +++ b/media/filters/gpu_video_decoder.h @@ -208,6 +208,9 @@ class MEDIA_EXPORT GpuVideoDecoder // this class not require the render thread's loop to be processing. bool shutting_down_; + // Indicates decoding error occurred. + bool error_occured_; + DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoder); }; diff --git a/media/filters/video_frame_generator.cc b/media/filters/video_frame_generator.cc index 93dbd80..3141399 100644 --- a/media/filters/video_frame_generator.cc +++ b/media/filters/video_frame_generator.cc @@ -80,7 +80,7 @@ void VideoFrameGenerator::ReadOnDecoderThread(const ReadCB& read_cb) { // TODO(wjia): set pixel data to pre-defined patterns if it's desired to // verify frame content. - read_cb.Run(video_frame); + read_cb.Run(kOk, video_frame); } void VideoFrameGenerator::StopOnDecoderThread(const base::Closure& callback) { diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc index 51c51b0..83d5294 100644 --- a/media/filters/video_renderer_base.cc +++ b/media/filters/video_renderer_base.cc @@ -359,13 +359,28 @@ void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { } } -void VideoRendererBase::FrameReady(scoped_refptr<VideoFrame> frame) { +void VideoRendererBase::FrameReady(VideoDecoder::DecoderStatus status, + scoped_refptr<VideoFrame> frame) { base::AutoLock auto_lock(lock_); DCHECK_NE(state_, kUninitialized); CHECK(pending_read_); pending_read_ = false; + if (status == VideoDecoder::kDecodeError) { + DCHECK(!frame); + host()->SetError(PIPELINE_ERROR_DECODE); + return; + } + + if (status == VideoDecoder::kDecryptError) { + DCHECK(!frame); + host()->SetError(PIPELINE_ERROR_DECRYPT); + return; + } + + DCHECK_EQ(status, VideoDecoder::kOk); + // Already-queued Decoder ReadCB's can fire after various state transitions // have happened; in that case just drop those frames immediately. if (state_ == kStopped || state_ == kError || state_ == kFlushed || @@ -460,8 +475,8 @@ void VideoRendererBase::AttemptRead_Locked() { (!ready_frames_.empty() && ready_frames_.back()->IsEndOfStream()) || state_ == kFlushingDecoder || state_ == kFlushing) { - return; - } + return; + } pending_read_ = true; decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this)); diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h index 2746ccb..c710e1c 100644 --- a/media/filters/video_renderer_base.h +++ b/media/filters/video_renderer_base.h @@ -74,8 +74,10 @@ class MEDIA_EXPORT VideoRendererBase void PutCurrentFrame(scoped_refptr<VideoFrame> frame); private: - // Callback from the video decoder delivering decoded video frames. - void FrameReady(scoped_refptr<VideoFrame> frame); + // Callback from the video decoder delivering decoded video frames and + // reporting video decoder status. + void FrameReady(VideoDecoder::DecoderStatus status, + scoped_refptr<VideoFrame> frame); // Helper method that schedules an asynchronous read from the decoder as long // as there isn't a pending read and we have capacity. diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc index b7be329..e7c3f0c 100644 --- a/media/filters/video_renderer_base_unittest.cc +++ b/media/filters/video_renderer_base_unittest.cc @@ -131,7 +131,7 @@ class VideoRendererBaseTest : public ::testing::Test { VideoDecoder::ReadCB read_cb(queued_read_cb_); queued_read_cb_.Reset(); base::AutoUnlock u(lock_); - read_cb.Run(VideoFrame::CreateEmptyFrame()); + read_cb.Run(VideoDecoder::kOk, VideoFrame::CreateEmptyFrame()); } void StartSeeking(int64 timestamp) { @@ -192,27 +192,36 @@ class VideoRendererBaseTest : public ::testing::Test { VideoDecoder::ReadCB read_cb; { base::AutoLock l(lock_); - CHECK(!read_cb_.is_null()) << "Can't deliver a frame without a callback"; std::swap(read_cb, read_cb_); } if (timestamp == kEndOfStream) { - read_cb.Run(VideoFrame::CreateEmptyFrame()); + read_cb.Run(VideoDecoder::kOk, VideoFrame::CreateEmptyFrame()); } else { - read_cb.Run(CreateFrame(timestamp, kFrameDuration)); + read_cb.Run(VideoDecoder::kOk, CreateFrame(timestamp, kFrameDuration)); } } + void DecoderError() { + // Lock+swap to avoid re-entrancy issues. + VideoDecoder::ReadCB read_cb; + { + base::AutoLock l(lock_); + std::swap(read_cb, read_cb_); + } + + read_cb.Run(VideoDecoder::kDecodeError, NULL); + } + void AbortRead() { // Lock+swap to avoid re-entrancy issues. VideoDecoder::ReadCB read_cb; { base::AutoLock l(lock_); - CHECK(!read_cb_.is_null()) << "Can't deliver a frame without a callback"; std::swap(read_cb, read_cb_); } - read_cb.Run(NULL); + read_cb.Run(VideoDecoder::kOk, NULL); } void ExpectCurrentFrame(bool present) { @@ -335,7 +344,7 @@ class VideoRendererBaseTest : public ::testing::Test { // Abort pending read. if (!read_cb.is_null()) - read_cb.Run(NULL); + read_cb.Run(VideoDecoder::kOk, NULL); callback.Run(); } @@ -364,9 +373,10 @@ class VideoRendererBaseTest : public ::testing::Test { // Unlock to deliver the frame to avoid re-entrancy issues. base::AutoUnlock ul(lock_); if (timestamp == kEndOfStream) { - read_cb.Run(VideoFrame::CreateEmptyFrame()); + read_cb.Run(VideoDecoder::kOk, VideoFrame::CreateEmptyFrame()); } else { - read_cb.Run(CreateFrame(i * kFrameDuration, kFrameDuration)); + read_cb.Run(VideoDecoder::kOk, + CreateFrame(i * kFrameDuration, kFrameDuration)); i++; } } else { @@ -444,6 +454,15 @@ TEST_F(VideoRendererBaseTest, EndOfStream) { Shutdown(); } +TEST_F(VideoRendererBaseTest, DecoderError) { + Initialize(); + Play(); + RenderFrame(kFrameDuration); + EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE)); + DecoderError(); + Shutdown(); +} + TEST_F(VideoRendererBaseTest, Seek_Exact) { Initialize(); Pause(); |