diff options
author | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-19 01:03:53 +0000 |
---|---|---|
committer | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-19 01:03:53 +0000 |
commit | 4eb2ac96bb677b63173fab432bc66903a53f79b0 (patch) | |
tree | e69de4e7f4435cd37c6ff6f132d02e17d9e74253 /media | |
parent | ff122619c7259df74ccf792d7c3d9a1e433c4ce7 (diff) | |
download | chromium_src-4eb2ac96bb677b63173fab432bc66903a53f79b0.zip chromium_src-4eb2ac96bb677b63173fab432bc66903a53f79b0.tar.gz chromium_src-4eb2ac96bb677b63173fab432bc66903a53f79b0.tar.bz2 |
Revert 47603 - refactoring engine interface for openmax.
It was breaking several video tests:
http://build.chromium.org/buildbot/waterfall/builders/Webkit/builds/22419/steps/webkit_tests/logs/stdio
currently only fillbufferdone callback and emptythisbuffer is used.
Review URL: http://codereview.chromium.org/2095002
TBR=jiesun@google.com
Review URL: http://codereview.chromium.org/2070012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47608 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/decoder_base.h | 40 | ||||
-rw-r--r-- | media/filters/decoder_base_unittest.cc | 10 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.cc | 8 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.h | 2 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decode_engine.cc | 49 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decode_engine.h | 13 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decode_engine_unittest.cc | 77 | ||||
-rw-r--r-- | media/filters/omx_video_decode_engine.cc | 75 | ||||
-rw-r--r-- | media/filters/omx_video_decode_engine.h | 27 | ||||
-rw-r--r-- | media/filters/video_decode_engine.h | 39 | ||||
-rw-r--r-- | media/filters/video_decoder_impl.cc | 48 | ||||
-rw-r--r-- | media/filters/video_decoder_impl.h | 9 | ||||
-rw-r--r-- | media/filters/video_decoder_impl_unittest.cc | 91 |
13 files changed, 233 insertions, 255 deletions
diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h index 5642651..26b1d18 100644 --- a/media/filters/decoder_base.h +++ b/media/filters/decoder_base.h @@ -110,28 +110,10 @@ class DecoderBase : public Decoder { // Method that must be implemented by the derived class. If the decode // operation produces one or more outputs, the derived class should call // the EnequeueResult() method from within this method. - virtual void DoDecode(Buffer* input) = 0; + virtual void DoDecode(Buffer* input, Task* done_cb) = 0; MediaFormat media_format_; - void OnDecodeComplete() { - // Attempt to fulfill a pending read callback and schedule additional reads - // if necessary. - bool fulfilled = FulfillPendingRead(); - - // Issue reads as necessary. - // - // Note that it's possible for us to decode but not produce a frame, in - // which case |pending_reads_| will remain less than |read_queue_| so we - // need to schedule an additional read. - DCHECK_LE(pending_reads_, read_queue_.size()); - if (!fulfilled) { - DCHECK_LT(pending_reads_, read_queue_.size()); - demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); - ++pending_reads_; - } - } - private: bool IsStopped() { return state_ == kStopped; } @@ -251,7 +233,25 @@ class DecoderBase : public Decoder { } // Decode the frame right away. - DoDecode(buffer); + DoDecode(buffer, NewRunnableMethod(this, &DecoderBase::OnDecodeComplete)); + } + + void OnDecodeComplete() { + // Attempt to fulfill a pending read callback and schedule additional reads + // if necessary. + bool fulfilled = FulfillPendingRead(); + + // Issue reads as necessary. + // + // Note that it's possible for us to decode but not produce a frame, in + // which case |pending_reads_| will remain less than |read_queue_| so we + // need to schedule an additional read. + DCHECK_LE(pending_reads_, read_queue_.size()); + if (!fulfilled) { + DCHECK_LT(pending_reads_, read_queue_.size()); + demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); + ++pending_reads_; + } } // Attempts to fulfill a single pending read by dequeuing a buffer and read diff --git a/media/filters/decoder_base_unittest.cc b/media/filters/decoder_base_unittest.cc index 5468602..fc9214c 100644 --- a/media/filters/decoder_base_unittest.cc +++ b/media/filters/decoder_base_unittest.cc @@ -54,7 +54,7 @@ class MockDecoderImpl : public media::DecoderBase< Task* done_cb)); MOCK_METHOD0(DoStop, void()); MOCK_METHOD2(DoSeek, void(base::TimeDelta time, Task* done_cb)); - MOCK_METHOD1(DoDecode, void(media::Buffer* input)); + MOCK_METHOD2(DoDecode, void(media::Buffer* input, Task* done_cb)); private: FRIEND_TEST(media::DecoderBaseTest, FlowControl); @@ -83,7 +83,7 @@ ACTION(Initialize) { ACTION_P(SaveDecodeRequest, list) { scoped_refptr<Buffer> buffer = arg0; - list->push_back(buffer); + list->push_back(arg1); } ACTION(CompleteDemuxRequest) { @@ -121,11 +121,11 @@ TEST(DecoderBaseTest, FlowControl) { // Read. StrictMock<MockDecoderReadCallback> read_callback; - std::vector<scoped_refptr<Buffer> > decode_requests; + std::vector<Task*> decode_requests; EXPECT_CALL(*demuxer_stream, Read(NotNull())) .Times(2) .WillRepeatedly(CompleteDemuxRequest()); - EXPECT_CALL(*decoder, DoDecode(NotNull())) + EXPECT_CALL(*decoder, DoDecode(NotNull(), NotNull())) .Times(2) .WillRepeatedly(SaveDecodeRequest(&decode_requests)); decoder->Read( @@ -140,7 +140,7 @@ TEST(DecoderBaseTest, FlowControl) { EXPECT_CALL(read_callback, ReadCallback(NotNull())).Times(2); for (size_t i = 0; i < decode_requests.size(); ++i) { decoder->EnqueueResult(new MockDecoderOutput()); - decoder->OnDecodeComplete(); + AutoTaskRunner done_cb(decode_requests[i]); } decode_requests.clear(); message_loop.RunAllPending(); diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc index 6d4d512..7c3e206f 100644 --- a/media/filters/ffmpeg_audio_decoder.cc +++ b/media/filters/ffmpeg_audio_decoder.cc @@ -154,7 +154,9 @@ static void ConvertAudioF32ToS32(void* buffer, int buffer_size) { } } -void FFmpegAudioDecoder::DoDecode(Buffer* input) { +void FFmpegAudioDecoder::DoDecode(Buffer* input, Task* done_cb) { + AutoTaskRunner done_runner(done_cb); + // Due to FFmpeg API changes we no longer have const read-only pointers. AVPacket packet; av_init_packet(&packet); @@ -183,7 +185,6 @@ void FFmpegAudioDecoder::DoDecode(Buffer* input) { << input->GetDuration().InMicroseconds() << " us" << " , packet size: " << input->GetDataSize() << " bytes"; - DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(); return; } @@ -214,7 +215,6 @@ void FFmpegAudioDecoder::DoDecode(Buffer* input) { } EnqueueResult(result_buffer); - DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(); return; } @@ -225,7 +225,6 @@ void FFmpegAudioDecoder::DoDecode(Buffer* input) { input->GetTimestamp() != StreamSample::kInvalidTimestamp && input->GetDuration() != StreamSample::kInvalidTimestamp) { estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration(); - DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(); return; } @@ -239,7 +238,6 @@ void FFmpegAudioDecoder::DoDecode(Buffer* input) { result_buffer->SetDuration(input->GetDuration()); EnqueueResult(result_buffer); } - DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(); } base::TimeDelta FFmpegAudioDecoder::CalculateDuration(size_t size) { diff --git a/media/filters/ffmpeg_audio_decoder.h b/media/filters/ffmpeg_audio_decoder.h index d0ad27f..f5ff8ac 100644 --- a/media/filters/ffmpeg_audio_decoder.h +++ b/media/filters/ffmpeg_audio_decoder.h @@ -29,7 +29,7 @@ class FFmpegAudioDecoder : public DecoderBase<AudioDecoder, Buffer> { virtual void DoSeek(base::TimeDelta time, Task* done_cb); - virtual void DoDecode(Buffer* input); + virtual void DoDecode(Buffer* input, Task* done_cb); private: friend class FilterFactoryImpl0<FFmpegAudioDecoder>; diff --git a/media/filters/ffmpeg_video_decode_engine.cc b/media/filters/ffmpeg_video_decode_engine.cc index 50183d9..bf6089a 100644 --- a/media/filters/ffmpeg_video_decode_engine.cc +++ b/media/filters/ffmpeg_video_decode_engine.cc @@ -22,15 +22,8 @@ FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { } -void FFmpegVideoDecodeEngine::Initialize( - MessageLoop* message_loop, - AVStream* av_stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb) { +void FFmpegVideoDecodeEngine::Initialize(AVStream* stream, Task* done_cb) { AutoTaskRunner done_runner(done_cb); - // TODO(jiesun): empty_buffer_callback is not used yet until we had path to re - fill_this_buffer_callback_.reset(fill_buffer_callback); // Always try to use two threads for video decoding. There is little reason // not to since current day CPUs tend to be multi-core and we measured @@ -45,7 +38,6 @@ void FFmpegVideoDecodeEngine::Initialize( CHECK(state_ == kCreated); - AVStream* stream = av_stream; codec_context_ = stream->codec; codec_context_->flags2 |= CODEC_FLAG2_FAST; // Enable faster H264 decode. // Enable motion vector search (potentially slow), strong deblocking filter @@ -98,14 +90,15 @@ static void CopyPlane(size_t plane, } } -void FFmpegVideoDecodeEngine::EmptyThisBuffer( - scoped_refptr<Buffer> buffer) { - DecodeFrame(buffer); -} - -// Try to decode frame when both input and output are ready. -void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { - scoped_refptr<VideoFrame> video_frame; +// Decodes one frame of video with the given buffer. +void FFmpegVideoDecodeEngine::DecodeFrame( + Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_frame, + Task* done_cb) { + AutoTaskRunner done_runner(done_cb); + *got_frame = false; + *video_frame = NULL; // Create a packet for input data. // Due to FFmpeg API changes we no longer have const read-only pointers. @@ -131,13 +124,13 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { << buffer->GetDuration().InMicroseconds() << " us" << " , packet size: " << buffer->GetDataSize() << " bytes"; - fill_this_buffer_callback_->Run(video_frame); + *got_frame = false; return; } // If frame_decoded == 0, then no frame was produced. - if (frame_decoded == 0) { - fill_this_buffer_callback_->Run(video_frame); + *got_frame = frame_decoded != 0; + if (!*got_frame) { return; } @@ -148,7 +141,7 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { !av_frame_->data[VideoFrame::kUPlane] || !av_frame_->data[VideoFrame::kVPlane]) { // TODO(jiesun): this is also an error case handled as normal. - fill_this_buffer_callback_->Run(video_frame); + *got_frame = false; return; } @@ -170,10 +163,10 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { codec_context_->height, timestamp, duration, - &video_frame); - if (!video_frame.get()) { + video_frame); + if (!video_frame->get()) { // TODO(jiesun): this is also an error case handled as normal. - fill_this_buffer_callback_->Run(video_frame); + *got_frame = false; return; } @@ -182,11 +175,9 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { // avcodec_decode_video() call. // TODO(scherkus): figure out pre-allocation/buffer cycling scheme. // TODO(scherkus): is there a cleaner way to figure out the # of planes? - CopyPlane(VideoFrame::kYPlane, video_frame.get(), av_frame_.get()); - CopyPlane(VideoFrame::kUPlane, video_frame.get(), av_frame_.get()); - CopyPlane(VideoFrame::kVPlane, video_frame.get(), av_frame_.get()); - - fill_this_buffer_callback_->Run(video_frame); + CopyPlane(VideoFrame::kYPlane, video_frame->get(), av_frame_.get()); + CopyPlane(VideoFrame::kUPlane, video_frame->get(), av_frame_.get()); + CopyPlane(VideoFrame::kVPlane, video_frame->get(), av_frame_.get()); } void FFmpegVideoDecodeEngine::Flush(Task* done_cb) { diff --git a/media/filters/ffmpeg_video_decode_engine.h b/media/filters/ffmpeg_video_decode_engine.h index bead1fc..afafd58 100644 --- a/media/filters/ffmpeg_video_decode_engine.h +++ b/media/filters/ffmpeg_video_decode_engine.h @@ -25,12 +25,10 @@ class FFmpegVideoDecodeEngine : public VideoDecodeEngine { virtual ~FFmpegVideoDecodeEngine(); // Implementation of the VideoDecodeEngine Interface. - virtual void Initialize(MessageLoop* message_loop, - AVStream* av_stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb); - virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer); + virtual void Initialize(AVStream* stream, Task* done_cb); + virtual void DecodeFrame(Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_result, Task* done_cb); virtual void Flush(Task* done_cb); virtual VideoFrame::Format GetSurfaceFormat() const; @@ -43,12 +41,9 @@ class FFmpegVideoDecodeEngine : public VideoDecodeEngine { } private: - void DecodeFrame(scoped_refptr<Buffer> buffer); - AVCodecContext* codec_context_; State state_; scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> av_frame_; - scoped_ptr<FillThisBufferCallback> fill_this_buffer_callback_; DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngine); }; diff --git a/media/filters/ffmpeg_video_decode_engine_unittest.cc b/media/filters/ffmpeg_video_decode_engine_unittest.cc index 4919e66..f1d1680 100644 --- a/media/filters/ffmpeg_video_decode_engine_unittest.cc +++ b/media/filters/ffmpeg_video_decode_engine_unittest.cc @@ -74,21 +74,10 @@ class FFmpegVideoDecodeEngineTest : public testing::Test { TaskMocker done_cb; EXPECT_CALL(done_cb, Run()); - test_engine_->Initialize( - MessageLoop::current(), - &stream_, - NULL, - NewCallback(this, &FFmpegVideoDecodeEngineTest::OnDecodeComplete), - done_cb.CreateTask()); + test_engine_->Initialize(&stream_, done_cb.CreateTask()); EXPECT_EQ(VideoDecodeEngine::kNormal, test_engine_->state()); } - public: - MOCK_METHOD1(OnDecodeComplete, - void(scoped_refptr<VideoFrame> video_frame)); - - scoped_refptr<VideoFrame> video_frame_; - protected: scoped_ptr<FFmpegVideoDecodeEngine> test_engine_; scoped_array<uint8_t> frame_buffer_; StrictMock<MockFFmpeg> mock_ffmpeg_; @@ -122,12 +111,7 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { TaskMocker done_cb; EXPECT_CALL(done_cb, Run()); - test_engine_->Initialize( - MessageLoop::current(), - &stream_, - NULL, - NULL, - done_cb.CreateTask()); + test_engine_->Initialize(&stream_, done_cb.CreateTask()); EXPECT_EQ(VideoDecodeEngine::kError, test_engine_->state()); } @@ -145,12 +129,7 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_InitThreadFails) { TaskMocker done_cb; EXPECT_CALL(done_cb, Run()); - test_engine_->Initialize( - MessageLoop::current(), - &stream_, - NULL, - NULL, - done_cb.CreateTask()); + test_engine_->Initialize(&stream_, done_cb.CreateTask()); EXPECT_EQ(VideoDecodeEngine::kError, test_engine_->state()); } @@ -170,19 +149,10 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { TaskMocker done_cb; EXPECT_CALL(done_cb, Run()); - test_engine_->Initialize( - MessageLoop::current(), - &stream_, - NULL, - NULL, - done_cb.CreateTask()); + test_engine_->Initialize(&stream_, done_cb.CreateTask()); EXPECT_EQ(VideoDecodeEngine::kError, test_engine_->state()); } -ACTION_P(DecodeComplete, decoder) { - decoder->video_frame_ = arg0; -} - TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { Initialize(); @@ -200,13 +170,18 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame. Return(0))); - EXPECT_CALL(*this, OnDecodeComplete(_)) - .WillOnce(DecodeComplete(this)); - test_engine_->EmptyThisBuffer(buffer_); + TaskMocker done_cb; + EXPECT_CALL(done_cb, Run()); + + bool got_result; + scoped_refptr<VideoFrame> video_frame; + test_engine_->DecodeFrame(buffer_, &video_frame, &got_result, + done_cb.CreateTask()); + EXPECT_TRUE(got_result); EXPECT_EQ(kTimestamp.InMicroseconds(), - video_frame_->GetTimestamp().ToInternalValue()); + video_frame->GetTimestamp().ToInternalValue()); EXPECT_EQ(kDuration.ToInternalValue(), - video_frame_->GetDuration().ToInternalValue()); + video_frame->GetDuration().ToInternalValue()); } TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { @@ -219,10 +194,14 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { .WillOnce(DoAll(SetArgumentPointee<2>(0), // Simulate 0 byte frame. Return(0))); - EXPECT_CALL(*this, OnDecodeComplete(_)) - .WillOnce(DecodeComplete(this)); - test_engine_->EmptyThisBuffer(buffer_); - EXPECT_FALSE(video_frame_.get()); + TaskMocker done_cb; + EXPECT_CALL(done_cb, Run()); + + bool got_result; + scoped_refptr<VideoFrame> video_frame; + test_engine_->DecodeFrame(buffer_, &video_frame, &got_result, + done_cb.CreateTask()); + EXPECT_FALSE(got_result); } TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { @@ -234,10 +213,14 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) .WillOnce(Return(-1)); - EXPECT_CALL(*this, OnDecodeComplete(_)) - .WillOnce(DecodeComplete(this)); - test_engine_->EmptyThisBuffer(buffer_); - EXPECT_FALSE(video_frame_.get()); + TaskMocker done_cb; + EXPECT_CALL(done_cb, Run()); + + bool got_result; + scoped_refptr<VideoFrame> video_frame; + test_engine_->DecodeFrame(buffer_, &video_frame, &got_result, + done_cb.CreateTask()); + EXPECT_FALSE(got_result); } TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) { diff --git a/media/filters/omx_video_decode_engine.cc b/media/filters/omx_video_decode_engine.cc index 6d669f7..b8794f3 100644 --- a/media/filters/omx_video_decode_engine.cc +++ b/media/filters/omx_video_decode_engine.cc @@ -35,19 +35,12 @@ OmxVideoDecodeEngine::OmxVideoDecodeEngine() OmxVideoDecodeEngine::~OmxVideoDecodeEngine() { } -void OmxVideoDecodeEngine::Initialize( - MessageLoop* message_loop, - AVStream* av_stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb) { +void OmxVideoDecodeEngine::Initialize(AVStream* stream, Task* done_cb) { DCHECK_EQ(message_loop_, MessageLoop::current()); - fill_this_buffer_callback_.reset(fill_buffer_callback); AutoTaskRunner done_runner(done_cb); - omx_codec_ = new media::OmxCodec(message_loop); + omx_codec_ = new media::OmxCodec(message_loop_); - AVStream* stream = av_stream; width_ = stream->codec->width; height_ = stream->codec->height; @@ -82,19 +75,6 @@ void OmxVideoDecodeEngine::OnHardwareError() { state_ = kError; } -void OmxVideoDecodeEngine::EmptyThisBuffer(scoped_refptr<Buffer> buffer) { - if ( state_ != kNormal ) return; // discard the buffer. - if (!has_fed_on_eos_) { - omx_codec_->Feed(buffer, - NewCallback(this, &OmxVideoDecodeEngine::OnFeedDone)); - - if (buffer->IsEndOfStream()) { - has_fed_on_eos_ = true; - } - } - DecodeFrame(); -} - // This method assumes the input buffer contains exactly one frame or is an // end-of-stream buffer. The logic of this method and in OnReadComplete() is // based on this assumation. @@ -102,10 +82,31 @@ void OmxVideoDecodeEngine::EmptyThisBuffer(scoped_refptr<Buffer> buffer) { // For every input buffer received here, we submit one read request to the // decoder. So when a read complete callback is received, a corresponding // decode request must exist. -void OmxVideoDecodeEngine::DecodeFrame() { +void OmxVideoDecodeEngine::DecodeFrame(Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_result, + Task* done_cb) { DCHECK_EQ(message_loop_, MessageLoop::current()); - if (state_ != kNormal) + if (state_ != kNormal) { + *got_result = false; + *video_frame = NULL; + done_cb->Run(); + delete done_cb; return; + } + + if (!has_fed_on_eos_) { + omx_codec_->Feed(buffer, + NewCallback(this, &OmxVideoDecodeEngine::OnFeedDone)); + + if (buffer->IsEndOfStream()) { + has_fed_on_eos_ = true; + } + } + + // Enqueue the decode request and the associated buffer. + decode_request_queue_.push_back( + DecodeRequest(video_frame, got_result, done_cb)); // Submit a read request to the decoder. omx_codec_->Read(NewCallback(this, &OmxVideoDecodeEngine::OnReadComplete)); @@ -137,31 +138,41 @@ void OmxVideoDecodeEngine::OnReadComplete( DCHECK_EQ(message_loop_, MessageLoop::current()); DCHECK_EQ(buffer->nFilledLen, width_*height_*3/2 ); + // We assume that when we receive a read complete callback from + // OmxCodec there was a read request made. + CHECK(!decode_request_queue_.empty()); + DecodeRequest request = decode_request_queue_.front(); + decode_request_queue_.pop_front(); + *request.got_result = false; + + // Notify the read request to this object has been fulfilled. + AutoTaskRunner done_cb_runner(request.done_cb); + if (!buffer) // EOF signal by OmxCodec return; - scoped_refptr<VideoFrame> frame; VideoFrame::CreateFrame(GetSurfaceFormat(), width_, height_, StreamSample::kInvalidTimestamp, StreamSample::kInvalidTimestamp, - &frame); - if (!frame.get()) { + request.frame); + if (!request.frame->get()) { // TODO(jiesun): this is also an error case handled as normal. return; } + // Now we had everything ready. + *request.got_result = true; + // TODO(jiesun): Assume YUV 420 format. // TODO(jiesun): We will use VideoFrame to wrap OMX_BUFFERHEADTYPE. const int pixels = width_ * height_; - memcpy(frame->data(VideoFrame::kYPlane), buffer->pBuffer, pixels); - memcpy(frame->data(VideoFrame::kUPlane), buffer->pBuffer + pixels, + memcpy((*request.frame)->data(VideoFrame::kYPlane), buffer->pBuffer, pixels); + memcpy((*request.frame)->data(VideoFrame::kUPlane), buffer->pBuffer + pixels, pixels / 4); - memcpy(frame->data(VideoFrame::kVPlane), + memcpy((*request.frame)->data(VideoFrame::kVPlane), buffer->pBuffer + pixels + pixels /4, pixels / 4); - - fill_this_buffer_callback_->Run(frame); } } // namespace media diff --git a/media/filters/omx_video_decode_engine.h b/media/filters/omx_video_decode_engine.h index f245c03..756ccc1 100644 --- a/media/filters/omx_video_decode_engine.h +++ b/media/filters/omx_video_decode_engine.h @@ -31,12 +31,10 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { virtual ~OmxVideoDecodeEngine(); // Implementation of the VideoDecodeEngine Interface. - virtual void Initialize(MessageLoop* message_loop, - AVStream* av_stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb); - virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer); + virtual void Initialize(AVStream* stream, Task* done_cb); + virtual void DecodeFrame(Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_result, Task* done_cb); virtual void Flush(Task* done_cb); virtual VideoFrame::Format GetSurfaceFormat() const; @@ -53,7 +51,20 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { }; private: - void DecodeFrame(); + // A struct to hold parameters of a decode request. Objects pointed by + // these parameters are owned by the caller. + struct DecodeRequest { + DecodeRequest(scoped_refptr<VideoFrame>* f, bool* b, Task* cb) + : frame(f), + got_result(b), + done_cb(cb) { + } + + scoped_refptr<VideoFrame>* frame; + bool* got_result; + Task* done_cb; + }; + virtual void OnFeedDone(Buffer* buffer); virtual void OnHardwareError(); virtual void OnReadComplete(OMX_BUFFERHEADERTYPE* buffer); @@ -69,11 +80,11 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { bool has_fed_on_eos_; // Used to avoid sending an end of stream to // OpenMax twice since OpenMax does not always // handle this nicely. + std::list<DecodeRequest> decode_request_queue_; scoped_refptr<media::OmxCodec> omx_codec_; scoped_ptr<media::OmxConfigurator> omx_configurator_; MessageLoop* message_loop_; - scoped_ptr<FillThisBufferCallback> fill_this_buffer_callback_; DISALLOW_COPY_AND_ASSIGN(OmxVideoDecodeEngine); }; diff --git a/media/filters/video_decode_engine.h b/media/filters/video_decode_engine.h index 2cc767a..975ecea 100644 --- a/media/filters/video_decode_engine.h +++ b/media/filters/video_decode_engine.h @@ -5,13 +5,12 @@ #ifndef MEDIA_FILTERS_VIDEO_DECODE_ENGINE_H_ #define MEDIA_FILTERS_VIDEO_DECODE_ENGINE_H_ -#include "base/callback.h" -#include "base/message_loop.h" #include "media/base/video_frame.h" // FFmpeg types. // // TODO(ajwong): Try to cut the dependency on the FFmpeg types. +struct AVFrame; struct AVStream; class Task; @@ -32,34 +31,18 @@ class VideoDecodeEngine { VideoDecodeEngine() {} virtual ~VideoDecodeEngine() {} - typedef Callback1<scoped_refptr<Buffer> >::Type EmptyThisBufferCallback; - typedef Callback1<scoped_refptr<VideoFrame> >::Type FillThisBufferCallback; - // Initialized the engine. On successful Initialization, state() should // return kNormal. - virtual void Initialize(MessageLoop* message_loop, - AVStream* av_stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb) = 0; - - // These functions and callbacks could be used in two scenarios for both - // input and output streams: - // 1. Engine provide buffers. - // 2. Outside party provide buffers. - // The currently planned engine implementation: - // 1. provides the input buffer request inside engine through - // |EmptyThisBufferCallback|. The engine implementation has better knowledge - // of the decoder reordering delay and jittery removal requirements. Input - // buffers are returned into engine through |EmptyThisBuffer|. - // 2. Output buffers are provided from outside the engine, and feed into - // engine through |FillThisBuffer|. Output buffers are returned to outside - // by |FillThisBufferCallback|. - - virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer) = 0; - virtual void FillThisBuffer(scoped_refptr<VideoFrame> frame) { - NOTREACHED(); - }; + virtual void Initialize(AVStream* stream, Task* done_cb) = 0; + + // Decodes one frame of video with the given buffer. Returns false if there + // was a decode error, or a zero-byte frame was produced. + // + // TODO(ajwong): Should this function just allocate a new yuv_frame and return + // it via a "GetNextFrame()" method or similar? + virtual void DecodeFrame(Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_result, Task* done_cb) = 0; // Flushes the decode engine of any buffered input packets. virtual void Flush(Task* done_cb) = 0; diff --git a/media/filters/video_decoder_impl.cc b/media/filters/video_decoder_impl.cc index 7b1a4d6..38f36dc 100644 --- a/media/filters/video_decoder_impl.cc +++ b/media/filters/video_decoder_impl.cc @@ -59,10 +59,7 @@ void VideoDecoderImpl::DoInitialize(DemuxerStream* demuxer_stream, media_format_.SetAsInteger(MediaFormat::kHeight, height_); decode_engine_->Initialize( - message_loop(), av_stream, - NULL, - NewCallback(this, &VideoDecoderImpl::OnDecodeComplete), NewRunnableMethod(this, &VideoDecoderImpl::OnInitializeComplete, success, @@ -90,7 +87,9 @@ void VideoDecoderImpl::DoSeek(base::TimeDelta time, Task* done_cb) { decode_engine_->Flush(done_cb); } -void VideoDecoderImpl::DoDecode(Buffer* buffer) { +void VideoDecoderImpl::DoDecode(Buffer* buffer, Task* done_cb) { + AutoTaskRunner done_runner(done_cb); + // TODO(ajwong): This DoDecode() and OnDecodeComplete() set of functions is // too complicated to easily unittest. The test becomes fragile. Try to // find a way to reorganize into smaller units for testing. @@ -142,14 +141,31 @@ void VideoDecoderImpl::DoDecode(Buffer* buffer) { } // Otherwise, attempt to decode a single frame. - decode_engine_->EmptyThisBuffer(buffer); + bool* got_frame = new bool; + scoped_refptr<VideoFrame>* video_frame = new scoped_refptr<VideoFrame>(NULL); + decode_engine_->DecodeFrame( + buffer, + video_frame, + got_frame, + NewRunnableMethod(this, + &VideoDecoderImpl::OnDecodeComplete, + video_frame, + got_frame, + done_runner.release())); } -void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame> video_frame) { - if (video_frame.get()) { - // If we actually got data back, enqueue a frame. +void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame>* video_frame, + bool* got_frame, Task* done_cb) { + // Note: The |done_runner| must be declared *last* to ensure proper + // destruction order. + scoped_ptr<bool> got_frame_deleter(got_frame); + scoped_ptr<scoped_refptr<VideoFrame> > video_frame_deleter(video_frame); + AutoTaskRunner done_runner(done_cb); + + // If we actually got data back, enqueue a frame. + if (*got_frame) { last_pts_ = FindPtsAndDuration(*time_base_, pts_heap_, last_pts_, - video_frame.get()); + video_frame->get()); // Pop off a pts on a successful decode since we are "using up" one // timestamp. @@ -165,9 +181,9 @@ void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame> video_frame) { NOTREACHED() << "Attempting to decode more frames than were input."; } - video_frame->SetTimestamp(last_pts_.timestamp); - video_frame->SetDuration(last_pts_.duration); - EnqueueVideoFrame(video_frame); + (*video_frame)->SetTimestamp(last_pts_.timestamp); + (*video_frame)->SetDuration(last_pts_.duration); + EnqueueVideoFrame(*video_frame); } else { // When in kFlushCodec, any errored decode, or a 0-lengthed frame, // is taken as a signal to stop decoding. @@ -176,14 +192,6 @@ void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame> video_frame) { EnqueueEmptyFrame(); } } - - OnEmptyBufferDone(); -} - -void VideoDecoderImpl::OnEmptyBufferDone() { - // TODO(jiesun): what |DecodeBase::OnDecodeComplete| done is just - // what should be done in EmptyThisBufferCallback. - DecoderBase<VideoDecoder, VideoFrame>::OnDecodeComplete(); } void VideoDecoderImpl::EnqueueVideoFrame( diff --git a/media/filters/video_decoder_impl.h b/media/filters/video_decoder_impl.h index 6d47b3d..e63f961 100644 --- a/media/filters/video_decoder_impl.h +++ b/media/filters/video_decoder_impl.h @@ -34,10 +34,7 @@ class VideoDecoderImpl : public DecoderBase<VideoDecoder, VideoFrame> { virtual void DoInitialize(DemuxerStream* demuxer_stream, bool* success, Task* done_cb); virtual void DoSeek(base::TimeDelta time, Task* done_cb); - virtual void DoDecode(Buffer* input); - - protected: - virtual void OnEmptyBufferDone(); + virtual void DoDecode(Buffer* buffer, Task* done_cb); private: friend class FilterFactoryImpl1<VideoDecoderImpl, VideoDecodeEngine*>; @@ -71,8 +68,8 @@ class VideoDecoderImpl : public DecoderBase<VideoDecoder, VideoFrame> { // Methods that pickup after the decode engine has finished its action. virtual void OnInitializeComplete(bool* success /* Not owned */, Task* done_cb); - - virtual void OnDecodeComplete(scoped_refptr<VideoFrame> video_frame); + virtual void OnDecodeComplete(scoped_refptr<VideoFrame>* video_frame, + bool* got_frame, Task* done_cb); // Attempt to get the PTS and Duration for this frame by examining the time // info provided via packet stream (stored in |pts_heap|), or the info diff --git a/media/filters/video_decoder_impl_unittest.cc b/media/filters/video_decoder_impl_unittest.cc index d7f042d..5bef51c 100644 --- a/media/filters/video_decoder_impl_unittest.cc +++ b/media/filters/video_decoder_impl_unittest.cc @@ -29,7 +29,6 @@ using ::testing::ReturnNull; using ::testing::SetArgumentPointee; using ::testing::StrictMock; using ::testing::WithArg; -using ::testing::Invoke; namespace media { @@ -50,13 +49,10 @@ class MockFFmpegDemuxerStream : public MockDemuxerStream, class MockVideoDecodeEngine : public VideoDecodeEngine { public: - MOCK_METHOD5(Initialize, void(MessageLoop* message_loop, - AVStream* stream, - EmptyThisBufferCallback* empty_buffer_callback, - FillThisBufferCallback* fill_buffer_callback, - Task* done_cb)); - MOCK_METHOD1(EmptyThisBuffer, void(scoped_refptr<Buffer> buffer)); - MOCK_METHOD1(FillThisBuffer, void(scoped_refptr<VideoFrame> buffer)); + MOCK_METHOD2(Initialize, void(AVStream* stream, Task* done_cb)); + MOCK_METHOD4(DecodeFrame, void(Buffer* buffer, + scoped_refptr<VideoFrame>* video_frame, + bool* got_result, Task* done_cb)); MOCK_METHOD1(Flush, void(Task* done_cb)); MOCK_CONST_METHOD0(state, State()); MOCK_CONST_METHOD0(GetSurfaceFormat, VideoFrame::Format()); @@ -72,18 +68,15 @@ class DecoderPrivateMock : public VideoDecoderImpl { MOCK_METHOD1(EnqueueVideoFrame, void(const scoped_refptr<VideoFrame>& video_frame)); MOCK_METHOD0(EnqueueEmptyFrame, void()); + MOCK_METHOD3(CopyPlane, void(size_t plane, + const scoped_refptr<VideoFrame> video_frame, + const AVFrame* frame)); MOCK_METHOD4(FindPtsAndDuration, TimeTuple( const AVRational& time_base, const PtsHeap& pts_heap, const TimeTuple& last_pts, const VideoFrame* frame)); MOCK_METHOD0(SignalPipelineError, void()); - MOCK_METHOD0(OnEmptyBufferDone, void()); - - // change access qualifier for test: used in actions. - void OnDecodeComplete(scoped_refptr<VideoFrame> video_frame) { - VideoDecoderImpl::OnDecodeComplete(video_frame); - } }; // Fixture class to facilitate writing tests. Takes care of setting up the @@ -214,8 +207,8 @@ TEST_F(VideoDecoderImplTest, Initialize_EngineFails) { EXPECT_CALL(*demuxer_, GetAVStream()) .WillOnce(Return(&stream_)); - EXPECT_CALL(*engine_, Initialize(_, _, _, _, _)) - .WillOnce(WithArg<4>(InvokeRunnable())); + EXPECT_CALL(*engine_, Initialize(_, _)) + .WillOnce(WithArg<1>(InvokeRunnable())); EXPECT_CALL(*engine_, state()) .WillOnce(Return(VideoDecodeEngine::kError)); @@ -236,8 +229,8 @@ TEST_F(VideoDecoderImplTest, Initialize_Successful) { EXPECT_CALL(*demuxer_, GetAVStream()) .WillOnce(Return(&stream_)); - EXPECT_CALL(*engine_, Initialize(_, _, _, _, _)) - .WillOnce(WithArg<4>(InvokeRunnable())); + EXPECT_CALL(*engine_, Initialize(_, _)) + .WillOnce(WithArg<1>(InvokeRunnable())); EXPECT_CALL(*engine_, state()) .WillOnce(Return(VideoDecodeEngine::kNormal)); @@ -333,15 +326,6 @@ TEST_F(VideoDecoderImplTest, FindPtsAndDuration) { EXPECT_EQ(789, result_pts.duration.InMicroseconds()); } -ACTION_P2(DecodeComplete, decoder, video_frame) { - decoder->OnDecodeComplete(video_frame); -} - -ACTION_P2(DecodeNotComplete, decoder, video_frame) { - scoped_refptr<VideoFrame> null_frame; - decoder->OnDecodeComplete(null_frame); -} - TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Simulates a input sequence of three buffers, and six decode requests to // exercise the state transitions, and bookkeeping logic of DoDecode. @@ -359,13 +343,22 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Setup decoder to buffer one frame, decode one frame, fail one frame, // decode one more, and then fail the last one to end decoding. - EXPECT_CALL(*mock_engine, EmptyThisBuffer(_)) - .WillOnce(DecodeNotComplete(mock_decoder.get(), video_frame_)) - .WillOnce(DecodeComplete(mock_decoder.get(), video_frame_)) - .WillOnce(DecodeNotComplete(mock_decoder.get(), video_frame_)) - .WillOnce(DecodeComplete(mock_decoder.get(), video_frame_)) - .WillOnce(DecodeComplete(mock_decoder.get(), video_frame_)) - .WillOnce(DecodeNotComplete(mock_decoder.get(), video_frame_)); + EXPECT_CALL(*mock_engine, DecodeFrame(_, _, _,_)) + .WillOnce(DoAll(SetArgumentPointee<2>(false), + WithArg<3>(InvokeRunnable()))) + .WillOnce(DoAll(SetArgumentPointee<1>(video_frame_), + SetArgumentPointee<2>(true), + WithArg<3>(InvokeRunnable()))) + .WillOnce(DoAll(SetArgumentPointee<2>(false), + WithArg<3>(InvokeRunnable()))) + .WillOnce(DoAll(SetArgumentPointee<1>(video_frame_), + SetArgumentPointee<2>(true), + WithArg<3>(InvokeRunnable()))) + .WillOnce(DoAll(SetArgumentPointee<1>(video_frame_), + SetArgumentPointee<2>(true), + WithArg<3>(InvokeRunnable()))) + .WillOnce(DoAll(SetArgumentPointee<2>(false), + WithArg<3>(InvokeRunnable()))); EXPECT_CALL(*mock_decoder, FindPtsAndDuration(_, _, _, _)) .WillOnce(Return(kTestPts1)) .WillOnce(Return(kTestPts2)) @@ -374,22 +367,25 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { .Times(3); EXPECT_CALL(*mock_decoder, EnqueueEmptyFrame()) .Times(1); - EXPECT_CALL(*mock_decoder, OnEmptyBufferDone()) - .Times(6); + + // Setup callbacks to be executed 6 times. + TaskMocker done_cb; + EXPECT_CALL(done_cb, Run()).Times(6); // Setup initial state and check that it is sane. ASSERT_EQ(VideoDecoderImpl::kNormal, mock_decoder->state_); ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.timestamp); ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.duration); + // Decode once, which should simulate a buffering call. - mock_decoder->DoDecode(buffer_); + mock_decoder->DoDecode(buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kNormal, mock_decoder->state_); ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.timestamp); ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.duration); EXPECT_FALSE(mock_decoder->pts_heap_.IsEmpty()); // Decode a second time, which should yield the first frame. - mock_decoder->DoDecode(buffer_); + mock_decoder->DoDecode(buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kNormal, mock_decoder->state_); EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp); EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration); @@ -397,7 +393,7 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Decode a third time, with a regular buffer. The decode will error // out, but the state should be the same. - mock_decoder->DoDecode(buffer_); + mock_decoder->DoDecode(buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kNormal, mock_decoder->state_); EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp); EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration); @@ -405,7 +401,7 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Decode a fourth time, with an end of stream buffer. This should // yield the second frame, and stay in flushing mode. - mock_decoder->DoDecode(end_of_stream_buffer_); + mock_decoder->DoDecode(end_of_stream_buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kFlushCodec, mock_decoder->state_); EXPECT_TRUE(kTestPts2.timestamp == mock_decoder->last_pts_.timestamp); EXPECT_TRUE(kTestPts2.duration == mock_decoder->last_pts_.duration); @@ -413,7 +409,7 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Decode a fifth time with an end of stream buffer. this should // yield the third frame. - mock_decoder->DoDecode(end_of_stream_buffer_); + mock_decoder->DoDecode(end_of_stream_buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kFlushCodec, mock_decoder->state_); EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp); EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration); @@ -421,7 +417,7 @@ TEST_F(VideoDecoderImplTest, DoDecode_TestStateTransition) { // Decode a sixth time with an end of stream buffer. This should // Move into kDecodeFinished. - mock_decoder->DoDecode(end_of_stream_buffer_); + mock_decoder->DoDecode(end_of_stream_buffer_, done_cb.CreateTask()); EXPECT_EQ(VideoDecoderImpl::kDecodeFinished, mock_decoder->state_); EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp); EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration); @@ -440,9 +436,14 @@ TEST_F(VideoDecoderImplTest, DoDecode_FinishEnqueuesEmptyFrames) { // not even examined. EXPECT_CALL(*mock_decoder, EnqueueEmptyFrame()).Times(3); - mock_decoder->DoDecode(NULL); - mock_decoder->DoDecode(buffer_); - mock_decoder->DoDecode(end_of_stream_buffer_); + // Setup callbacks to be executed 3 times. + TaskMocker done_cb; + EXPECT_CALL(done_cb, Run()).Times(3); + + mock_decoder->DoDecode(NULL, done_cb.CreateTask()); + mock_decoder->DoDecode(buffer_, done_cb.CreateTask()); + mock_decoder->DoDecode(end_of_stream_buffer_, done_cb.CreateTask()); + EXPECT_EQ(VideoDecoderImpl::kDecodeFinished, mock_decoder->state_); } |