summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-28 03:27:25 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-28 03:27:25 +0000
commit0cd3abfaa5e7248db27b0fd3e0c0ee8e47299eac (patch)
tree82119d1efe7d26f8af1ff29fcc026824ba9a178e /media
parentcc97e48a8bfc2fdb5c3c2c8dc7204b33ff86b56d (diff)
downloadchromium_src-0cd3abfaa5e7248db27b0fd3e0c0ee8e47299eac.zip
chromium_src-0cd3abfaa5e7248db27b0fd3e0c0ee8e47299eac.tar.gz
chromium_src-0cd3abfaa5e7248db27b0fd3e0c0ee8e47299eac.tar.bz2
Rewrite FFmpegVideoDecoder tests based on existing FFmpegVideoDecodeEngine tests.
The old tests were mostly useless as they tested using a mocked VideoDecodeEngine. Review URL: http://codereview.chromium.org/8340008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107681 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc733
1 files changed, 434 insertions, 299 deletions
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index a2ba238..bae062f 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -10,389 +10,524 @@
#include "base/string_util.h"
#include "media/base/data_buffer.h"
#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"
#include "media/ffmpeg/ffmpeg_common.h"
+#include "media/filters/ffmpeg_glue.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "media/video/video_decode_engine.h"
#include "media/video/video_decode_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::AnyNumber;
-using ::testing::DoAll;
-using ::testing::Message;
-using ::testing::Return;
-using ::testing::ReturnNull;
+using ::testing::Invoke;
using ::testing::ReturnRef;
-using ::testing::SetArgumentPointee;
+using ::testing::SaveArg;
using ::testing::StrictMock;
-using ::testing::WithArg;
-using ::testing::Invoke;
namespace media {
static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
-static const gfx::Size kCodedSize(1280, 720);
-static const gfx::Rect kVisibleRect(1280, 720);
-static const gfx::Size kNaturalSize(1280, 720);
+static const gfx::Size kCodedSize(320, 240);
+static const gfx::Rect kVisibleRect(320, 240);
+static const gfx::Size kNaturalSize(522, 288);
static const AVRational kFrameRate = { 100, 1 };
static const AVRational kAspectRatio = { 1, 1 };
-// Holds timestamp and duration data needed for properly enqueuing a frame.
-struct TimeTuple {
- base::TimeDelta timestamp;
- base::TimeDelta duration;
-};
+ACTION_P(ReturnBuffer, buffer) {
+ arg0.Run(buffer);
+}
-static const TimeTuple kTestPts1 =
- { base::TimeDelta::FromMicroseconds(123),
- base::TimeDelta::FromMicroseconds(50) };
-static const TimeTuple kTestPts2 =
- { base::TimeDelta::FromMicroseconds(456),
- base::TimeDelta::FromMicroseconds(60) };
-static const TimeTuple kTestPts3 =
- { base::TimeDelta::FromMicroseconds(789),
- base::TimeDelta::FromMicroseconds(60) };
-static const PipelineStatistics kStatistics;
-
-// TODO(hclam): Share this in a separate file.
-class MockVideoDecodeEngine : public VideoDecodeEngine {
+class FFmpegVideoDecoderTest : public testing::Test {
public:
- MOCK_METHOD4(Initialize, void(MessageLoop* message_loop,
- VideoDecodeEngine::EventHandler* event_handler,
- VideoDecodeContext* context,
- const VideoDecoderConfig& config));
- MOCK_METHOD1(ConsumeVideoSample, void(scoped_refptr<Buffer> buffer));
- MOCK_METHOD1(ProduceVideoFrame, void(scoped_refptr<VideoFrame> buffer));
- MOCK_METHOD0(Uninitialize, void());
- MOCK_METHOD0(Flush, void());
- MOCK_METHOD0(Seek, void());
-
- MockVideoDecodeEngine() : event_handler_(NULL) {}
-
- VideoDecodeEngine::EventHandler* event_handler_;
-};
+ FFmpegVideoDecoderTest()
+ : decoder_(new FFmpegVideoDecoder(&message_loop_, NULL)),
+ demuxer_(new StrictMock<MockDemuxerStream>()) {
+ CHECK(FFmpegGlue::GetInstance());
-// Class that just mocks the private functions.
-class DecoderPrivateMock : public FFmpegVideoDecoder {
- public:
- DecoderPrivateMock(MessageLoop* message_loop,
- VideoDecodeContext* context)
- : FFmpegVideoDecoder(message_loop, context) {
+ decoder_->set_host(&host_);
+ decoder_->set_consume_video_frame_callback(base::Bind(
+ &FFmpegVideoDecoderTest::ConsumeVideoFrame, base::Unretained(this)));
+
+ // Initialize various test buffers.
+ frame_buffer_.reset(new uint8[kCodedSize.GetArea()]);
+ end_of_stream_buffer_ = new DataBuffer(0);
+ ReadTestDataFile("vp8-I-frame-320x240", &i_frame_buffer_);
+ ReadTestDataFile("vp8-corrupt-I-frame", &corrupt_i_frame_buffer_);
+
+ config_.Initialize(kCodecVP8, kVideoFormat, kCodedSize, kVisibleRect,
+ kFrameRate.num, kFrameRate.den,
+ kAspectRatio.num, kAspectRatio.den,
+ NULL, 0);
}
- // change access qualifier for test: used in actions.
- void ProduceVideoSample(scoped_refptr<Buffer> buffer) {
- FFmpegVideoDecoder::ProduceVideoSample(buffer);
+ virtual ~FFmpegVideoDecoderTest() {}
+
+ void Initialize() {
+ InitializeWithConfig(config_);
}
- void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame,
- const PipelineStatistics& statistics) {
- FFmpegVideoDecoder::ConsumeVideoFrame(frame, statistics);
+
+ void InitializeWithConfig(const VideoDecoderConfig& config) {
+ EXPECT_CALL(*demuxer_, video_decoder_config())
+ .WillOnce(ReturnRef(config));
+
+ decoder_->Initialize(demuxer_, NewExpectedClosure(),
+ base::Bind(&MockStatisticsCallback::OnStatistics,
+ base::Unretained(&statistics_callback_)));
+
+ message_loop_.RunAllPending();
}
- void OnReadComplete(Buffer* buffer) {
- FFmpegVideoDecoder::OnReadComplete(buffer);
+
+ void Pause() {
+ decoder_->Pause(NewExpectedClosure());
+ message_loop_.RunAllPending();
}
-};
-ACTION_P2(EngineInitialize, engine, success) {
- engine->event_handler_ = arg1;
- engine->event_handler_->OnInitializeComplete(success);
-}
+ void Flush() {
+ decoder_->Flush(NewExpectedClosure());
+ message_loop_.RunAllPending();
+ }
-ACTION_P(EngineUninitialize, engine) {
- if (engine->event_handler_)
- engine->event_handler_->OnUninitializeComplete();
-}
+ void Seek(int64 timestamp) {
+ decoder_->Seek(base::TimeDelta::FromMicroseconds(timestamp),
+ NewExpectedStatusCB(PIPELINE_OK));
+ message_loop_.RunAllPending();
+ }
-ACTION_P(EngineFlush, engine) {
- if (engine->event_handler_)
- engine->event_handler_->OnFlushComplete();
-}
+ void Stop() {
+ decoder_->Stop(NewExpectedClosure());
+ message_loop_.RunAllPending();
+ }
-ACTION_P(EngineSeek, engine) {
- if (engine->event_handler_)
- engine->event_handler_->OnSeekComplete();
-}
+ // Sets up expectations for FFmpegVideoDecodeEngine to preroll after
+ // receiving a Seek(). The adjustment on Read() is due to the decoder
+ // delaying frame output.
+ //
+ // TODO(scherkus): this is madness -- there's no reason for a decoder to
+ // assume it should preroll anything.
+ void ExpectSeekPreroll() {
+ EXPECT_CALL(*demuxer_, Read(_))
+ .Times(Limits::kMaxVideoFrames + 1)
+ .WillRepeatedly(ReturnBuffer(i_frame_buffer_));
+ EXPECT_CALL(statistics_callback_, OnStatistics(_))
+ .Times(Limits::kMaxVideoFrames);
+ EXPECT_CALL(*this, ConsumeVideoFrame(_))
+ .Times(Limits::kMaxVideoFrames);
+ }
-// Fixture class to facilitate writing tests. Takes care of setting up the
-// FFmpeg, pipeline and filter host mocks.
-class FFmpegVideoDecoderTest : public testing::Test {
- protected:
- FFmpegVideoDecoderTest() {
- // Create an FFmpegVideoDecoder, and MockVideoDecodeEngine.
- //
- // TODO(ajwong): Break the test's dependency on FFmpegVideoDecoder.
- decoder_ = new DecoderPrivateMock(&message_loop_, NULL);
- renderer_ = new MockVideoRenderer();
- engine_ = new StrictMock<MockVideoDecodeEngine>();
-
- // Inject mocks and prepare a demuxer stream.
- decoder_->set_host(&host_);
- decoder_->set_consume_video_frame_callback(
- base::Bind(&MockVideoRenderer::ConsumeVideoFrame,
- base::Unretained(renderer_.get())));
- decoder_->SetVideoDecodeEngineForTest(engine_);
- demuxer_ = new StrictMock<MockDemuxerStream>();
-
- // Initialize FFmpeg fixtures.
- memset(&yuv_frame_, 0, sizeof(yuv_frame_));
- base::TimeDelta zero;
- video_frame_ = VideoFrame::CreateFrame(VideoFrame::YV12,
- kVisibleRect.width(),
- kVisibleRect.height(),
- zero, zero);
- buffer_ = new DataBuffer(1);
- end_of_stream_buffer_ = new DataBuffer(0);
+ // Sets up expectations for FFmpegVideoDecodeEngine to preroll after
+ // receiving a Seek() but for the end of stream case.
+ //
+ // TODO(scherkus): this is madness -- there's no reason for a decoder to
+ // assume it should preroll anything.
+ void ExpectSeekPrerollEndOfStream() {
+ EXPECT_CALL(*demuxer_, Read(_))
+ .Times(Limits::kMaxVideoFrames)
+ .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+ EXPECT_CALL(statistics_callback_, OnStatistics(_))
+ .Times(Limits::kMaxVideoFrames);
+ }
- EXPECT_CALL(stats_callback_object_, OnStatistics(_))
- .Times(AnyNumber());
+ // Sets up expectations and actions to put FFmpegVideoDecoder in an active
+ // decoding state.
+ void EnterDecodingState() {
+ scoped_refptr<VideoFrame> video_frame;
+ DecodeSingleFrame(i_frame_buffer_, &video_frame);
- config_.Initialize(kCodecVP8, kVideoFormat, kCodedSize, kVisibleRect,
- kFrameRate.num, kFrameRate.den,
- kAspectRatio.num, kAspectRatio.den,
- NULL, 0);
+ ASSERT_TRUE(video_frame);
+ EXPECT_FALSE(video_frame->IsEndOfStream());
}
- virtual ~FFmpegVideoDecoderTest() {
- // The presence of an event handler means we need to uninitialize.
- if (engine_->event_handler_) {
- EXPECT_CALL(*engine_, Uninitialize())
- .WillOnce(EngineUninitialize(engine_));
- }
+ // Sets up expectations and actions to put FFmpegVideoDecoder in an end
+ // of stream state.
+ void EnterEndOfStreamState() {
+ EXPECT_CALL(statistics_callback_, OnStatistics(_));
- decoder_->Stop(NewExpectedClosure());
+ scoped_refptr<VideoFrame> video_frame;
+ CallProduceVideoFrame(&video_frame);
+ ASSERT_TRUE(video_frame);
+ EXPECT_TRUE(video_frame->IsEndOfStream());
+ }
- // Finish up any remaining tasks.
- message_loop_.RunAllPending();
+ // 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.
+ void DecodeSingleFrame(const scoped_refptr<Buffer>& buffer,
+ scoped_refptr<VideoFrame>* video_frame) {
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(buffer))
+ .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+
+ EXPECT_CALL(statistics_callback_, OnStatistics(_));
+
+ CallProduceVideoFrame(video_frame);
}
- void InitializeDecoderSuccessfully() {
- EXPECT_CALL(*demuxer_, video_decoder_config())
- .WillOnce(ReturnRef(config_));
+ // Decodes |i_frame_buffer_| and then decodes the data contained in
+ // the file named |test_file_name|. This function expects both buffers
+ // to decode to frames that are the same size.
+ void DecodeIFrameThenTestFile(const std::string& test_file_name) {
+ Initialize();
+
+ scoped_refptr<VideoFrame> video_frame_a;
+ scoped_refptr<VideoFrame> video_frame_b;
+
+ scoped_refptr<Buffer> buffer;
+ ReadTestDataFile(test_file_name, &buffer);
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(i_frame_buffer_))
+ .WillOnce(ReturnBuffer(buffer))
+ .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+
+ EXPECT_CALL(statistics_callback_, OnStatistics(_))
+ .Times(2);
+
+ CallProduceVideoFrame(&video_frame_a);
+ CallProduceVideoFrame(&video_frame_b);
+
+ size_t expected_width = static_cast<size_t>(kVisibleRect.width());
+ size_t expected_height = static_cast<size_t>(kVisibleRect.height());
+
+ ASSERT_TRUE(video_frame_a);
+ ASSERT_TRUE(video_frame_b);
+ EXPECT_EQ(expected_width, video_frame_a->width());
+ EXPECT_EQ(expected_height, video_frame_a->height());
+ EXPECT_EQ(expected_width, video_frame_b->width());
+ EXPECT_EQ(expected_height, video_frame_b->height());
+ }
- EXPECT_CALL(*engine_, Initialize(_, _, _, _))
- .WillOnce(EngineInitialize(engine_, true));
+ void CallProduceVideoFrame(scoped_refptr<VideoFrame>* video_frame) {
+ EXPECT_CALL(*this, ConsumeVideoFrame(_))
+ .WillOnce(SaveArg<0>(video_frame));
+
+ decoder_->ProduceVideoFrame(VideoFrame::CreateFrame(
+ VideoFrame::YV12, kVisibleRect.width(), kVisibleRect.height(),
+ kNoTimestamp, kNoTimestamp));
- decoder_->Initialize(demuxer_,
- NewExpectedClosure(), NewStatisticsCallback());
message_loop_.RunAllPending();
}
- StatisticsCallback NewStatisticsCallback() {
- return base::Bind(&MockStatisticsCallback::OnStatistics,
- base::Unretained(&stats_callback_object_));
+ void SetupTimestampTest() {
+ Initialize();
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(Invoke(this, &FFmpegVideoDecoderTest::ReadTimestamp));
+ EXPECT_CALL(statistics_callback_, OnStatistics(_))
+ .Times(AnyNumber());
+ }
+
+ void PushTimestamp(int64 timestamp) {
+ timestamps_.push_back(timestamp);
+ }
+
+ int64 PopTimestamp() {
+ scoped_refptr<VideoFrame> video_frame;
+ CallProduceVideoFrame(&video_frame);
+
+ return video_frame->GetTimestamp().InMicroseconds();
+ }
+
+ void ReadTimestamp(const DemuxerStream::ReadCallback& read_callback) {
+ if (timestamps_.empty()) {
+ read_callback.Run(end_of_stream_buffer_);
+ return;
+ }
+
+ i_frame_buffer_->SetTimestamp(
+ base::TimeDelta::FromMicroseconds(timestamps_.front()));
+ timestamps_.pop_front();
+ read_callback.Run(i_frame_buffer_);
}
- // Fixture members.
- MockVideoDecodeEngine* engine_; // Owned by |decoder_|.
- scoped_refptr<DecoderPrivateMock> decoder_;
- scoped_refptr<MockVideoRenderer> renderer_;
+ MOCK_METHOD1(ConsumeVideoFrame, void(scoped_refptr<VideoFrame>));
+
+ MessageLoop message_loop_;
+ scoped_refptr<FFmpegVideoDecoder> decoder_;
scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_;
- scoped_refptr<DataBuffer> buffer_;
- scoped_refptr<DataBuffer> end_of_stream_buffer_;
- MockStatisticsCallback stats_callback_object_;
+ MockStatisticsCallback statistics_callback_;
StrictMock<MockFilterHost> host_;
- MessageLoop message_loop_;
+ VideoDecoderConfig config_;
- // FFmpeg fixtures.
- AVFrame yuv_frame_;
- scoped_refptr<VideoFrame> video_frame_;
+ // Various buffers for testing.
+ scoped_array<uint8_t> frame_buffer_;
+ scoped_refptr<Buffer> end_of_stream_buffer_;
+ scoped_refptr<Buffer> i_frame_buffer_;
+ scoped_refptr<Buffer> corrupt_i_frame_buffer_;
- VideoDecoderConfig config_;
+ // Used for generating timestamped buffers.
+ std::deque<int64> timestamps_;
private:
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
};
-TEST_F(FFmpegVideoDecoderTest, Initialize_EngineFails) {
- EXPECT_CALL(*demuxer_, video_decoder_config())
- .WillOnce(ReturnRef(config_));
+TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
+ Initialize();
+}
- EXPECT_CALL(*engine_, Initialize(_, _, _, _))
- .WillOnce(EngineInitialize(engine_, false));
+TEST_F(FFmpegVideoDecoderTest, Initialize_FindDecoderFails) {
+ // Test avcodec_find_decoder() returning NULL.
+ VideoDecoderConfig config(kUnknownVideoCodec, kVideoFormat,
+ kCodedSize, kVisibleRect,
+ kFrameRate.num, kFrameRate.den,
+ kAspectRatio.num, kAspectRatio.den,
+ NULL, 0);
EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
+ InitializeWithConfig(config);
+}
- decoder_->Initialize(demuxer_,
- NewExpectedClosure(), NewStatisticsCallback());
- message_loop_.RunAllPending();
+TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
+ // Specify Theora w/o extra data so that avcodec_open() fails.
+ VideoDecoderConfig config(kCodecTheora, kVideoFormat,
+ kCodedSize, kVisibleRect,
+ kFrameRate.num, kFrameRate.den,
+ kAspectRatio.num, kAspectRatio.den,
+ NULL, 0);
+
+ EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
+ InitializeWithConfig(config);
}
-TEST_F(FFmpegVideoDecoderTest, Initialize_Successful) {
- InitializeDecoderSuccessfully();
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
+ Initialize();
- // Test that the uncompressed video surface matches the dimensions
- // specified by FFmpeg.
- EXPECT_EQ(kNaturalSize, decoder_->natural_size());
+ // Simulate decoding a single frame.
+ scoped_refptr<VideoFrame> video_frame;
+ DecodeSingleFrame(i_frame_buffer_, &video_frame);
+
+ ASSERT_TRUE(video_frame);
+ EXPECT_FALSE(video_frame->IsEndOfStream());
}
-TEST_F(FFmpegVideoDecoderTest, OnError) {
- InitializeDecoderSuccessfully();
+// Verify current behavior for 0 byte frames. FFmpeg simply ignores
+// the 0 byte frames.
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
+ Initialize();
+
+ scoped_refptr<DataBuffer> zero_byte_buffer = new DataBuffer(1);
+
+ 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_));
- scoped_refptr<VideoFrame> null_frame;
- EXPECT_CALL(*renderer_, ConsumeVideoFrame(null_frame));
- engine_->event_handler_->OnError();
+ EXPECT_CALL(statistics_callback_, OnStatistics(_))
+ .Times(3);
+
+ CallProduceVideoFrame(&video_frame_a);
+ CallProduceVideoFrame(&video_frame_b);
+ CallProduceVideoFrame(&video_frame_c);
+
+ ASSERT_TRUE(video_frame_a);
+ ASSERT_TRUE(video_frame_b);
+ ASSERT_TRUE(video_frame_a);
+
+ EXPECT_FALSE(video_frame_a->IsEndOfStream());
+ EXPECT_FALSE(video_frame_b->IsEndOfStream());
+ EXPECT_TRUE(video_frame_c->IsEndOfStream());
}
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(corrupt_i_frame_buffer_))
+ .WillRepeatedly(ReturnBuffer(i_frame_buffer_));
-ACTION_P2(ReadFromDemux, decoder, buffer) {
- decoder->ProduceVideoSample(buffer);
+ scoped_refptr<VideoFrame> video_frame;
+ CallProduceVideoFrame(&video_frame);
+
+ // XXX: SERIOUSLY? This seems broken to call NULL on decoder error.
+ EXPECT_FALSE(video_frame);
}
-ACTION_P3(ReturnFromDemux, decoder, buffer, time_tuple) {
- buffer->SetTimestamp(time_tuple.timestamp);
- buffer->SetDuration(time_tuple.duration);
- decoder->OnReadComplete(buffer);
+// Multi-threaded decoders have different behavior than single-threaded
+// decoders at the end of the stream. Multithreaded decoders hide errors
+// that happen on the last |codec_context_->thread_count| frames to avoid
+// prematurely signalling EOS. This test just exposes that behavior so we can
+// detect if it changes.
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
+ Initialize();
+
+ scoped_refptr<VideoFrame> video_frame;
+ DecodeSingleFrame(corrupt_i_frame_buffer_, &video_frame);
+
+ ASSERT_TRUE(video_frame);
+ EXPECT_TRUE(video_frame->IsEndOfStream());
}
-ACTION_P4(DecodeComplete, decoder, video_frame, time_tuple, statistics) {
- video_frame->SetTimestamp(time_tuple.timestamp);
- video_frame->SetDuration(time_tuple.duration);
- decoder->ConsumeVideoFrame(video_frame, statistics);
+// Decode |i_frame_buffer_| and then a frame with a larger width and verify
+// the output size didn't change.
+// TODO(acolwell): Fix InvalidRead detected by Valgrind
+//TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
+// DecodeIFrameThenTestFile("vp8-I-frame-640x240");
+//}
+
+// Decode |i_frame_buffer_| and then a frame with a smaller width and verify
+// the output size didn't change.
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
+ DecodeIFrameThenTestFile("vp8-I-frame-160x240");
}
-ACTION_P3(DecodeNotComplete, decoder, buffer, statistics) {
- scoped_refptr<VideoFrame> null_frame;
- if (buffer->IsEndOfStream()) // We had started flushing.
- decoder->ConsumeVideoFrame(null_frame, statistics);
- else
- decoder->ProduceVideoSample(buffer);
+// Decode |i_frame_buffer_| and then a frame with a larger height and verify
+// the output size didn't change.
+// TODO(acolwell): Fix InvalidRead detected by Valgrind
+//TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
+// DecodeIFrameThenTestFile("vp8-I-frame-320x480");
+//}
+
+// Decode |i_frame_buffer_| and then a frame with a smaller height and verify
+// the output size didn't change.
+TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
+ DecodeIFrameThenTestFile("vp8-I-frame-320x120");
}
-ACTION_P(ConsumePTS, pts_heap) {
- pts_heap->Pop();
+// Test pausing when decoder has initialized but not decoded.
+TEST_F(FFmpegVideoDecoderTest, Pause_Initialized) {
+ Initialize();
+ Pause();
}
-TEST_F(FFmpegVideoDecoderTest, DoDecode_TestStateTransition) {
- // Simulates a input sequence of three buffers, and six decode requests to
- // exercise the state transitions, and bookkeeping logic of DoDecode.
- //
- // We try to verify the following:
- // 1) Non-EoS buffer timestamps are pushed into the pts_heap.
- // 2) Timestamps are popped for each decoded frame.
- // 3) The last_pts_ is updated for each decoded frame.
- // 4) kDecodeFinished is never left regardless of what kind of buffer is
- // given.
- // 5) All state transitions happen as expected.
- InitializeDecoderSuccessfully();
-
- // Setup initial state and check that it is sane.
- ASSERT_EQ(FFmpegVideoDecoder::kNormal, decoder_->state_);
- ASSERT_TRUE(base::TimeDelta() == decoder_->pts_stream_.current_pts());
- ASSERT_TRUE(base::TimeDelta() == decoder_->pts_stream_.current_duration());
-
- // 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(*engine_, ProduceVideoFrame(_))
- .Times(4)
- .WillRepeatedly(ReadFromDemux(decoder_.get(), buffer_));
- EXPECT_CALL(*demuxer_.get(), Read(_))
- .Times(6)
- .WillOnce(ReturnFromDemux(decoder_.get(), buffer_, kTestPts1))
- .WillOnce(ReturnFromDemux(decoder_.get(), buffer_, kTestPts3))
- .WillOnce(ReturnFromDemux(decoder_.get(), buffer_, kTestPts2))
- .WillOnce(ReturnFromDemux(decoder_.get(),
- end_of_stream_buffer_, kTestPts3))
- .WillOnce(ReturnFromDemux(decoder_.get(),
- end_of_stream_buffer_, kTestPts3))
- .WillOnce(ReturnFromDemux(decoder_.get(),
- end_of_stream_buffer_, kTestPts3));
- EXPECT_CALL(*engine_, ConsumeVideoSample(_))
- .WillOnce(DecodeNotComplete(decoder_.get(), buffer_, kStatistics))
- .WillOnce(DecodeComplete(decoder_.get(),
- video_frame_, kTestPts1, kStatistics))
- .WillOnce(DecodeNotComplete(decoder_.get(),
- buffer_, kStatistics))
- .WillOnce(DecodeComplete(decoder_.get(),
- video_frame_, kTestPts2, kStatistics))
- .WillOnce(DecodeComplete(decoder_.get(),
- video_frame_, kTestPts3, kStatistics))
- .WillOnce(DecodeNotComplete(decoder_.get(),
- end_of_stream_buffer_, kStatistics));
- EXPECT_CALL(*renderer_.get(), ConsumeVideoFrame(_))
- .Times(4);
- EXPECT_CALL(stats_callback_object_, OnStatistics(_))
- .Times(4);
-
- // First request from renderer: at first round decode engine did not produce
- // any frame. Decoder will issue another read from demuxer. at second round
- // decode engine will get a valid frame.
- decoder_->ProduceVideoFrame(video_frame_);
- message_loop_.RunAllPending();
- EXPECT_EQ(FFmpegVideoDecoder::kNormal, decoder_->state_);
- ASSERT_TRUE(kTestPts1.timestamp == decoder_->pts_stream_.current_pts());
- ASSERT_TRUE(kTestPts1.duration == decoder_->pts_stream_.current_duration());
-
- // Second request from renderer: at first round decode engine did not produce
- // any frame. Decoder will issue another read from demuxer. at second round
- // decode engine will get a valid frame.
- decoder_->ProduceVideoFrame(video_frame_);
- message_loop_.RunAllPending();
- EXPECT_EQ(FFmpegVideoDecoder::kFlushCodec, decoder_->state_);
- EXPECT_TRUE(kTestPts2.timestamp == decoder_->pts_stream_.current_pts());
- EXPECT_TRUE(kTestPts2.duration == decoder_->pts_stream_.current_duration());
-
- // Third request from renderer: decode engine will return frame on the
- // first round. Input stream had reach EOS, therefore we had entered
- // kFlushCodec state after this call.
- decoder_->ProduceVideoFrame(video_frame_);
- message_loop_.RunAllPending();
- EXPECT_EQ(FFmpegVideoDecoder::kFlushCodec, decoder_->state_);
- EXPECT_TRUE(kTestPts3.timestamp == decoder_->pts_stream_.current_pts());
- EXPECT_TRUE(kTestPts3.duration == decoder_->pts_stream_.current_duration());
-
- // Fourth request from renderer: Both input/output reach EOF. therefore
- // we had reached the kDecodeFinished state after this call.
- decoder_->ProduceVideoFrame(video_frame_);
- message_loop_.RunAllPending();
- EXPECT_EQ(FFmpegVideoDecoder::kDecodeFinished, decoder_->state_);
- EXPECT_TRUE(kTestPts3.timestamp == decoder_->pts_stream_.current_pts());
- EXPECT_TRUE(kTestPts3.duration == decoder_->pts_stream_.current_duration());
+// Test pausing when decoder has decoded single frame.
+TEST_F(FFmpegVideoDecoderTest, Pause_Decoding) {
+ Initialize();
+ EnterDecodingState();
+ Pause();
}
-TEST_F(FFmpegVideoDecoderTest, DoSeek) {
- // Simulates receiving a call to DoSeek() while in every possible state. In
- // every case, it should clear the timestamp queue, flush the decoder and
- // reset the state to kNormal.
- const base::TimeDelta kZero;
- const FFmpegVideoDecoder::DecoderState kStates[] = {
- FFmpegVideoDecoder::kNormal,
- FFmpegVideoDecoder::kFlushCodec,
- FFmpegVideoDecoder::kDecodeFinished,
- FFmpegVideoDecoder::kStopped,
- };
-
- InitializeDecoderSuccessfully();
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kStates); ++i) {
- SCOPED_TRACE(Message() << "Iteration " << i);
-
- // Push in some timestamps.
- buffer_->SetTimestamp(kTestPts1.timestamp);
- decoder_->pts_stream_.EnqueuePts(buffer_);
- buffer_->SetTimestamp(kTestPts2.timestamp);
- decoder_->pts_stream_.EnqueuePts(buffer_);
- buffer_->SetTimestamp(kTestPts3.timestamp);
- decoder_->pts_stream_.EnqueuePts(buffer_);
-
- decoder_->state_ = kStates[i];
-
- // Expect a flush.
- EXPECT_CALL(*engine_, Flush())
- .WillOnce(EngineFlush(engine_));
- decoder_->Flush(NewExpectedClosure());
+// Test pausing when decoder has hit end of stream.
+TEST_F(FFmpegVideoDecoderTest, Pause_EndOfStream) {
+ Initialize();
+ EnterDecodingState();
+ EnterEndOfStreamState();
+ Pause();
+}
- // Expect Seek and verify the results.
- EXPECT_CALL(*engine_, Seek())
- .WillOnce(EngineSeek(engine_));
- decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK));
+// Test flushing when decoder has initialized but not decoded.
+TEST_F(FFmpegVideoDecoderTest, Flush_Initialized) {
+ Initialize();
+ Flush();
+}
- EXPECT_TRUE(kZero == decoder_->pts_stream_.current_duration());
- EXPECT_EQ(FFmpegVideoDecoder::kNormal, decoder_->state_);
- }
+// Test flushing when decoder has decoded single frame.
+TEST_F(FFmpegVideoDecoderTest, Flush_Decoding) {
+ Initialize();
+ EnterDecodingState();
+ Flush();
+}
+
+// Test flushing when decoder has hit end of stream.
+//
+// TODO(scherkus): test is disabled until we clean up buffer recycling.
+TEST_F(FFmpegVideoDecoderTest, DISABLED_Flush_EndOfStream) {
+ Initialize();
+ EnterDecodingState();
+ EnterEndOfStreamState();
+ Flush();
+}
+
+// Test seeking when decoder has initialized but not decoded.
+TEST_F(FFmpegVideoDecoderTest, Seek_Initialized) {
+ Initialize();
+ ExpectSeekPreroll();
+ Seek(1000);
+}
+
+// Test seeking when decoder has decoded single frame.
+TEST_F(FFmpegVideoDecoderTest, Seek_Decoding) {
+ Initialize();
+ EnterDecodingState();
+ ExpectSeekPreroll();
+ Seek(1000);
+}
+
+// Test seeking when decoder has hit end of stream.
+TEST_F(FFmpegVideoDecoderTest, Seek_EndOfStream) {
+ Initialize();
+ EnterDecodingState();
+ EnterEndOfStreamState();
+ ExpectSeekPrerollEndOfStream();
+ Seek(1000);
+}
+
+// Test stopping when decoder has initialized but not decoded.
+TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
+ Initialize();
+ Stop();
+}
+
+// Test stopping when decoder has decoded single frame.
+TEST_F(FFmpegVideoDecoderTest, Stop_Decoding) {
+ Initialize();
+ EnterDecodingState();
+ Stop();
+}
+
+// Test stopping when decoder has hit end of stream.
+TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
+ Initialize();
+ EnterDecodingState();
+ EnterEndOfStreamState();
+ Stop();
+}
+
+// Test normal operation of timestamping where all input has valid timestamps.
+TEST_F(FFmpegVideoDecoderTest, Timestamps_Normal) {
+ SetupTimestampTest();
+
+ PushTimestamp(0);
+ PushTimestamp(1000);
+ PushTimestamp(2000);
+ PushTimestamp(3000);
+
+ EXPECT_EQ(0, PopTimestamp());
+ EXPECT_EQ(1000, PopTimestamp());
+ EXPECT_EQ(2000, PopTimestamp());
+ EXPECT_EQ(3000, PopTimestamp());
+}
+
+// Test situation where some input timestamps are missing and estimation will
+// be used based on the frame rate.
+TEST_F(FFmpegVideoDecoderTest, Timestamps_Estimated) {
+ SetupTimestampTest();
+
+ PushTimestamp(0);
+ PushTimestamp(1000);
+ PushTimestamp(kNoTimestamp.InMicroseconds());
+ PushTimestamp(kNoTimestamp.InMicroseconds());
+
+ EXPECT_EQ(0, PopTimestamp());
+ EXPECT_EQ(1000, PopTimestamp());
+ EXPECT_EQ(11000, PopTimestamp());
+ EXPECT_EQ(21000, PopTimestamp());
+}
+
+// Test resulting timestamps from end of stream.
+TEST_F(FFmpegVideoDecoderTest, Timestamps_EndOfStream) {
+ SetupTimestampTest();
+
+ PushTimestamp(0);
+ PushTimestamp(1000);
+
+ EXPECT_EQ(0, PopTimestamp());
+ EXPECT_EQ(1000, PopTimestamp());
+
+ // Following are all end of stream buffers.
+ EXPECT_EQ(0, PopTimestamp());
+ EXPECT_EQ(0, PopTimestamp());
+ EXPECT_EQ(0, PopTimestamp());
}
} // namespace media