This source file includes following definitions.
- ACTION_P
- decode_cb_
- Initialize
- InitializeWithConfigAndStatus
- InitializeWithConfig
- Reinitialize
- Reset
- Stop
- EnterDecodingState
- EnterEndOfStreamState
- DecodeMultipleFrames
- DecodeSingleFrame
- DecodeIFrameThenTestFile
- Decode
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "media/base/decoder_buffer.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/limits.h"
#include "media/base/mock_filters.h"
#include "media/base/test_data_util.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::InSequence;
using ::testing::IsNull;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace media {
static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
static const gfx::Size kCodedSize(320, 240);
static const gfx::Rect kVisibleRect(320, 240);
static const gfx::Size kNaturalSize(320, 240);
ACTION_P(ReturnBuffer, buffer) {
arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
}
class FFmpegVideoDecoderTest : public testing::Test {
public:
FFmpegVideoDecoderTest()
: decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
decode_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
base::Unretained(this))) {
FFmpegGlue::InitializeFFmpeg();
frame_buffer_.reset(new uint8[kCodedSize.GetArea()]);
end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240");
corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame");
}
virtual ~FFmpegVideoDecoderTest() {
Stop();
}
void Initialize() {
InitializeWithConfig(TestVideoConfig::Normal());
}
void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
PipelineStatus status) {
decoder_->Initialize(config, NewExpectedStatusCB(status));
message_loop_.RunUntilIdle();
}
void InitializeWithConfig(const VideoDecoderConfig& config) {
InitializeWithConfigAndStatus(config, PIPELINE_OK);
}
void Reinitialize() {
InitializeWithConfig(TestVideoConfig::Large());
}
void Reset() {
decoder_->Reset(NewExpectedClosure());
message_loop_.RunUntilIdle();
}
void Stop() {
decoder_->Stop();
message_loop_.RunUntilIdle();
}
void EnterDecodingState() {
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());
}
void EnterEndOfStreamState() {
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;
typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers,
OutputFrames* output_frames) {
InputBuffers::const_iterator input_iter = input_buffers.begin();
for (;;) {
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->end_of_stream()) {
output_frames->push_back(frame);
continue;
} else {
return status;
}
case VideoDecoder::kNotEnoughData:
DCHECK(!frame);
continue;
case VideoDecoder::kAborted:
NOTREACHED();
case VideoDecoder::kDecodeError:
case VideoDecoder::kDecryptError:
DCHECK(!frame);
return status;
}
}
}
void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
VideoDecoder::Status* status,
scoped_refptr<VideoFrame>* video_frame) {
InputBuffers input_buffers;
input_buffers.push_back(buffer);
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();
}
void DecodeIFrameThenTestFile(const std::string& test_file_name,
int expected_width,
int expected_height) {
Initialize();
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
InputBuffers input_buffers;
input_buffers.push_back(i_frame_buffer_);
input_buffers.push_back(buffer);
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(original_size.width(),
output_frames[0]->visible_rect().size().width());
EXPECT_EQ(original_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 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();
}
MOCK_METHOD2(FrameReady, void(VideoDecoder::Status,
const scoped_refptr<VideoFrame>&));
base::MessageLoop message_loop_;
scoped_ptr<FFmpegVideoDecoder> decoder_;
VideoDecoder::DecodeCB decode_cb_;
scoped_ptr<uint8_t[]> frame_buffer_;
scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
scoped_refptr<DecoderBuffer> i_frame_buffer_;
scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
private:
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
};
TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
Initialize();
}
TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedDecoder) {
InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedPixelFormat) {
VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
VideoFrame::UNKNOWN,
kCodedSize, kVisibleRect, kNaturalSize,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
kVideoFormat,
kCodedSize, kVisibleRect, kNaturalSize,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorTooLarge) {
int width = kVisibleRect.size().width();
int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorTooLarge) {
int den = kVisibleRect.size().width() + 1;
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
kVideoFormat,
kCodedSize, kVisibleRect, natural_size,
NULL, 0, false);
InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
Initialize();
Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_Failure) {
Initialize();
InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
Initialize();
EnterDecodingState();
Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
Initialize();
EnterDecodingState();
Reset();
Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
Initialize();
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());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
Initialize();
scoped_refptr<DecoderBuffer> zero_byte_buffer = new DecoderBuffer(0);
InputBuffers input_buffers;
input_buffers.push_back(i_frame_buffer_);
input_buffers.push_back(zero_byte_buffer);
input_buffers.push_back(i_frame_buffer_);
OutputFrames output_frames;
VideoDecoder::Status status =
DecodeMultipleFrames(input_buffers, &output_frames);
EXPECT_EQ(VideoDecoder::kOk, status);
ASSERT_EQ(2U, output_frames.size());
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;
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);
Decode(i_frame_buffer_, &status, &frame);
DCHECK(!frame);
DCHECK_EQ(VideoDecoder::kDecodeError, status);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
Initialize();
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());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
DecodeIFrameThenTestFile("vp8-I-frame-640x240", 640, 240);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
DecodeIFrameThenTestFile("vp8-I-frame-160x240", 160, 240);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
DecodeIFrameThenTestFile("vp8-I-frame-320x480", 320, 480);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120);
}
TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
Initialize();
Reset();
}
TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
Initialize();
EnterDecodingState();
Reset();
}
TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
Initialize();
EnterDecodingState();
EnterEndOfStreamState();
Reset();
}
TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
Initialize();
Stop();
}
TEST_F(FFmpegVideoDecoderTest, Stop_Decoding) {
Initialize();
EnterDecodingState();
Stop();
}
TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
Initialize();
EnterDecodingState();
EnterEndOfStreamState();
Stop();
}
}