summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-28 03:12:46 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-28 03:12:46 +0000
commit8d1e8fb039043ccbf80e6cc3d81d814ebb10d799 (patch)
treeb1f99c6b0960ee7706eefd24772d0cf4b96c05e0 /media
parent237a14858315c4ba7e9d72ad532df50a07e1a1d1 (diff)
downloadchromium_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.cc7
-rw-r--r--media/base/filters.h27
-rw-r--r--media/filters/ffmpeg_video_decoder.cc14
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc103
-rw-r--r--media/filters/gpu_video_decoder.cc23
-rw-r--r--media/filters/gpu_video_decoder.h3
-rw-r--r--media/filters/video_frame_generator.cc2
-rw-r--r--media/filters/video_renderer_base.cc21
-rw-r--r--media/filters/video_renderer_base.h6
-rw-r--r--media/filters/video_renderer_base_unittest.cc37
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();