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, Initialize_UnsupportedPixelFormat) { | |
172 // Ensure decoder handles unsupport pixel formats without crashing. | |
173 VideoDecoderConfig config(kCodecVP8, VideoFrame::INVALID, | |
174 kCodedSize, kVisibleRect, | |
175 kFrameRate.num, kFrameRate.den, | |
176 kAspectRatio.num, kAspectRatio.den, | |
177 NULL, 0); | |
178 EXPECT_CALL(*this, OnInitializeComplete(false)); | |
179 test_engine_->Initialize(this, config); | |
180 } | |
181 | |
182 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) { | |
183 Initialize(); | |
184 | |
185 // Simulate decoding a single frame. | |
186 scoped_refptr<VideoFrame> video_frame; | |
187 DecodeASingleFrame(i_frame_buffer_, &video_frame); | |
188 | |
189 // |video_frame| timestamp is 0 because we set the timestamp based off | |
190 // the buffer timestamp. | |
191 ASSERT_TRUE(video_frame); | |
192 EXPECT_EQ(0, video_frame->GetTimestamp().ToInternalValue()); | |
193 EXPECT_EQ(10000, video_frame->GetDuration().ToInternalValue()); | |
194 } | |
195 | |
196 | |
197 // Verify current behavior for 0 byte frames. FFmpeg simply ignores | |
198 // the 0 byte frames. | |
199 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) { | |
200 Initialize(); | |
201 | |
202 scoped_refptr<DataBuffer> zero_byte_buffer = new DataBuffer(1); | |
203 | |
204 scoped_refptr<VideoFrame> video_frame_a; | |
205 scoped_refptr<VideoFrame> video_frame_b; | |
206 scoped_refptr<VideoFrame> video_frame_c; | |
207 | |
208 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
209 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
210 .WillOnce(DemuxComplete(test_engine_.get(), zero_byte_buffer)) | |
211 .WillOnce(DemuxComplete(test_engine_.get(), i_frame_buffer_)) | |
212 .WillRepeatedly(DemuxComplete(test_engine_.get(), | |
213 end_of_stream_buffer_)); | |
214 | |
215 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
216 .WillOnce(SaveArg<0>(&video_frame_a)) | |
217 .WillOnce(SaveArg<0>(&video_frame_b)) | |
218 .WillOnce(SaveArg<0>(&video_frame_c)); | |
219 CallProduceVideoFrame(); | |
220 CallProduceVideoFrame(); | |
221 CallProduceVideoFrame(); | |
222 | |
223 EXPECT_TRUE(video_frame_a); | |
224 EXPECT_TRUE(video_frame_b); | |
225 EXPECT_FALSE(video_frame_c); | |
226 } | |
227 | |
228 | |
229 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) { | |
230 Initialize(); | |
231 | |
232 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
233 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) | |
234 .WillRepeatedly(DemuxComplete(test_engine_.get(), i_frame_buffer_)); | |
235 EXPECT_CALL(*this, OnError()); | |
236 | |
237 CallProduceVideoFrame(); | |
238 } | |
239 | |
240 // Multi-threaded decoders have different behavior than single-threaded | |
241 // decoders at the end of the stream. Multithreaded decoders hide errors | |
242 // that happen on the last |codec_context_->thread_count| frames to avoid | |
243 // prematurely signalling EOS. This test just exposes that behavior so we can | |
244 // detect if it changes. | |
245 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeErrorAtEndOfStream) { | |
246 Initialize(); | |
247 | |
248 EXPECT_CALL(*this, ProduceVideoSample(_)) | |
249 .WillOnce(DemuxComplete(test_engine_.get(), corrupt_i_frame_buffer_)) | |
250 .WillRepeatedly(DemuxComplete(test_engine_.get(), end_of_stream_buffer_)); | |
251 | |
252 scoped_refptr<VideoFrame> video_frame; | |
253 EXPECT_CALL(*this, ConsumeVideoFrame(_, _)) | |
254 .WillOnce(SaveArg<0>(&video_frame)); | |
255 CallProduceVideoFrame(); | |
256 | |
257 EXPECT_FALSE(video_frame); | |
258 } | |
259 | |
260 // Decode |i_frame_buffer_| and then a frame with a larger width and verify | |
261 // the output size didn't change. | |
262 // TODO(acolwell): Fix InvalidRead detected by Valgrind | |
263 //TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerWidth) { | |
264 // DecodeIFrameThenTestFile("vp8-I-frame-640x240"); | |
265 //} | |
266 | |
267 // Decode |i_frame_buffer_| and then a frame with a smaller width and verify | |
268 // the output size didn't change. | |
269 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerWidth) { | |
270 DecodeIFrameThenTestFile("vp8-I-frame-160x240"); | |
271 } | |
272 | |
273 // Decode |i_frame_buffer_| and then a frame with a larger height and verify | |
274 // the output size didn't change. | |
275 // TODO(acolwell): Fix InvalidRead detected by Valgrind | |
276 //TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_LargerHeight) { | |
277 // DecodeIFrameThenTestFile("vp8-I-frame-320x480"); | |
278 //} | |
279 | |
280 // Decode |i_frame_buffer_| and then a frame with a smaller height and verify | |
281 // the output size didn't change. | |
282 TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { | |
283 DecodeIFrameThenTestFile("vp8-I-frame-320x120"); | |
284 } | |
285 | |
286 } // namespace media | |
OLD | NEW |