| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/memory/scoped_ptr.h" | |
| 6 #include "base/message_loop.h" | |
| 7 #include "media/base/data_buffer.h" | |
| 8 #include "media/base/pipeline.h" | |
| 9 #include "media/base/test_data_util.h" | |
| 10 #include "media/filters/ffmpeg_glue.h" | |
| 11 #include "media/video/ffmpeg_video_decode_engine.h" | |
| 12 #include "testing/gmock/include/gmock/gmock.h" | |
| 13 #include "testing/gtest/include/gtest/gtest.h" | |
| 14 | |
| 15 using ::testing::_; | |
| 16 using ::testing::DoAll; | |
| 17 using ::testing::Return; | |
| 18 using ::testing::ReturnNull; | |
| 19 using ::testing::SaveArg; | |
| 20 using ::testing::SetArgumentPointee; | |
| 21 using ::testing::StrictMock; | |
| 22 | |
| 23 namespace media { | |
| 24 | |
| 25 static const VideoFrame::Format kVideoFormat = VideoFrame::YV12; | |
| 26 static const gfx::Size kCodedSize(320, 240); | |
| 27 static const gfx::Rect kVisibleRect(320, 240); | |
| 28 static const gfx::Size kNaturalSize(522, 288); | |
| 29 static const AVRational kFrameRate = { 100, 1 }; | |
| 30 static const AVRational kAspectRatio = { 1, 1 }; | |
| 31 | |
| 32 ACTION_P2(DemuxComplete, engine, buffer) { | |
| 33 engine->ConsumeVideoSample(buffer); | |
| 34 } | |
| 35 | |
| 36 class FFmpegVideoDecodeEngineTest | |
| 37 : public testing::Test, | |
| 38 public VideoDecodeEngine::EventHandler { | |
| 39 public: | |
| 40 FFmpegVideoDecodeEngineTest() | |
| 41 : config_(kCodecVP8, kVideoFormat, kCodedSize, kVisibleRect, | |
| 42 kFrameRate.num, kFrameRate.den, | |
| 43 kAspectRatio.num, kAspectRatio.den, | |
| 44 NULL, 0) { | |
| 45 CHECK(FFmpegGlue::GetInstance()); | |
| 46 | |
| 47 // Setup FFmpeg structures. | |
| 48 frame_buffer_.reset(new uint8[kCodedSize.GetArea()]); | |
| 49 | |
| 50 test_engine_.reset(new FFmpegVideoDecodeEngine()); | |
| 51 | |
| 52 ReadTestDataFile("vp8-I-frame-320x240", &i_frame_buffer_); | |
| 53 ReadTestDataFile("vp8-corrupt-I-frame", &corrupt_i_frame_buffer_); | |
| 54 | |
| 55 end_of_stream_buffer_ = new DataBuffer(0); | |
| 56 } | |
| 57 | |
| 58 ~FFmpegVideoDecodeEngineTest() { | |
| 59 test_engine_.reset(); | |
| 60 } | |
| 61 | |
| 62 void Initialize() { | |
| 63 EXPECT_CALL(*this, OnInitializeComplete(true)); | |
| 64 test_engine_->Initialize(this, config_); | |
| 65 } | |
| 66 | |
| 67 // Decodes the single compressed frame in |buffer| and writes the | |
| 68 // uncompressed output to |video_frame|. This method works with single | |
| 69 // and multithreaded decoders. End of stream buffers are used to trigger | |
| 70 // the frame to be returned in the multithreaded decoder case. | |
| 71 void DecodeASingleFrame(const scoped_refptr<Buffer>& buffer, | |
| 72 scoped_refptr<VideoFrame>* video_frame) { | |
| 73 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 74 .WillOnce(DemuxComplete(test_engine_.get(), buffer)) | |
| 75 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 76 end_of_stream_buffer_)); | |
| 77 | |
| 78 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 79 .WillOnce(SaveArg<0>(video_frame)); | |
| 80 CallProduceVideoFrame(); | |
| 81 } | |
| 82 | |
| 83 // Decodes |i_frame_buffer_| and then decodes the data contained in | |
| 84 // the file named |test_file_name|. This function expects both buffers | |
| 85 // to decode to frames that are the same size. | |
| 86 void DecodeIFrameThenTestFile(const std::string& test_file_name) { | |
| 87 Initialize(); | |
| 88 | |
| 89 scoped_refptr<VideoFrame> video_frame_a; | |
| 90 scoped_refptr<VideoFrame> video_frame_b; | |
| 91 | |
| 92 scoped_refptr<Buffer> buffer; | |
| 93 ReadTestDataFile(test_file_name, &buffer); | |
| 94 | |
| 95 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 96 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
| 97 .WillOnce(DemuxComplete(test_engine_.get(), buffer)) | |
| 98 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 99 end_of_stream_buffer_)); | |
| 100 | |
| 101 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 102 .WillOnce(SaveArg<0>(&video_frame_a)) | |
| 103 .WillOnce(SaveArg<0>(&video_frame_b)); | |
| 104 CallProduceVideoFrame(); | |
| 105 CallProduceVideoFrame(); | |
| 106 | |
| 107 size_t expected_width = static_cast<size_t>(kVisibleRect.width()); | |
| 108 size_t expected_height = static_cast<size_t>(kVisibleRect.height()); | |
| 109 | |
| 110 EXPECT_EQ(expected_width, video_frame_a->width()); | |
| 111 EXPECT_EQ(expected_height, video_frame_a->height()); | |
| 112 EXPECT_EQ(expected_width, video_frame_b->width()); | |
| 113 EXPECT_EQ(expected_height, video_frame_b->height()); | |
| 114 } | |
| 115 | |
| 116 // VideoDecodeEngine::EventHandler implementation. | |
| 117 MOCK_METHOD2(ConsumeVideoFrame, | |
| 118 void(scoped_refptr<VideoFrame>, const PipelineStatistics&)); | |
| 119 MOCK_METHOD1(ProduceVideoSample, void(scoped_refptr<Buffer>)); | |
| 120 MOCK_METHOD1(OnInitializeComplete, void(bool)); | |
| 121 MOCK_METHOD0(OnUninitializeComplete, void()); | |
| 122 MOCK_METHOD0(OnFlushComplete, void()); | |
| 123 MOCK_METHOD0(OnSeekComplete, void()); | |
| 124 MOCK_METHOD0(OnError, void()); | |
| 125 | |
| 126 void CallProduceVideoFrame() { | |
| 127 test_engine_->ProduceVideoFrame(VideoFrame::CreateFrame( | |
| 128 VideoFrame::YV12, kVisibleRect.width(), kVisibleRect.height(), | |
| 129 kNoTimestamp, kNoTimestamp)); | |
| 130 } | |
| 131 | |
| 132 protected: | |
| 133 VideoDecoderConfig config_; | |
| 134 scoped_ptr<FFmpegVideoDecodeEngine> test_engine_; | |
| 135 scoped_array<uint8_t> frame_buffer_; | |
| 136 scoped_refptr<Buffer> i_frame_buffer_; | |
| 137 scoped_refptr<Buffer> corrupt_i_frame_buffer_; | |
| 138 scoped_refptr<Buffer> end_of_stream_buffer_; | |
| 139 | |
| 140 private: | |
| 141 DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngineTest); | |
| 142 }; | |
| 143 | |
| 144 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) { | |
| 145 Initialize(); | |
| 146 } | |
| 147 | |
| 148 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { | |
| 149 VideoDecoderConfig config(kUnknownVideoCodec, kVideoFormat, | |
| 150 kCodedSize, kVisibleRect, | |
| 151 kFrameRate.num, kFrameRate.den, | |
| 152 kAspectRatio.num, kAspectRatio.den, | |
| 153 NULL, 0); | |
| 154 | |
| 155 // Test avcodec_find_decoder() returning NULL. | |
| 156 EXPECT_CALL(*this, OnInitializeComplete(false)); | |
| 157 test_engine_->Initialize(this, config); | |
| 158 } | |
| 159 | |
| 160 TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { | |
| 161 // Specify Theora w/o extra data so that avcodec_open() fails. | |
| 162 VideoDecoderConfig config(kCodecTheora, kVideoFormat, | |
| 163 kCodedSize, kVisibleRect, | |
| 164 kFrameRate.num, kFrameRate.den, | |
| 165 kAspectRatio.num, kAspectRatio.den, | |
| 166 NULL, 0); | |
| 167 EXPECT_CALL(*this, OnInitializeComplete(false)); | |
| 168 test_engine_->Initialize(this, config); | |
| 169 } | |
| 170 | |
| 171 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { | |
| 172 Initialize(); | |
| 173 | |
| 174 // Simulate decoding a single frame. | |
| 175 scoped_refptr<VideoFrame> video_frame; | |
| 176 DecodeASingleFrame(i_frame_buffer_, &video_frame); | |
| 177 | |
| 178 // |video_frame| timestamp is 0 because we set the timestamp based off | |
| 179 // the buffer timestamp. | |
| 180 ASSERT_TRUE(video_frame); | |
| 181 EXPECT_EQ(0, video_frame->GetTimestamp().ToInternalValue()); | |
| 182 EXPECT_EQ(10000, video_frame->GetDuration().ToInternalValue()); | |
| 183 } | |
| 184 | |
| 185 | |
| 186 // Verify current behavior for 0 byte frames. FFmpeg simply ignores | |
| 187 // the 0 byte frames. | |
| 188 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { | |
| 189 Initialize(); | |
| 190 | |
| 191 scoped_refptr<DataBuffer> zero_byte_buffer = new DataBuffer(1); | |
| 192 | |
| 193 scoped_refptr<VideoFrame> video_frame_a; | |
| 194 scoped_refptr<VideoFrame> video_frame_b; | |
| 195 scoped_refptr<VideoFrame> video_frame_c; | |
| 196 | |
| 197 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 198 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
| 199 .WillOnce(DemuxComplete(test_engine_.get(), zero_byte_buffer)) | |
| 200 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
| 201 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
| 202 end_of_stream_buffer_)); | |
| 203 | |
| 204 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 205 .WillOnce(SaveArg<0>(&video_frame_a)) | |
| 206 .WillOnce(SaveArg<0>(&video_frame_b)) | |
| 207 .WillOnce(SaveArg<0>(&video_frame_c)); | |
| 208 CallProduceVideoFrame(); | |
| 209 CallProduceVideoFrame(); | |
| 210 CallProduceVideoFrame(); | |
| 211 | |
| 212 EXPECT_TRUE(video_frame_a); | |
| 213 EXPECT_TRUE(video_frame_b); | |
| 214 EXPECT_FALSE(video_frame_c); | |
| 215 } | |
| 216 | |
| 217 | |
| 218 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { | |
| 219 Initialize(); | |
| 220 | |
| 221 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 222 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) | |
| 223 .WillRepeatedly(DemuxComplete(test_engine_.get(), i_frame_buffer_)); | |
| 224 EXPECT_CALL(*this, OnError()); | |
| 225 | |
| 226 CallProduceVideoFrame(); | |
| 227 } | |
| 228 | |
| 229 // Multi-threaded decoders have different behavior than single-threaded | |
| 230 // decoders at the end of the stream. Multithreaded decoders hide errors | |
| 231 // that happen on the last |codec_context_->thread_count| frames to avoid | |
| 232 // prematurely signalling EOS. This test just exposes that behavior so we can | |
| 233 // detect if it changes. | |
| 234 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeErrorAtEndOfStream) { | |
| 235 Initialize(); | |
| 236 | |
| 237 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
| 238 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) | |
| 239 .WillRepeatedly(DemuxComplete(test_engine_.get(), end_of_stream_buffer_)); | |
| 240 | |
| 241 scoped_refptr<VideoFrame> video_frame; | |
| 242 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
| 243 .WillOnce(SaveArg<0>(&video_frame)); | |
| 244 CallProduceVideoFrame(); | |
| 245 | |
| 246 EXPECT_FALSE(video_frame); | |
| 247 } | |
| 248 | |
| 249 // Decode |i_frame_buffer_| and then a frame with a larger width and verify | |
| 250 // the output size didn't change. | |
| 251 // TODO(acolwell): Fix InvalidRead detected by Valgrind | |
| 252 //TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) { | |
| 253 // DecodeIFrameThenTestFile("vp8-I-frame-640x240"); | |
| 254 //} | |
| 255 | |
| 256 // Decode |i_frame_buffer_| and then a frame with a smaller width and verify | |
| 257 // the output size didn't change. | |
| 258 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) { | |
| 259 DecodeIFrameThenTestFile("vp8-I-frame-160x240"); | |
| 260 } | |
| 261 | |
| 262 // Decode |i_frame_buffer_| and then a frame with a larger height and verify | |
| 263 // the output size didn't change. | |
| 264 // TODO(acolwell): Fix InvalidRead detected by Valgrind | |
| 265 //TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) { | |
| 266 // DecodeIFrameThenTestFile("vp8-I-frame-320x480"); | |
| 267 //} | |
| 268 | |
| 269 // Decode |i_frame_buffer_| and then a frame with a smaller height and verify | |
| 270 // the output size didn't change. | |
| 271 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { | |
| 272 DecodeIFrameThenTestFile("vp8-I-frame-320x120"); | |
| 273 } | |
| 274 | |
| 275 } // namespace media | |
| OLD | NEW |