| Index: media/filters/ffmpeg_video_decoder_unittest.cc
|
| ===================================================================
|
| --- media/filters/ffmpeg_video_decoder_unittest.cc (revision 277175)
|
| +++ media/filters/ffmpeg_video_decoder_unittest.cc (working copy)
|
| @@ -2,7 +2,6 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include <list>
|
| #include <string>
|
| #include <vector>
|
|
|
| @@ -27,7 +26,6 @@
|
|
|
| using ::testing::_;
|
| using ::testing::AtLeast;
|
| -using ::testing::AtMost;
|
| using ::testing::InSequence;
|
| using ::testing::IsNull;
|
| using ::testing::Return;
|
| @@ -49,7 +47,7 @@
|
| public:
|
| FFmpegVideoDecoderTest()
|
| : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
|
| - decode_cb_(base::Bind(&FFmpegVideoDecoderTest::DecodeDone,
|
| + decode_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
|
| base::Unretained(this))) {
|
| FFmpegGlue::InitializeFFmpeg();
|
|
|
| @@ -70,9 +68,7 @@
|
|
|
| void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
|
| PipelineStatus status) {
|
| - decoder_->Initialize(config, false, NewExpectedStatusCB(status),
|
| - base::Bind(&FFmpegVideoDecoderTest::FrameReady,
|
| - base::Unretained(this)));
|
| + decoder_->Initialize(config, false, NewExpectedStatusCB(status));
|
| message_loop_.RunUntilIdle();
|
| }
|
|
|
| @@ -97,18 +93,24 @@
|
| // Sets up expectations and actions to put FFmpegVideoDecoder in an active
|
| // decoding state.
|
| void EnterDecodingState() {
|
| - EXPECT_EQ(VideoDecoder::kOk, DecodeSingleFrame(i_frame_buffer_));
|
| - ASSERT_EQ(2U, output_frames_.size());
|
| - EXPECT_FALSE(output_frames_[0]->end_of_stream());
|
| - EXPECT_TRUE(output_frames_[1]->end_of_stream());
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame.get());
|
| + EXPECT_FALSE(video_frame->end_of_stream());
|
| }
|
|
|
| // Sets up expectations and actions to put FFmpegVideoDecoder in an end
|
| // of stream state.
|
| void EnterEndOfStreamState() {
|
| - EXPECT_EQ(VideoDecoder::kOk, DecodeSingleFrame(end_of_stream_buffer_));
|
| - ASSERT_FALSE(output_frames_.empty());
|
| - EXPECT_TRUE(output_frames_.back()->end_of_stream());
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecodeSingleFrame(end_of_stream_buffer_, &status, &video_frame);
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame.get());
|
| + EXPECT_TRUE(video_frame->end_of_stream());
|
| }
|
|
|
| typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
|
| @@ -117,10 +119,11 @@
|
| // Decodes all buffers in |input_buffers| and push all successfully decoded
|
| // output frames (excluding EOS frames) into |output_frames|.
|
| // Returns the last decode status returned by the decoder.
|
| - VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers) {
|
| + VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers,
|
| + OutputFrames* output_frames) {
|
| InputBuffers::const_iterator input_iter = input_buffers.begin();
|
|
|
| - while (output_frames_.empty() || !output_frames_.back()->end_of_stream()) {
|
| + for (;;) {
|
| // Prepare input buffer.
|
| scoped_refptr<DecoderBuffer> buffer;
|
| if (input_iter != input_buffers.end()) {
|
| @@ -130,33 +133,53 @@
|
| buffer = end_of_stream_buffer_;
|
| }
|
|
|
| - VideoDecoder::Status status = Decode(buffer);
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> frame;
|
| + Decode(buffer, &status, &frame);
|
|
|
| switch (status) {
|
| case VideoDecoder::kOk:
|
| - break;
|
| + DCHECK(frame);
|
| + if (!frame->end_of_stream()) {
|
| + output_frames->push_back(frame);
|
| + continue;
|
| + } else { // EOS
|
| + return status;
|
| + }
|
| + case VideoDecoder::kNotEnoughData:
|
| + DCHECK(!frame);
|
| + continue;
|
| case VideoDecoder::kAborted:
|
| NOTREACHED();
|
| case VideoDecoder::kDecodeError:
|
| case VideoDecoder::kDecryptError:
|
| - DCHECK(output_frames_.empty());
|
| + DCHECK(!frame);
|
| return status;
|
| }
|
| }
|
| -
|
| - return VideoDecoder::kOk;
|
| }
|
|
|
| // Decodes the single compressed frame in |buffer| and writes the
|
| // uncompressed output to |video_frame|. This method works with single
|
| // and multithreaded decoders. End of stream buffers are used to trigger
|
| // the frame to be returned in the multithreaded decoder case.
|
| - VideoDecoder::Status DecodeSingleFrame(
|
| - const scoped_refptr<DecoderBuffer>& buffer) {
|
| + void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
|
| + VideoDecoder::Status* status,
|
| + scoped_refptr<VideoFrame>* video_frame) {
|
| InputBuffers input_buffers;
|
| input_buffers.push_back(buffer);
|
|
|
| - return DecodeMultipleFrames(input_buffers);
|
| + OutputFrames output_frames;
|
| + *status = DecodeMultipleFrames(input_buffers, &output_frames);
|
| +
|
| + if (*status != VideoDecoder::kOk)
|
| + return;
|
| +
|
| + ASSERT_LE(output_frames.size(), 1U);
|
| + if (output_frames.size() == 1U)
|
| + *video_frame = output_frames[0];
|
| + else
|
| + *video_frame = VideoFrame::CreateEOSFrame();
|
| }
|
|
|
| // Decodes |i_frame_buffer_| and then decodes the data contained in
|
| @@ -172,41 +195,38 @@
|
| input_buffers.push_back(i_frame_buffer_);
|
| input_buffers.push_back(buffer);
|
|
|
| + OutputFrames output_frames;
|
| VideoDecoder::Status status =
|
| - DecodeMultipleFrames(input_buffers);
|
| + DecodeMultipleFrames(input_buffers, &output_frames);
|
|
|
| EXPECT_EQ(VideoDecoder::kOk, status);
|
| - ASSERT_EQ(3U, output_frames_.size());
|
| + ASSERT_EQ(2U, output_frames.size());
|
|
|
| gfx::Size original_size = kVisibleRect.size();
|
| EXPECT_EQ(original_size.width(),
|
| - output_frames_[0]->visible_rect().size().width());
|
| + output_frames[0]->visible_rect().size().width());
|
| EXPECT_EQ(original_size.height(),
|
| - output_frames_[0]->visible_rect().size().height());
|
| + output_frames[0]->visible_rect().size().height());
|
| EXPECT_EQ(expected_width,
|
| - output_frames_[1]->visible_rect().size().width());
|
| + output_frames[1]->visible_rect().size().width());
|
| EXPECT_EQ(expected_height,
|
| - output_frames_[1]->visible_rect().size().height());
|
| - EXPECT_TRUE(output_frames_[2]->end_of_stream());
|
| + output_frames[1]->visible_rect().size().height());
|
| }
|
|
|
| - VideoDecoder::Status Decode(const scoped_refptr<DecoderBuffer>& buffer) {
|
| - VideoDecoder::Status status;
|
| - EXPECT_CALL(*this, DecodeDone(_)).WillOnce(SaveArg<0>(&status));
|
| + void Decode(const scoped_refptr<DecoderBuffer>& buffer,
|
| + VideoDecoder::Status* status,
|
| + scoped_refptr<VideoFrame>* video_frame) {
|
| + EXPECT_CALL(*this, FrameReady(_, _))
|
| + .WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame)));
|
|
|
| decoder_->Decode(buffer, decode_cb_);
|
|
|
| message_loop_.RunUntilIdle();
|
| -
|
| - return status;
|
| }
|
|
|
| - void FrameReady(const scoped_refptr<VideoFrame>& frame) {
|
| - output_frames_.push_back(frame);
|
| - }
|
| + MOCK_METHOD2(FrameReady, void(VideoDecoder::Status,
|
| + const scoped_refptr<VideoFrame>&));
|
|
|
| - MOCK_METHOD1(DecodeDone, void(VideoDecoder::Status));
|
| -
|
| base::MessageLoop message_loop_;
|
| scoped_ptr<FFmpegVideoDecoder> decoder_;
|
|
|
| @@ -218,8 +238,6 @@
|
| scoped_refptr<DecoderBuffer> i_frame_buffer_;
|
| scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
|
|
|
| - OutputFrames output_frames_;
|
| -
|
| private:
|
| DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
|
| };
|
| @@ -337,10 +355,13 @@
|
| Initialize();
|
|
|
| // Simulate decoding a single frame.
|
| - EXPECT_EQ(VideoDecoder::kOk, DecodeSingleFrame(i_frame_buffer_));
|
| - ASSERT_EQ(2U, output_frames_.size());
|
| - EXPECT_FALSE(output_frames_[0]->end_of_stream());
|
| - EXPECT_TRUE(output_frames_[1]->end_of_stream());
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame.get());
|
| + EXPECT_FALSE(video_frame->end_of_stream());
|
| }
|
|
|
| // Verify current behavior for 0 byte frames. FFmpeg simply ignores
|
| @@ -355,31 +376,38 @@
|
| input_buffers.push_back(zero_byte_buffer);
|
| input_buffers.push_back(i_frame_buffer_);
|
|
|
| - VideoDecoder::Status status = DecodeMultipleFrames(input_buffers);
|
| + OutputFrames output_frames;
|
| + VideoDecoder::Status status =
|
| + DecodeMultipleFrames(input_buffers, &output_frames);
|
|
|
| EXPECT_EQ(VideoDecoder::kOk, status);
|
| - ASSERT_EQ(3U, output_frames_.size());
|
| + ASSERT_EQ(2U, output_frames.size());
|
|
|
| - EXPECT_FALSE(output_frames_[0]->end_of_stream());
|
| - EXPECT_FALSE(output_frames_[1]->end_of_stream());
|
| - EXPECT_TRUE(output_frames_[2]->end_of_stream());
|
| + EXPECT_FALSE(output_frames[0]->end_of_stream());
|
| + EXPECT_FALSE(output_frames[1]->end_of_stream());
|
| }
|
|
|
| TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
|
| Initialize();
|
|
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> frame;
|
| +
|
| // The error is only raised on the second decode attempt, so we expect at
|
| // least one successful decode but we don't expect valid frame to be decoded.
|
| // During the second decode attempt an error is raised.
|
| - EXPECT_EQ(VideoDecoder::kOk, Decode(corrupt_i_frame_buffer_));
|
| - EXPECT_TRUE(output_frames_.empty());
|
| - EXPECT_EQ(VideoDecoder::kDecodeError, Decode(i_frame_buffer_));
|
| - EXPECT_TRUE(output_frames_.empty());
|
| + Decode(corrupt_i_frame_buffer_, &status, &frame);
|
| + DCHECK(!frame);
|
| + DCHECK_EQ(VideoDecoder::kNotEnoughData, status);
|
| + Decode(i_frame_buffer_, &status, &frame);
|
| + DCHECK(!frame);
|
| + DCHECK_EQ(VideoDecoder::kDecodeError, status);
|
|
|
| // After a decode error occurred, all following decodes will return
|
| // kDecodeError.
|
| - EXPECT_EQ(VideoDecoder::kDecodeError, Decode(i_frame_buffer_));
|
| - EXPECT_TRUE(output_frames_.empty());
|
| + Decode(i_frame_buffer_, &status, &frame);
|
| + DCHECK(!frame);
|
| + DCHECK_EQ(VideoDecoder::kDecodeError, status);
|
| }
|
|
|
| // Multi-threaded decoders have different behavior than single-threaded
|
| @@ -390,9 +418,13 @@
|
| TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
|
| Initialize();
|
|
|
| - EXPECT_EQ(VideoDecoder::kOk, DecodeSingleFrame(corrupt_i_frame_buffer_));
|
| - ASSERT_FALSE(output_frames_.empty());
|
| - EXPECT_TRUE(output_frames_.back()->end_of_stream());
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecodeSingleFrame(corrupt_i_frame_buffer_, &status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame.get());
|
| + EXPECT_TRUE(video_frame->end_of_stream());
|
| }
|
|
|
| // Decode |i_frame_buffer_| and then a frame with a larger width and verify
|
|
|