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:
@@ -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;
|
||||
|
Reference in New Issue
Block a user