Index: media/video/ffmpeg_video_decode_engine_unittest.cc |
=================================================================== |
--- media/video/ffmpeg_video_decode_engine_unittest.cc (revision 96976) |
+++ media/video/ffmpeg_video_decode_engine_unittest.cc (working copy) |
@@ -5,10 +5,9 @@ |
#include "base/memory/scoped_ptr.h" |
#include "base/message_loop.h" |
#include "media/base/data_buffer.h" |
+#include "media/base/mock_ffmpeg.h" |
#include "media/base/mock_task.h" |
#include "media/base/pipeline.h" |
-#include "media/base/test_data_util.h" |
-#include "media/filters/ffmpeg_glue.h" |
#include "media/video/ffmpeg_video_decode_engine.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -22,12 +21,21 @@ |
namespace media { |
-static const size_t kWidth = 320; |
-static const size_t kHeight = 240; |
+static const int kWidth = 320; |
+static const int kHeight = 240; |
static const int kSurfaceWidth = 522; |
static const int kSurfaceHeight = 288; |
static const AVRational kFrameRate = { 100, 1 }; |
+static void InitializeFrame(uint8_t* data, int width, AVFrame* frame) { |
+ frame->data[0] = data; |
+ frame->data[1] = data; |
+ frame->data[2] = data; |
+ frame->linesize[0] = width; |
+ frame->linesize[1] = width / 2; |
+ frame->linesize[2] = width / 2; |
+} |
+ |
ACTION_P(DecodeComplete, decoder) { |
decoder->set_video_frame(arg0); |
} |
@@ -45,13 +53,19 @@ |
public VideoDecodeEngine::EventHandler { |
public: |
FFmpegVideoDecodeEngineTest() |
- : config_(kCodecVP8, kWidth, kHeight, kSurfaceWidth, kSurfaceHeight, |
+ : config_(kCodecH264, kWidth, kHeight, kSurfaceWidth, kSurfaceHeight, |
kFrameRate.num, kFrameRate.den, NULL, 0) { |
- CHECK(FFmpegGlue::GetInstance()); |
// Setup FFmpeg structures. |
frame_buffer_.reset(new uint8[kWidth * kHeight]); |
+ memset(&yuv_frame_, 0, sizeof(yuv_frame_)); |
+ InitializeFrame(frame_buffer_.get(), kWidth, &yuv_frame_); |
+ memset(&codec_context_, 0, sizeof(codec_context_)); |
+ memset(&codec_, 0, sizeof(codec_)); |
+ |
+ buffer_ = new DataBuffer(1); |
+ |
test_engine_.reset(new FFmpegVideoDecodeEngine()); |
video_frame_ = VideoFrame::CreateFrame(VideoFrame::YV12, |
@@ -59,8 +73,6 @@ |
kHeight, |
kNoTimestamp, |
kNoTimestamp); |
- |
- ReadTestDataFile("vp8-I-frame-320x240", &i_frame_buffer_); |
} |
~FFmpegVideoDecodeEngineTest() { |
@@ -68,20 +80,48 @@ |
} |
void Initialize() { |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) |
+ .WillOnce(Return(&codec_context_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) |
+ .WillOnce(Return(&codec_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) |
+ .WillOnce(Return(&yuv_frame_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecOpen(&codec_context_, &codec_)) |
+ .WillOnce(Return(0)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) |
+ .WillOnce(Return(0)); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) |
+ .Times(1); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) |
+ .Times(1); |
+ |
EXPECT_CALL(*this, OnInitializeComplete(_)) |
.WillOnce(SaveInitializeResult(this)); |
test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); |
EXPECT_TRUE(info_.success); |
} |
- void Decode(const scoped_refptr<Buffer>& buffer) { |
+ void Decode() { |
+ 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))); |
+ |
EXPECT_CALL(*this, ProduceVideoSample(_)) |
- .WillOnce(DemuxComplete(test_engine_.get(), buffer)); |
+ .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); |
EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) |
.WillOnce(DecodeComplete(this)); |
test_engine_->ProduceVideoFrame(video_frame_); |
} |
+ void ChangeDimensions(int width, int height) { |
+ frame_buffer_.reset(new uint8[width * height]); |
+ InitializeFrame(frame_buffer_.get(), width, &yuv_frame_); |
+ codec_context_.width = width; |
+ codec_context_.height = height; |
+ } |
+ |
// VideoDecodeEngine::EventHandler implementation. |
MOCK_METHOD2(ConsumeVideoFrame, |
void(scoped_refptr<VideoFrame> video_frame, |
@@ -110,8 +150,13 @@ |
scoped_refptr<VideoFrame> video_frame_; |
scoped_ptr<FFmpegVideoDecodeEngine> test_engine_; |
scoped_array<uint8_t> frame_buffer_; |
- scoped_refptr<Buffer> i_frame_buffer_; |
+ StrictMock<MockFFmpeg> mock_ffmpeg_; |
+ AVFrame yuv_frame_; |
+ AVCodecContext codec_context_; |
+ AVCodec codec_; |
+ scoped_refptr<DataBuffer> buffer_; |
+ |
private: |
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngineTest); |
}; |
@@ -121,36 +166,61 @@ |
} |
TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { |
- VideoDecoderConfig config(kUnknown, kWidth, kHeight, kSurfaceWidth, |
- kSurfaceHeight, kFrameRate.num, kFrameRate.den, |
- NULL, 0); |
// Test avcodec_find_decoder() returning NULL. |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) |
+ .WillOnce(Return(&codec_context_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) |
+ .WillOnce(ReturnNull()); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) |
+ .WillOnce(Return(&yuv_frame_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) |
+ .WillOnce(Return(0)); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) |
+ .Times(1); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) |
+ .Times(1); |
+ |
EXPECT_CALL(*this, OnInitializeComplete(_)) |
.WillOnce(SaveInitializeResult(this)); |
- test_engine_->Initialize(MessageLoop::current(), this, NULL, config); |
+ test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); |
EXPECT_FALSE(info_.success); |
} |
TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { |
- // Specify Theora w/o extra data so that avcodec_open() fails. |
- VideoDecoderConfig config(kCodecTheora, kWidth, kHeight, kSurfaceWidth, |
- kSurfaceHeight, kFrameRate.num, kFrameRate.den, |
- NULL, 0); |
+ // Test avcodec_open() failing. |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) |
+ .WillOnce(Return(&codec_context_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) |
+ .WillOnce(Return(&codec_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) |
+ .WillOnce(Return(&yuv_frame_)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecOpen(&codec_context_, &codec_)) |
+ .WillOnce(Return(-1)); |
+ EXPECT_CALL(mock_ffmpeg_, AVCodecClose(&codec_context_)) |
+ .WillOnce(Return(0)); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) |
+ .Times(1); |
+ EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) |
+ .Times(1); |
+ |
EXPECT_CALL(*this, OnInitializeComplete(_)) |
.WillOnce(SaveInitializeResult(this)); |
- test_engine_->Initialize(MessageLoop::current(), this, NULL, config); |
+ test_engine_->Initialize(MessageLoop::current(), this, NULL, config_); |
EXPECT_FALSE(info_.success); |
} |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { |
Initialize(); |
- // We rely on FFmpeg for timestamp and duration reporting. |
- const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(0); |
- const base::TimeDelta kDuration = base::TimeDelta::FromMicroseconds(10000); |
+ // We rely on FFmpeg for timestamp and duration reporting. The one tricky |
+ // bit is calculating the duration when |repeat_pict| > 0. |
+ const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(123); |
+ const base::TimeDelta kDuration = base::TimeDelta::FromMicroseconds(15000); |
+ yuv_frame_.repeat_pict = 1; |
+ yuv_frame_.reordered_opaque = kTimestamp.InMicroseconds(); |
// Simulate decoding a single frame. |
- Decode(i_frame_buffer_); |
+ Decode(); |
// |video_frame_| timestamp is 0 because we set the timestamp based off |
// the buffer timestamp. |
@@ -162,11 +232,19 @@ |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { |
Initialize(); |
- scoped_refptr<Buffer> buffer_a = new DataBuffer(1); |
+ // Expect a bunch of avcodec calls. |
+ EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_)) |
+ .Times(2); |
+ EXPECT_CALL(mock_ffmpeg_, |
+ AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) |
+ .WillOnce(DoAll(SetArgumentPointee<2>(0), // Simulate 0 byte frame. |
+ Return(0))) |
+ .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame. |
+ Return(0))); |
EXPECT_CALL(*this, ProduceVideoSample(_)) |
- .WillOnce(DemuxComplete(test_engine_.get(), buffer_a)) |
- .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)); |
+ .WillOnce(DemuxComplete(test_engine_.get(), buffer_)) |
+ .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); |
EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) |
.WillOnce(DecodeComplete(this)); |
test_engine_->ProduceVideoFrame(video_frame_); |
@@ -177,19 +255,14 @@ |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { |
Initialize(); |
- scoped_refptr<DataBuffer> buffer = |
- new DataBuffer(i_frame_buffer_->GetDataSize()); |
- buffer->SetDataSize(i_frame_buffer_->GetDataSize()); |
+ // Expect a bunch of avcodec calls. |
+ EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_)); |
+ EXPECT_CALL(mock_ffmpeg_, |
+ AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _)) |
+ .WillOnce(Return(-1)); |
- uint8* buf = buffer->GetWritableData(); |
- memcpy(buf, i_frame_buffer_->GetData(), buffer->GetDataSize()); |
- |
- // Corrupt bytes by flipping bits w/ xor. |
- for (size_t i = 0; i < buffer->GetDataSize(); i++) |
- buf[i] ^= 0xA5; |
- |
EXPECT_CALL(*this, ProduceVideoSample(_)) |
- .WillOnce(DemuxComplete(test_engine_.get(), buffer)); |
+ .WillOnce(DemuxComplete(test_engine_.get(), buffer_)); |
EXPECT_CALL(*this, OnError()); |
test_engine_->ProduceVideoFrame(video_frame_); |
@@ -197,71 +270,46 @@ |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) { |
Initialize(); |
- |
- // Decode a frame and verify the width. |
- Decode(i_frame_buffer_); |
- EXPECT_EQ(video_frame_->width(), kWidth); |
- EXPECT_EQ(video_frame_->height(), kHeight); |
- |
- // Now decode a frame with a larger width and verify the output size didn't |
- // change. |
- scoped_refptr<Buffer> buffer; |
- ReadTestDataFile("vp8-I-frame-640x240", &buffer); |
- Decode(buffer); |
- |
- EXPECT_EQ(kWidth, video_frame_->width()); |
- EXPECT_EQ(kHeight, video_frame_->height()); |
+ ChangeDimensions(kWidth * 2, kHeight); |
+ Decode(); |
} |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) { |
Initialize(); |
- |
- // Decode a frame and verify the width. |
- Decode(i_frame_buffer_); |
- EXPECT_EQ(video_frame_->width(), kWidth); |
- EXPECT_EQ(video_frame_->height(), kHeight); |
- |
- // Now decode a frame with a smaller width and verify the output size didn't |
- // change. |
- scoped_refptr<Buffer> buffer; |
- ReadTestDataFile("vp8-I-frame-160x240", &buffer); |
- Decode(buffer); |
- EXPECT_EQ(video_frame_->width(), kWidth); |
- EXPECT_EQ(video_frame_->height(), kHeight); |
+ ChangeDimensions(kWidth / 2, kHeight); |
+ Decode(); |
} |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) { |
Initialize(); |
- |
- // Decode a frame and verify the width. |
- Decode(i_frame_buffer_); |
- EXPECT_EQ(video_frame_->width(), kWidth); |
- EXPECT_EQ(video_frame_->height(), kHeight); |
- |
- // Now decode a frame with a larger height and verify the output |
- // size didn't change. |
- scoped_refptr<Buffer> buffer; |
- ReadTestDataFile("vp8-I-frame-320x480", &buffer); |
- Decode(buffer); |
- EXPECT_EQ(kWidth, video_frame_->width()); |
- EXPECT_EQ(kHeight, video_frame_->height()); |
+ ChangeDimensions(kWidth, kHeight * 2); |
+ Decode(); |
} |
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { |
Initialize(); |
+ ChangeDimensions(kWidth, kHeight / 2); |
+ Decode(); |
+} |
- // Decode a frame and verify the width. |
- Decode(i_frame_buffer_); |
- EXPECT_EQ(video_frame_->width(), kWidth); |
- EXPECT_EQ(video_frame_->height(), kHeight); |
+TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) { |
+ Initialize(); |
- // Now decode a frame with a smaller height and verify the output size |
- // didn't change. |
- scoped_refptr<Buffer> buffer; |
- ReadTestDataFile("vp8-I-frame-320x120", &buffer); |
- Decode(buffer); |
- EXPECT_EQ(kWidth, video_frame_->width()); |
- EXPECT_EQ(kHeight, video_frame_->height()); |
+ // YV12 formats. |
+ codec_context_.pix_fmt = PIX_FMT_YUV420P; |
+ EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat()); |
+ codec_context_.pix_fmt = PIX_FMT_YUVJ420P; |
+ EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat()); |
+ |
+ // YV16 formats. |
+ codec_context_.pix_fmt = PIX_FMT_YUV422P; |
+ EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat()); |
+ codec_context_.pix_fmt = PIX_FMT_YUVJ422P; |
+ EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat()); |
+ |
+ // Invalid value. |
+ codec_context_.pix_fmt = PIX_FMT_NONE; |
+ EXPECT_EQ(VideoFrame::INVALID, test_engine_->GetSurfaceFormat()); |
} |
} // namespace media |