blob: 40b122d2494795d33b292fba3cfb59a3e8032e3a [file] [log] [blame]
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <deque>
#include "base/singleton.h"
#include "media/base/data_buffer.h"
#include "media/base/filters.h"
#include "media/base/mock_ffmpeg.h"
#include "media/base/mock_filter_host.h"
#include "media/filters/ffmpeg_common.h"
#include "media/filters/ffmpeg_interfaces.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::ReturnNull;
using ::testing::SetArgumentPointee;
using ::testing::StrictMock;
namespace media {
class MockDemuxerStream : public DemuxerStream, public AVStreamProvider {
public:
// DemuxerStream interface.
MOCK_METHOD0(media_format, const MediaFormat&());
MOCK_METHOD1(Read, void(Callback1<Buffer*>::Type* read_callback));
MOCK_METHOD1(QueryInterface, void*(const char* interface_id));
// AVStreamProvider interface.
MOCK_METHOD0(GetAVStream, AVStream*());
};
// Class that just mocks the private functions.
class DecoderPrivateMock : public FFmpegVideoDecoder {
public:
MOCK_METHOD3(EnqueueVideoFrame, bool(VideoSurface::Format surface_format,
const TimeTuple& time,
const AVFrame* frame));
MOCK_METHOD0(EnqueueEmptyFrame, void());
MOCK_METHOD3(CopyPlane, void(size_t plane, const VideoSurface& surface,
const AVFrame* frame));
MOCK_METHOD1(GetSurfaceFormat,
VideoSurface::Format(const AVCodecContext& codec_context));
MOCK_METHOD3(DecodeFrame, bool(const Buffer& buffer,
AVCodecContext* codec_context,
AVFrame* yuv_frame));
MOCK_METHOD4(FindPtsAndDuration, TimeTuple(const AVRational& time_base,
const TimeQueue& pts_queue,
const TimeTuple& last_pts,
const AVFrame* frame));
MOCK_METHOD0(SignalPipelineError, void());
};
// Fixture class to facilitate writing tests. Takes care of setting up the
// FFmpeg, pipeline and filter host mocks.
class FFmpegVideoDecoderTest : public testing::Test {
protected:
static const int kWidth;
static const int kHeight;
static const FFmpegVideoDecoder::TimeTuple kTestPts1;
static const FFmpegVideoDecoder::TimeTuple kTestPts2;
FFmpegVideoDecoderTest() {
MediaFormat media_format;
media_format.SetAsString(MediaFormat::kMimeType, mime_type::kFFmpegVideo);
// Create an FFmpegVideoDecoder.
factory_ = FFmpegVideoDecoder::CreateFactory();
decoder_ = factory_->Create<FFmpegVideoDecoder>(media_format);
DCHECK(decoder_);
// Provide a message loop.
decoder_->SetMessageLoop(&message_loop_);
// Manually set the thread id for tests that don't initialize the decoder.
decoder_->set_thread_id(PlatformThread::CurrentId());
// Prepare a filter host, pipeline and demuxer for the video decoder.
pipeline_.reset(new MockPipeline());
filter_host_.reset(new MockFilterHost<VideoDecoder>(pipeline_.get(),
decoder_));
demuxer_ = new MockDemuxerStream();
// Initialize FFmpeg fixtures.
memset(&stream_, 0, sizeof(stream_));
memset(&codec_context_, 0, sizeof(codec_context_));
memset(&codec_, 0, sizeof(codec_));
memset(&yuv_frame_, 0, sizeof(yuv_frame_));
stream_.codec = &codec_context_;
codec_context_.width = kWidth;
codec_context_.height = kHeight;
buffer_ = new DataBuffer();
buffer_->GetWritableData(1);
end_of_stream_buffer_ = new DataBuffer();
// Initialize MockFFmpeg.
MockFFmpeg::set(&mock_ffmpeg_);
}
virtual ~FFmpegVideoDecoderTest() {
// Call Stop() to shut down internal threads.
decoder_->Stop();
// Finish up any remaining tasks.
message_loop_.RunAllPending();
// Reset MockFFmpeg.
MockFFmpeg::set(NULL);
}
// Fixture members.
scoped_refptr<FilterFactory> factory_;
scoped_refptr<FFmpegVideoDecoder> decoder_;
scoped_ptr<MockPipeline> pipeline_;
scoped_ptr<MockFilterHost<VideoDecoder> > filter_host_;
scoped_refptr<MockDemuxerStream> demuxer_;
scoped_refptr<DataBuffer> buffer_;
scoped_refptr<DataBuffer> end_of_stream_buffer_;
MessageLoop message_loop_;
// FFmpeg fixtures.
AVStream stream_;
AVCodecContext codec_context_;
AVCodec codec_;
AVFrame yuv_frame_;
StrictMock<MockFFmpeg> mock_ffmpeg_;
private:
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
};
const int FFmpegVideoDecoderTest::kWidth = 1280;
const int FFmpegVideoDecoderTest::kHeight = 720;
const FFmpegVideoDecoder::TimeTuple FFmpegVideoDecoderTest::kTestPts1 =
{ base::TimeDelta::FromMicroseconds(123),
base::TimeDelta::FromMicroseconds(50) };
const FFmpegVideoDecoder::TimeTuple FFmpegVideoDecoderTest::kTestPts2 =
{ base::TimeDelta::FromMicroseconds(456),
base::TimeDelta::FromMicroseconds(60) };
TEST(FFmpegVideoDecoderFactoryTest, Create) {
// Should only accept video/x-ffmpeg mime type.
scoped_refptr<FilterFactory> factory = FFmpegVideoDecoder::CreateFactory();
MediaFormat media_format;
media_format.SetAsString(MediaFormat::kMimeType, "foo/x-bar");
scoped_refptr<VideoDecoder> decoder =
factory->Create<VideoDecoder>(media_format);
ASSERT_FALSE(decoder);
// Try again with video/x-ffmpeg mime type.
media_format.Clear();
media_format.SetAsString(MediaFormat::kMimeType,
mime_type::kFFmpegVideo);
decoder = factory->Create<VideoDecoder>(media_format);
ASSERT_TRUE(decoder);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_QueryInterfaceFails) {
// Test QueryInterface returning NULL.
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(ReturnNull());
EXPECT_TRUE(decoder_->Initialize(demuxer_));
message_loop_.RunAllPending();
EXPECT_TRUE(filter_host_->WaitForError(PIPELINE_ERROR_DECODE));
EXPECT_FALSE(filter_host_->IsInitialized());
}
TEST_F(FFmpegVideoDecoderTest, Initialize_FindDecoderFails) {
// Test avcodec_find_decoder() returning NULL.
AVStreamProvider* av_stream_provider = demuxer_;
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(Return(av_stream_provider));
EXPECT_CALL(*demuxer_, GetAVStream())
.WillOnce(Return(&stream_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
.WillOnce(ReturnNull());
EXPECT_TRUE(decoder_->Initialize(demuxer_));
message_loop_.RunAllPending();
EXPECT_TRUE(filter_host_->WaitForError(PIPELINE_ERROR_DECODE));
EXPECT_FALSE(filter_host_->IsInitialized());
}
TEST_F(FFmpegVideoDecoderTest, Initialize_InitThreadFails) {
// Test avcodec_thread_init() failing.
AVStreamProvider* av_stream_provider = demuxer_;
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(Return(av_stream_provider));
EXPECT_CALL(*demuxer_, GetAVStream())
.WillOnce(Return(&stream_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
.WillOnce(Return(&codec_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
.WillOnce(Return(-1));
EXPECT_TRUE(decoder_->Initialize(demuxer_));
message_loop_.RunAllPending();
EXPECT_TRUE(filter_host_->WaitForError(PIPELINE_ERROR_DECODE));
EXPECT_FALSE(filter_host_->IsInitialized());
}
TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
// Test avcodec_open() failing.
AVStreamProvider* av_stream_provider = demuxer_;
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(Return(av_stream_provider));
EXPECT_CALL(*demuxer_, GetAVStream())
.WillOnce(Return(&stream_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
.WillOnce(Return(&codec_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
.WillOnce(Return(0));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
.WillOnce(Return(-1));
EXPECT_TRUE(decoder_->Initialize(demuxer_));
message_loop_.RunAllPending();
EXPECT_TRUE(filter_host_->WaitForError(PIPELINE_ERROR_DECODE));
EXPECT_FALSE(filter_host_->IsInitialized());
}
TEST_F(FFmpegVideoDecoderTest, Initialize_Successful) {
// Test successful initialization.
AVStreamProvider* av_stream_provider = demuxer_;
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(Return(av_stream_provider));
EXPECT_CALL(*demuxer_, GetAVStream())
.WillOnce(Return(&stream_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
.WillOnce(Return(&codec_));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
.WillOnce(Return(0));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
.WillOnce(Return(0));
EXPECT_TRUE(decoder_->Initialize(demuxer_));
message_loop_.RunAllPending();
EXPECT_TRUE(filter_host_->WaitForInitialized());
EXPECT_TRUE(filter_host_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
// Test that the output media format is an uncompressed video surface that
// matches the dimensions specified by FFmpeg.
const MediaFormat& media_format = decoder_->media_format();
std::string mime_type;
int width = 0;
int height = 0;
EXPECT_TRUE(media_format.GetAsString(MediaFormat::kMimeType, &mime_type));
EXPECT_STREQ(mime_type::kUncompressedVideo, mime_type.c_str());
EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kWidth, &width));
EXPECT_EQ(kWidth, width);
EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kHeight, &height));
EXPECT_EQ(kHeight, height);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
// Expect a bunch of avcodec calls.
EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
EXPECT_CALL(mock_ffmpeg_,
AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
.WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame.
Return(0)));
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
EXPECT_TRUE(decoder->DecodeFrame(*buffer_, &codec_context_, &yuv_frame_));
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DiscontinuousBuffer) {
buffer_->SetDiscontinuous(true);
// Expect a bunch of avcodec calls.
EXPECT_CALL(mock_ffmpeg_, AVCodecFlushBuffers(&codec_context_));
EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
EXPECT_CALL(mock_ffmpeg_,
AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
.WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame.
Return(0)));
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
EXPECT_TRUE(decoder->DecodeFrame(*buffer_, &codec_context_, &yuv_frame_));
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
// Expect a bunch of avcodec calls.
EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
EXPECT_CALL(mock_ffmpeg_,
AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
.WillOnce(DoAll(SetArgumentPointee<2>(0), // Simulate 0 byte frame.
Return(0)));
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
EXPECT_FALSE(decoder->DecodeFrame(*buffer_, &codec_context_, &yuv_frame_));
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
// Expect a bunch of avcodec calls.
EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
EXPECT_CALL(mock_ffmpeg_,
AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
.WillOnce(Return(-1));
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
EXPECT_FALSE(decoder->DecodeFrame(*buffer_, &codec_context_, &yuv_frame_));
}
TEST_F(FFmpegVideoDecoderTest, GetSurfaceFormat) {
AVCodecContext context;
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
// YV12 formats.
context.pix_fmt = PIX_FMT_YUV420P;
EXPECT_EQ(VideoSurface::YV12, decoder->GetSurfaceFormat(context));
context.pix_fmt = PIX_FMT_YUVJ420P;
EXPECT_EQ(VideoSurface::YV12, decoder->GetSurfaceFormat(context));
// YV16 formats.
context.pix_fmt = PIX_FMT_YUV422P;
EXPECT_EQ(VideoSurface::YV16, decoder->GetSurfaceFormat(context));
context.pix_fmt = PIX_FMT_YUVJ422P;
EXPECT_EQ(VideoSurface::YV16, decoder->GetSurfaceFormat(context));
// Invalid value.
context.pix_fmt = PIX_FMT_NONE;
EXPECT_EQ(VideoSurface::INVALID, decoder->GetSurfaceFormat(context));
}
TEST_F(FFmpegVideoDecoderTest, FindPtsAndDuration) {
// Start with an empty timestamp queue.
FFmpegVideoDecoder::TimeQueue pts_queue;
scoped_refptr<FFmpegVideoDecoder> decoder = new FFmpegVideoDecoder();
// Use 1/2 second for simple results. Thus, calculated Durations should be
// 500000 microseconds.
AVRational time_base = {1, 2};
// Setup the last known pts to be at 100 microseconds with a have 16
// duration.
FFmpegVideoDecoder::TimeTuple last_pts;
last_pts.timestamp = base::TimeDelta::FromMicroseconds(100);
last_pts.duration = base::TimeDelta::FromMicroseconds(16);
// Simulate an uninitialized yuv_frame.
yuv_frame_.pts = AV_NOPTS_VALUE;
FFmpegVideoDecoder::TimeTuple result_pts =
decoder->FindPtsAndDuration(time_base, pts_queue, last_pts, &yuv_frame_);
EXPECT_EQ(116, result_pts.timestamp.InMicroseconds());
EXPECT_EQ(500000, result_pts.duration.InMicroseconds());
// Test that providing no frame has the same result as an uninitialized
// frame.
result_pts = decoder->FindPtsAndDuration(time_base,
pts_queue,
last_pts,
NULL);
EXPECT_EQ(116, result_pts.timestamp.InMicroseconds());
EXPECT_EQ(500000, result_pts.duration.InMicroseconds());
// Test that having pts == 0 in the frame also behaves like the pts is not
// provided. This is because FFmpeg set the pts to zero when there is no
// data for the frame, which means that value is useless to us.
yuv_frame_.pts = 0;
result_pts =
decoder->FindPtsAndDuration(time_base, pts_queue, last_pts, &yuv_frame_);
EXPECT_EQ(116, result_pts.timestamp.InMicroseconds());
EXPECT_EQ(500000, result_pts.duration.InMicroseconds());
// Add a pts to the timequeue and make sure it overrides estimation.
pts_queue.push(base::TimeDelta::FromMicroseconds(123));
result_pts = decoder->FindPtsAndDuration(time_base,
pts_queue,
last_pts,
&yuv_frame_);
EXPECT_EQ(123, result_pts.timestamp.InMicroseconds());
EXPECT_EQ(500000, result_pts.duration.InMicroseconds());
// Add a pts into the frame and make sure it overrides the timequeue.
yuv_frame_.pts = 333;
yuv_frame_.repeat_pict = 2;
result_pts = decoder->FindPtsAndDuration(time_base,
pts_queue,
last_pts,
&yuv_frame_);
EXPECT_EQ(166500000, result_pts.timestamp.InMicroseconds());
EXPECT_EQ(1500000, result_pts.duration.InMicroseconds());
}
TEST_F(FFmpegVideoDecoderTest, OnDecode_TestStateTransition) {
// Simulates a input sequence of three buffers, and six decode requests to
// exercise the state transitions, and bookkeeping logic of OnDecode.
//
// We try to verify the following:
// 1) Non-EoS buffer timestamps are pushed into the pts_queue.
// 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.
scoped_refptr<DecoderPrivateMock> mock_decoder =
new StrictMock<DecoderPrivateMock>();
// 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_decoder, DecodeFrame(_, _, _))
.WillOnce(Return(false))
.WillOnce(DoAll(SetArgumentPointee<2>(yuv_frame_), Return(true)))
.WillOnce(Return(false))
.WillOnce(DoAll(SetArgumentPointee<2>(yuv_frame_), Return(true)))
.WillOnce(DoAll(SetArgumentPointee<2>(yuv_frame_), Return(true)))
.WillOnce(Return(false));
EXPECT_CALL(*mock_decoder, GetSurfaceFormat(_))
.Times(3)
.WillRepeatedly(Return(VideoSurface::YV16));
EXPECT_CALL(*mock_decoder, EnqueueVideoFrame(_, _, _))
.Times(3)
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_decoder, FindPtsAndDuration(_, _, _, _))
.WillOnce(Return(kTestPts1))
.WillOnce(Return(kTestPts2))
.WillOnce(Return(kTestPts1));
EXPECT_CALL(*mock_decoder, EnqueueEmptyFrame())
.Times(1);
// Setup FFmpeg expectations for frame allocations. We do
// 6 decodes in this test.
EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame())
.Times(6)
.WillRepeatedly(Return(&yuv_frame_));
EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_))
.Times(6);
// Setup initial state and check that it is sane.
mock_decoder->codec_context_ = &codec_context_;
ASSERT_EQ(FFmpegVideoDecoder::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->OnDecode(buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kNormal, mock_decoder->state_);
ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.timestamp);
ASSERT_TRUE(base::TimeDelta() == mock_decoder->last_pts_.duration);
EXPECT_EQ(1u, mock_decoder->pts_queue_.size());
// Decode a second time, which should yield the first frame.
mock_decoder->OnDecode(buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kNormal, mock_decoder->state_);
EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp);
EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration);
EXPECT_EQ(1u, mock_decoder->pts_queue_.size());
// Decode a third time, with a regular buffer. The decode will error
// out, but the state should be the same.
mock_decoder->OnDecode(buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kNormal, mock_decoder->state_);
EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp);
EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration);
EXPECT_EQ(2u, mock_decoder->pts_queue_.size());
// Decode a fourth time, with an end of stream buffer. This should
// yield the second frame, and stay in flushing mode.
mock_decoder->OnDecode(end_of_stream_buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kFlushCodec, mock_decoder->state_);
EXPECT_TRUE(kTestPts2.timestamp == mock_decoder->last_pts_.timestamp);
EXPECT_TRUE(kTestPts2.duration == mock_decoder->last_pts_.duration);
EXPECT_EQ(1u, mock_decoder->pts_queue_.size());
// Decode a fifth time with an end of stream buffer. this should
// yield the third frame.
mock_decoder->OnDecode(end_of_stream_buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kFlushCodec, mock_decoder->state_);
EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp);
EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration);
EXPECT_EQ(0u, mock_decoder->pts_queue_.size());
// Decode a sixth time with an end of stream buffer. This should
// Move into kDecodeFinished.
mock_decoder->OnDecode(end_of_stream_buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kDecodeFinished, mock_decoder->state_);
EXPECT_TRUE(kTestPts1.timestamp == mock_decoder->last_pts_.timestamp);
EXPECT_TRUE(kTestPts1.duration == mock_decoder->last_pts_.duration);
EXPECT_EQ(0u, mock_decoder->pts_queue_.size());
}
TEST_F(FFmpegVideoDecoderTest, OnDecode_EnqueueVideoFrameError) {
scoped_refptr<DecoderPrivateMock> mock_decoder =
new StrictMock<DecoderPrivateMock>();
// Setup decoder to decode one frame, but then fail on enqueue.
EXPECT_CALL(*mock_decoder, DecodeFrame(_, _, _))
.WillOnce(DoAll(SetArgumentPointee<2>(yuv_frame_), Return(true)));
EXPECT_CALL(*mock_decoder, GetSurfaceFormat(_))
.WillOnce(Return(VideoSurface::YV16));
EXPECT_CALL(*mock_decoder, EnqueueVideoFrame(_, _, _))
.WillOnce(Return(false));
EXPECT_CALL(*mock_decoder, FindPtsAndDuration(_, _, _, _))
.WillOnce(Return(kTestPts1));
EXPECT_CALL(*mock_decoder, SignalPipelineError());
// Setup FFmpeg expectations for frame allocations.
EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame())
.WillOnce(Return(&yuv_frame_));
EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_));
// Attempt the decode.
buffer_->GetWritableData(1);
mock_decoder->codec_context_ = &codec_context_;
mock_decoder->OnDecode(buffer_);
}
TEST_F(FFmpegVideoDecoderTest, OnDecode_FinishEnqueuesEmptyFrames) {
scoped_refptr<DecoderPrivateMock> mock_decoder =
new StrictMock<DecoderPrivateMock>();
// Move the decoder into the finished state for this test.
mock_decoder->state_ = FFmpegVideoDecoder::kDecodeFinished;
// Expect 2 calls, make two calls.
EXPECT_CALL(*mock_decoder, EnqueueEmptyFrame()).Times(3);
mock_decoder->OnDecode(NULL);
mock_decoder->OnDecode(buffer_);
mock_decoder->OnDecode(end_of_stream_buffer_);
EXPECT_EQ(FFmpegVideoDecoder::kDecodeFinished, mock_decoder->state_);
}
TEST_F(FFmpegVideoDecoderTest, TimeQueue_Ordering) {
FFmpegVideoDecoder::TimeQueue queue;
queue.push(kTestPts1.timestamp);
queue.push(kTestPts2.timestamp);
queue.push(kTestPts1.timestamp);
EXPECT_TRUE(kTestPts1.timestamp == queue.top());
queue.pop();
EXPECT_TRUE(kTestPts1.timestamp == queue.top());
queue.pop();
EXPECT_TRUE(kTestPts2.timestamp == queue.top());
queue.pop();
}
} // namespace media