| Index: media/filters/ffmpeg_video_decoder_unittest.cc
|
| diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
|
| index f6ac974fc280555dd5b6d781a8e5e308efebea99..c3f5fd51a334fb744ace3804693f20ad9f8cd27c 100644
|
| --- a/media/filters/ffmpeg_video_decoder_unittest.cc
|
| +++ b/media/filters/ffmpeg_video_decoder_unittest.cc
|
| @@ -3,6 +3,7 @@
|
| // found in the LICENSE file.
|
|
|
| #include <string>
|
| +#include <vector>
|
|
|
| #include "base/bind.h"
|
| #include "base/callback_helpers.h"
|
| @@ -46,7 +47,6 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
| public:
|
| FFmpegVideoDecoderTest()
|
| : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
|
| - demuxer_(new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)),
|
| read_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
|
| base::Unretained(this))) {
|
| FFmpegGlue::InitializeFFmpeg();
|
| @@ -68,11 +68,9 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
|
|
| void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
|
| PipelineStatus status) {
|
| - demuxer_->set_video_decoder_config(config);
|
| - decoder_->Initialize(demuxer_.get(), NewExpectedStatusCB(status),
|
| + decoder_->Initialize(config, NewExpectedStatusCB(status),
|
| base::Bind(&MockStatisticsCB::OnStatistics,
|
| base::Unretained(&statistics_cb_)));
|
| -
|
| message_loop_.RunUntilIdle();
|
| }
|
|
|
| @@ -109,14 +107,58 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
| // Sets up expectations and actions to put FFmpegVideoDecoder in an end
|
| // of stream state.
|
| void EnterEndOfStreamState() {
|
| - scoped_refptr<VideoFrame> video_frame;
|
| VideoDecoder::Status status;
|
| - Read(&status, &video_frame);
|
| + 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->IsEndOfStream());
|
| }
|
|
|
| + typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
|
| + typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
|
| +
|
| + // 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,
|
| + OutputFrames* output_frames) {
|
| + InputBuffers::const_iterator input_iter = input_buffers.begin();
|
| +
|
| + for (;;) {
|
| + // Prepare input buffer.
|
| + scoped_refptr<DecoderBuffer> buffer;
|
| + if (input_iter != input_buffers.end()) {
|
| + buffer = *input_iter;
|
| + ++input_iter;
|
| + } else {
|
| + buffer = end_of_stream_buffer_;
|
| + }
|
| +
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> frame;
|
| + Decode(buffer, &status, &frame);
|
| +
|
| + switch (status) {
|
| + case VideoDecoder::kOk:
|
| + DCHECK(frame);
|
| + if (!frame->IsEndOfStream()) {
|
| + output_frames->push_back(frame);
|
| + continue;
|
| + } else { // EOS
|
| + return status;
|
| + }
|
| + case VideoDecoder::kNotEnoughData:
|
| + DCHECK(!frame);
|
| + continue;
|
| + case VideoDecoder::kDecodeError:
|
| + case VideoDecoder::kDecryptError:
|
| + DCHECK(!frame);
|
| + return status;
|
| + }
|
| + }
|
| + }
|
| +
|
| // 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
|
| @@ -124,13 +166,23 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
| void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
|
| VideoDecoder::Status* status,
|
| scoped_refptr<VideoFrame>* video_frame) {
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(ReturnBuffer(buffer))
|
| - .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
|
| + InputBuffers input_buffers;
|
| + input_buffers.push_back(buffer);
|
| +
|
| + if (!buffer->IsEndOfStream())
|
| + EXPECT_CALL(statistics_cb_, OnStatistics(_));
|
|
|
| - EXPECT_CALL(statistics_cb_, OnStatistics(_));
|
| + OutputFrames output_frames;
|
| + *status = DecodeMultipleFrames(input_buffers, &output_frames);
|
|
|
| - Read(status, video_frame);
|
| + 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::CreateEmptyFrame();
|
| }
|
|
|
| // Decodes |i_frame_buffer_| and then decodes the data contained in
|
| @@ -140,44 +192,40 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
| int expected_width,
|
| int expected_height) {
|
| Initialize();
|
| -
|
| - VideoDecoder::Status status_a;
|
| - VideoDecoder::Status status_b;
|
| - scoped_refptr<VideoFrame> video_frame_a;
|
| - scoped_refptr<VideoFrame> video_frame_b;
|
| -
|
| scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
|
|
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(ReturnBuffer(i_frame_buffer_))
|
| - .WillOnce(ReturnBuffer(buffer))
|
| - .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
|
| + InputBuffers input_buffers;
|
| + input_buffers.push_back(i_frame_buffer_);
|
| + input_buffers.push_back(buffer);
|
|
|
| EXPECT_CALL(statistics_cb_, OnStatistics(_))
|
| .Times(2);
|
|
|
| - Read(&status_a, &video_frame_a);
|
| - Read(&status_b, &video_frame_b);
|
| + OutputFrames output_frames;
|
| + VideoDecoder::Status status =
|
| + DecodeMultipleFrames(input_buffers, &output_frames);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_EQ(2U, output_frames.size());
|
|
|
| gfx::Size original_size = kVisibleRect.size();
|
| - EXPECT_EQ(VideoDecoder::kOk, status_a);
|
| - EXPECT_EQ(VideoDecoder::kOk, status_b);
|
| - ASSERT_TRUE(video_frame_a.get());
|
| - ASSERT_TRUE(video_frame_b.get());
|
| EXPECT_EQ(original_size.width(),
|
| - video_frame_a->visible_rect().size().width());
|
| + output_frames[0]->visible_rect().size().width());
|
| EXPECT_EQ(original_size.height(),
|
| - video_frame_a->visible_rect().size().height());
|
| - EXPECT_EQ(expected_width, video_frame_b->visible_rect().size().width());
|
| - EXPECT_EQ(expected_height, video_frame_b->visible_rect().size().height());
|
| + output_frames[0]->visible_rect().size().height());
|
| + EXPECT_EQ(expected_width,
|
| + output_frames[1]->visible_rect().size().width());
|
| + EXPECT_EQ(expected_height,
|
| + output_frames[1]->visible_rect().size().height());
|
| }
|
|
|
| - void Read(VideoDecoder::Status* status,
|
| - scoped_refptr<VideoFrame>* video_frame) {
|
| + 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_->Read(read_cb_);
|
| + decoder_->Decode(buffer, read_cb_);
|
|
|
| message_loop_.RunUntilIdle();
|
| }
|
| @@ -187,7 +235,6 @@ class FFmpegVideoDecoderTest : public testing::Test {
|
|
|
| base::MessageLoop message_loop_;
|
| scoped_ptr<FFmpegVideoDecoder> decoder_;
|
| - scoped_ptr<StrictMock<MockDemuxerStream> > demuxer_;
|
| MockStatisticsCB statistics_cb_;
|
|
|
| VideoDecoder::ReadCB read_cb_;
|
| @@ -213,7 +260,7 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedDecoder) {
|
| }
|
|
|
| TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedPixelFormat) {
|
| - // Ensure decoder handles unsupport pixel formats without crashing.
|
| + // Ensure decoder handles unsupported pixel formats without crashing.
|
| VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
|
| VideoFrame::INVALID,
|
| kCodedSize, kVisibleRect, kNaturalSize,
|
| @@ -331,63 +378,46 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
|
|
|
| scoped_refptr<DecoderBuffer> zero_byte_buffer = new DecoderBuffer(0);
|
|
|
| - VideoDecoder::Status status_a;
|
| - VideoDecoder::Status status_b;
|
| - VideoDecoder::Status status_c;
|
| - scoped_refptr<VideoFrame> video_frame_a;
|
| - scoped_refptr<VideoFrame> video_frame_b;
|
| - scoped_refptr<VideoFrame> video_frame_c;
|
| -
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(ReturnBuffer(i_frame_buffer_))
|
| - .WillOnce(ReturnBuffer(zero_byte_buffer))
|
| - .WillOnce(ReturnBuffer(i_frame_buffer_))
|
| - .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
|
| + InputBuffers input_buffers;
|
| + input_buffers.push_back(i_frame_buffer_);
|
| + input_buffers.push_back(zero_byte_buffer);
|
| + input_buffers.push_back(i_frame_buffer_);
|
|
|
| EXPECT_CALL(statistics_cb_, OnStatistics(_))
|
| .Times(2);
|
|
|
| - Read(&status_a, &video_frame_a);
|
| - Read(&status_b, &video_frame_b);
|
| - Read(&status_c, &video_frame_c);
|
| + OutputFrames output_frames;
|
| + VideoDecoder::Status status =
|
| + DecodeMultipleFrames(input_buffers, &output_frames);
|
|
|
| - EXPECT_EQ(VideoDecoder::kOk, status_a);
|
| - EXPECT_EQ(VideoDecoder::kOk, status_b);
|
| - EXPECT_EQ(VideoDecoder::kOk, status_c);
|
| -
|
| - ASSERT_TRUE(video_frame_a.get());
|
| - ASSERT_TRUE(video_frame_b.get());
|
| - ASSERT_TRUE(video_frame_c.get());
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_EQ(2U, output_frames.size());
|
|
|
| - EXPECT_FALSE(video_frame_a->IsEndOfStream());
|
| - EXPECT_FALSE(video_frame_b->IsEndOfStream());
|
| - EXPECT_TRUE(video_frame_c->IsEndOfStream());
|
| + EXPECT_FALSE(output_frames[0]->IsEndOfStream());
|
| + EXPECT_FALSE(output_frames[1]->IsEndOfStream());
|
| }
|
|
|
| TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
|
| Initialize();
|
|
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(ReturnBuffer(corrupt_i_frame_buffer_))
|
| - .WillRepeatedly(ReturnBuffer(i_frame_buffer_));
|
| + InputBuffers input_buffers;
|
| + input_buffers.push_back(corrupt_i_frame_buffer_);
|
| + input_buffers.push_back(i_frame_buffer_);
|
| + input_buffers.push_back(i_frame_buffer_);
|
|
|
| // The error is only raised on the second decode attempt, so we expect at
|
| // least one successful decode but we don't expect FrameReady() to be
|
| // executed as an error is raised instead.
|
| EXPECT_CALL(statistics_cb_, OnStatistics(_));
|
|
|
| - // Our read should still get satisfied with end of stream frame during an
|
| - // error.
|
| - VideoDecoder::Status status;
|
| - scoped_refptr<VideoFrame> video_frame;
|
| - Read(&status, &video_frame);
|
| - EXPECT_EQ(VideoDecoder::kDecodeError, status);
|
| - EXPECT_FALSE(video_frame.get());
|
| + OutputFrames output_frames;
|
| + VideoDecoder::Status status =
|
| + DecodeMultipleFrames(input_buffers, &output_frames);
|
|
|
| - // After a decode error occurred, all following read will return kDecodeError.
|
| - Read(&status, &video_frame);
|
| EXPECT_EQ(VideoDecoder::kDecodeError, status);
|
| - EXPECT_FALSE(video_frame.get());
|
| + // After a decode error occurred, all following decodes will return
|
| + // kDecodeError. Therefore, no |output_frames| will be available.
|
| + ASSERT_TRUE(output_frames.empty());
|
| }
|
|
|
| // Multi-threaded decoders have different behavior than single-threaded
|
| @@ -452,25 +482,6 @@ TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
|
| Reset();
|
| }
|
|
|
| -// Test resetting when there is a pending read on the demuxer.
|
| -TEST_F(FFmpegVideoDecoderTest, Reset_DuringPendingRead) {
|
| - Initialize();
|
| -
|
| - // Request a read on the decoder and ensure the demuxer has been called.
|
| - DemuxerStream::ReadCB read_cb;
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(SaveArg<0>(&read_cb));
|
| - decoder_->Read(read_cb_);
|
| - ASSERT_FALSE(read_cb.is_null());
|
| -
|
| - // Reset the decoder.
|
| - Reset();
|
| -
|
| - EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| -
|
| - read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
|
| -}
|
| -
|
| // Test stopping when decoder has initialized but not decoded.
|
| TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
|
| Initialize();
|
| @@ -492,77 +503,4 @@ TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
|
| Stop();
|
| }
|
|
|
| -// Test stopping when there is a pending read on the demuxer.
|
| -TEST_F(FFmpegVideoDecoderTest, Stop_DuringPendingRead) {
|
| - Initialize();
|
| -
|
| - // Request a read on the decoder and ensure the demuxer has been called.
|
| - DemuxerStream::ReadCB read_cb;
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(SaveArg<0>(&read_cb));
|
| - decoder_->Read(read_cb_);
|
| - ASSERT_FALSE(read_cb.is_null());
|
| -
|
| - EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| -
|
| - Stop();
|
| -
|
| - read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
|
| -}
|
| -
|
| -// Test stopping when there is a pending read on the demuxer.
|
| -TEST_F(FFmpegVideoDecoderTest, Stop_DuringPendingReadAndReset) {
|
| - Initialize();
|
| -
|
| - // Request a read on the decoder and ensure the demuxer has been called.
|
| - DemuxerStream::ReadCB read_cb;
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(SaveArg<0>(&read_cb));
|
| - decoder_->Read(read_cb_);
|
| - ASSERT_FALSE(read_cb.is_null());
|
| -
|
| - Reset();
|
| -
|
| - EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| - Stop();
|
| -
|
| - read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
|
| -}
|
| -
|
| -// Test aborted read on the demuxer stream.
|
| -TEST_F(FFmpegVideoDecoderTest, DemuxerRead_Aborted) {
|
| - Initialize();
|
| -
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>()));
|
| -
|
| - VideoDecoder::Status status;
|
| - scoped_refptr<VideoFrame> video_frame;
|
| -
|
| - Read(&status, &video_frame);
|
| -
|
| - EXPECT_EQ(VideoDecoder::kOk, status);
|
| - EXPECT_FALSE(video_frame.get());
|
| -}
|
| -
|
| -// Test aborted read on the demuxer stream during pending reset.
|
| -TEST_F(FFmpegVideoDecoderTest, DemuxerRead_AbortedDuringReset) {
|
| - Initialize();
|
| -
|
| - // Request a read on the decoder and ensure the demuxer has been called.
|
| - DemuxerStream::ReadCB read_cb;
|
| - EXPECT_CALL(*demuxer_, Read(_))
|
| - .WillOnce(SaveArg<0>(&read_cb));
|
| - decoder_->Read(read_cb_);
|
| - ASSERT_FALSE(read_cb.is_null());
|
| -
|
| - // Reset while there is still an outstanding read on the demuxer.
|
| - Reset();
|
| -
|
| - // Signal an aborted demuxer read: a NULL video frame should be returned.
|
| - EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| - read_cb.Run(DemuxerStream::kAborted, NULL);
|
| - message_loop_.RunUntilIdle();
|
| -}
|
| -
|
| } // namespace media
|
|
|