0

Handle changes in video frame size.

BUG=67303
TEST=media_unittests

Review URL: http://codereview.chromium.org/6034007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70703 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
scherkus@chromium.org
2011-01-07 02:08:11 +00:00
parent 23ec85d9f3
commit a62d804db9
2 changed files with 78 additions and 33 deletions

@@ -127,24 +127,29 @@ void FFmpegVideoDecodeEngine::Initialize(
event_handler_->OnInitializeComplete(info);
}
// TODO(fbarchard): Find way to remove this memcpy of the entire image.
// TODO(scherkus): Move this function to a utility class and unit test.
static void CopyPlane(size_t plane,
scoped_refptr<VideoFrame> video_frame,
const AVFrame* frame) {
const AVFrame* frame,
size_t source_height) {
DCHECK_EQ(video_frame->width() % 2, 0u);
const uint8* source = frame->data[plane];
const size_t source_stride = frame->linesize[plane];
uint8* dest = video_frame->data(plane);
const size_t dest_stride = video_frame->stride(plane);
// Calculate amounts to copy and clamp to minium frame dimensions.
size_t bytes_per_line = video_frame->width();
size_t copy_lines = video_frame->height();
size_t copy_lines = std::min(video_frame->height(), source_height);
if (plane != VideoFrame::kYPlane) {
bytes_per_line /= 2;
if (video_frame->format() == VideoFrame::YV12) {
copy_lines = (copy_lines + 1) / 2;
}
}
DCHECK(bytes_per_line <= source_stride && bytes_per_line <= dest_stride);
bytes_per_line = std::min(bytes_per_line, source_stride);
// Copy!
for (size_t i = 0; i < copy_lines; ++i) {
memcpy(dest, source, bytes_per_line);
source += source_stride;
@@ -278,9 +283,10 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) {
// Copy the frame data since FFmpeg reuses internal buffers for AVFrame
// output, meaning the data is only valid until the next
// avcodec_decode_video() call.
CopyPlane(VideoFrame::kYPlane, video_frame.get(), av_frame_.get());
CopyPlane(VideoFrame::kUPlane, video_frame.get(), av_frame_.get());
CopyPlane(VideoFrame::kVPlane, video_frame.get(), av_frame_.get());
size_t height = codec_context_->height;
CopyPlane(VideoFrame::kYPlane, video_frame.get(), av_frame_.get(), height);
CopyPlane(VideoFrame::kUPlane, video_frame.get(), av_frame_.get(), height);
CopyPlane(VideoFrame::kVPlane, video_frame.get(), av_frame_.get(), height);
} else {
// Get the VideoFrame from allocator which associate with av_frame_.
video_frame = allocator_->DecodeDone(codec_context_, av_frame_.get());

@@ -24,6 +24,23 @@ static const int kWidth = 320;
static const int kHeight = 240;
static const AVRational kTimeBase = { 1, 100 };
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->video_frame_ = arg0;
}
ACTION_P2(DemuxComplete, engine, buffer) {
engine->ConsumeVideoSample(buffer);
}
ACTION_P(SaveInitializeResult, engine) {
engine->info_ = arg0;
}
@@ -35,12 +52,7 @@ class FFmpegVideoDecodeEngineTest : public testing::Test,
// Setup FFmpeg structures.
frame_buffer_.reset(new uint8[kWidth * kHeight]);
memset(&yuv_frame_, 0, sizeof(yuv_frame_));
// DecodeFrame will check these pointers as non-NULL value.
yuv_frame_.data[0] = yuv_frame_.data[1] = yuv_frame_.data[2]
= frame_buffer_.get();
yuv_frame_.linesize[0] = kWidth;
yuv_frame_.linesize[1] = yuv_frame_.linesize[2] = kWidth >> 1;
InitializeFrame(frame_buffer_.get(), kWidth, &yuv_frame_);
memset(&codec_context_, 0, sizeof(codec_context_));
codec_context_.width = kWidth;
@@ -96,6 +108,27 @@ class FFmpegVideoDecodeEngineTest : public testing::Test,
EXPECT_TRUE(info_.success);
}
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_));
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;
}
public:
MOCK_METHOD1(ConsumeVideoFrame,
void(scoped_refptr<VideoFrame> video_frame));
@@ -193,14 +226,6 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) {
EXPECT_FALSE(info_.success);
}
ACTION_P2(DemuxComplete, engine, buffer) {
engine->ConsumeVideoSample(buffer);
}
ACTION_P(DecodeComplete, decoder) {
decoder->video_frame_ = arg0;
}
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) {
Initialize();
@@ -211,18 +236,8 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) {
yuv_frame_.repeat_pict = 1;
yuv_frame_.reordered_opaque = kTimestamp.InMicroseconds();
// 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)));
EXPECT_CALL(*this, ProduceVideoSample(_))
.WillOnce(DemuxComplete(test_engine_.get(), buffer_));
EXPECT_CALL(*this, ConsumeVideoFrame(_))
.WillOnce(DecodeComplete(this));
test_engine_->ProduceVideoFrame(video_frame_);
// Simulate decoding a single frame.
Decode();
// |video_frame_| timestamp is 0 because we set the timestamp based off
// the buffer timestamp.
@@ -272,6 +287,30 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) {
EXPECT_FALSE(video_frame_.get());
}
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) {
Initialize();
ChangeDimensions(kWidth * 2, kHeight);
Decode();
}
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) {
Initialize();
ChangeDimensions(kWidth / 2, kHeight);
Decode();
}
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) {
Initialize();
ChangeDimensions(kWidth, kHeight * 2);
Decode();
}
TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) {
Initialize();
ChangeDimensions(kWidth, kHeight / 2);
Decode();
}
TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) {
// YV12 formats.
codec_context_.pix_fmt = PIX_FMT_YUV420P;