1 // Copyright (c) 2012 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 <stdint.h>
6
7 #include <list>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/singleton.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/test/gmock_callback_support.h"
19 #include "base/test/task_environment.h"
20 #include "media/base/decoder_buffer.h"
21 #include "media/base/limits.h"
22 #include "media/base/media_log.h"
23 #include "media/base/media_util.h"
24 #include "media/base/mock_filters.h"
25 #include "media/base/mock_media_log.h"
26 #include "media/base/test_data_util.h"
27 #include "media/base/test_helpers.h"
28 #include "media/base/video_decoder.h"
29 #include "media/base/video_frame.h"
30 #include "media/base/video_util.h"
31 #include "media/ffmpeg/ffmpeg_common.h"
32 #include "media/filters/ffmpeg_video_decoder.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34
35 using ::testing::_;
36 using ::testing::AtLeast;
37 using ::testing::AtMost;
38 using ::testing::InSequence;
39 using ::testing::IsNull;
40 using ::testing::Return;
41 using ::testing::SaveArg;
42 using ::testing::StrictMock;
43
44 namespace media {
45
46 static const gfx::Size kCodedSize(320, 240);
47 static const gfx::Rect kVisibleRect(320, 240);
48 static const gfx::Size kNaturalSize(320, 240);
49
ACTION_P(ReturnBuffer,buffer)50 ACTION_P(ReturnBuffer, buffer) {
51 arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
52 }
53
54 MATCHER(ContainsFailedToSendLog, "") {
55 return CONTAINS_STRING(arg, "Failed to send");
56 }
57
58 class FFmpegVideoDecoderTest : public testing::Test {
59 public:
FFmpegVideoDecoderTest()60 FFmpegVideoDecoderTest() : decoder_(new FFmpegVideoDecoder(&media_log_)) {
61 // Initialize various test buffers.
62 frame_buffer_.reset(new uint8_t[kCodedSize.GetArea()]);
63 end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
64 i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240");
65 corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame");
66 }
67
~FFmpegVideoDecoderTest()68 ~FFmpegVideoDecoderTest() override { Destroy(); }
69
Initialize()70 void Initialize() {
71 InitializeWithConfig(TestVideoConfig::Normal());
72 }
73
InitializeWithConfigWithResult(const VideoDecoderConfig & config,bool success)74 void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
75 bool success) {
76 decoder_->Initialize(
77 config, false, nullptr,
78 base::BindOnce(
79 [](bool success, Status status) {
80 EXPECT_EQ(status.is_ok(), success);
81 },
82 success),
83 base::BindRepeating(&FFmpegVideoDecoderTest::FrameReady,
84 base::Unretained(this)),
85 base::NullCallback());
86 base::RunLoop().RunUntilIdle();
87 }
88
InitializeWithConfig(const VideoDecoderConfig & config)89 void InitializeWithConfig(const VideoDecoderConfig& config) {
90 InitializeWithConfigWithResult(config, true);
91 }
92
Reinitialize()93 void Reinitialize() {
94 InitializeWithConfig(TestVideoConfig::Large());
95 }
96
Reset()97 void Reset() {
98 decoder_->Reset(NewExpectedClosure());
99 base::RunLoop().RunUntilIdle();
100 }
101
Destroy()102 void Destroy() {
103 decoder_.reset();
104 base::RunLoop().RunUntilIdle();
105 }
106
107 // Sets up expectations and actions to put FFmpegVideoDecoder in an active
108 // decoding state.
EnterDecodingState()109 void EnterDecodingState() {
110 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
111 ASSERT_EQ(1U, output_frames_.size());
112 }
113
114 // Sets up expectations and actions to put FFmpegVideoDecoder in an end
115 // of stream state.
EnterEndOfStreamState()116 void EnterEndOfStreamState() {
117 EXPECT_TRUE(DecodeSingleFrame(end_of_stream_buffer_).is_ok());
118 ASSERT_FALSE(output_frames_.empty());
119 }
120
121 typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
122 typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
123
124 // Decodes all buffers in |input_buffers| and push all successfully decoded
125 // output frames into |output_frames|.
126 // Returns the last decode status returned by the decoder.
DecodeMultipleFrames(const InputBuffers & input_buffers)127 Status DecodeMultipleFrames(const InputBuffers& input_buffers) {
128 for (auto iter = input_buffers.begin(); iter != input_buffers.end();
129 ++iter) {
130 Status status = Decode(*iter);
131 switch (status.code()) {
132 case StatusCode::kOk:
133 break;
134 case StatusCode::kAborted:
135 NOTREACHED();
136 FALLTHROUGH;
137 default:
138 DCHECK(output_frames_.empty());
139 return status;
140 }
141 }
142 return StatusCode::kOk;
143 }
144
145 // Decodes the single compressed frame in |buffer| and writes the
146 // uncompressed output to |video_frame|. This method works with single
147 // and multithreaded decoders. End of stream buffers are used to trigger
148 // the frame to be returned in the multithreaded decoder case.
DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer)149 Status DecodeSingleFrame(scoped_refptr<DecoderBuffer> buffer) {
150 InputBuffers input_buffers;
151 input_buffers.push_back(buffer);
152 input_buffers.push_back(end_of_stream_buffer_);
153
154 return DecodeMultipleFrames(input_buffers);
155 }
156
157 // Decodes |i_frame_buffer_| and then decodes the data contained in
158 // the file named |test_file_name|. This function expects both buffers
159 // to decode to frames that are the same size.
DecodeIFrameThenTestFile(const std::string & test_file_name,int expected_width,int expected_height)160 void DecodeIFrameThenTestFile(const std::string& test_file_name,
161 int expected_width,
162 int expected_height) {
163 Initialize();
164 scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
165
166 InputBuffers input_buffers;
167 input_buffers.push_back(i_frame_buffer_);
168 input_buffers.push_back(buffer);
169 input_buffers.push_back(end_of_stream_buffer_);
170
171 Status status = DecodeMultipleFrames(input_buffers);
172
173 EXPECT_TRUE(status.is_ok());
174 ASSERT_EQ(2U, output_frames_.size());
175
176 gfx::Size original_size = kVisibleRect.size();
177 EXPECT_EQ(original_size.width(),
178 output_frames_[0]->visible_rect().size().width());
179 EXPECT_EQ(original_size.height(),
180 output_frames_[0]->visible_rect().size().height());
181 EXPECT_EQ(expected_width,
182 output_frames_[1]->visible_rect().size().width());
183 EXPECT_EQ(expected_height,
184 output_frames_[1]->visible_rect().size().height());
185 }
186
Decode(scoped_refptr<DecoderBuffer> buffer)187 Status Decode(scoped_refptr<DecoderBuffer> buffer) {
188 Status status;
189 EXPECT_CALL(*this, DecodeDone(_)).WillOnce(SaveArg<0>(&status));
190
191 decoder_->Decode(buffer, base::BindOnce(&FFmpegVideoDecoderTest::DecodeDone,
192 base::Unretained(this)));
193
194 base::RunLoop().RunUntilIdle();
195
196 return status;
197 }
198
FrameReady(scoped_refptr<VideoFrame> frame)199 void FrameReady(scoped_refptr<VideoFrame> frame) {
200 DCHECK(!frame->metadata()->end_of_stream);
201 output_frames_.push_back(std::move(frame));
202 }
203
204 MOCK_METHOD1(DecodeDone, void(Status));
205
206 StrictMock<MockMediaLog> media_log_;
207
208 base::test::SingleThreadTaskEnvironment task_environment_;
209 std::unique_ptr<FFmpegVideoDecoder> decoder_;
210
211 // Various buffers for testing.
212 std::unique_ptr<uint8_t[]> frame_buffer_;
213 scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
214 scoped_refptr<DecoderBuffer> i_frame_buffer_;
215 scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
216
217 OutputFrames output_frames_;
218
219 private:
220 DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
221 };
222
TEST_F(FFmpegVideoDecoderTest,Initialize_Normal)223 TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
224 Initialize();
225 }
226
TEST_F(FFmpegVideoDecoderTest,Initialize_OpenDecoderFails)227 TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
228 // Specify Theora w/o extra data so that avcodec_open2() fails.
229 VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
230 VideoDecoderConfig::AlphaMode::kIsOpaque,
231 VideoColorSpace(), kNoTransformation, kCodedSize,
232 kVisibleRect, kNaturalSize, EmptyExtraData(),
233 EncryptionScheme::kUnencrypted);
234 InitializeWithConfigWithResult(config, false);
235 }
236
TEST_F(FFmpegVideoDecoderTest,Reinitialize_Normal)237 TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
238 Initialize();
239 Reinitialize();
240 }
241
TEST_F(FFmpegVideoDecoderTest,Reinitialize_AfterDecodeFrame)242 TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
243 Initialize();
244 EnterDecodingState();
245 Reinitialize();
246 }
247
TEST_F(FFmpegVideoDecoderTest,Reinitialize_AfterReset)248 TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
249 Initialize();
250 EnterDecodingState();
251 Reset();
252 Reinitialize();
253 }
254
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_Normal)255 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
256 Initialize();
257
258 // Simulate decoding a single frame.
259 EXPECT_TRUE(DecodeSingleFrame(i_frame_buffer_).is_ok());
260 ASSERT_EQ(1U, output_frames_.size());
261 }
262
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_DecodeError)263 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
264 Initialize();
265
266 EXPECT_MEDIA_LOG(ContainsFailedToSendLog());
267
268 // The error is only raised on the second decode attempt, so we expect at
269 // least one successful decode but we don't expect valid frame to be decoded.
270 // During the second decode attempt an error is raised.
271 EXPECT_TRUE(Decode(corrupt_i_frame_buffer_).is_ok());
272 EXPECT_TRUE(output_frames_.empty());
273 EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
274 EXPECT_TRUE(output_frames_.empty());
275
276 // After a decode error occurred, all following decodes will return
277 // DecodeStatus::DECODE_ERROR.
278 EXPECT_THAT(Decode(i_frame_buffer_), IsDecodeErrorStatus());
279 EXPECT_TRUE(output_frames_.empty());
280 }
281
282 // A corrupt frame followed by an EOS buffer should raise a decode error.
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_DecodeErrorAtEndOfStream)283 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
284 Initialize();
285
286 EXPECT_MEDIA_LOG(ContainsFailedToSendLog());
287
288 EXPECT_THAT(DecodeSingleFrame(corrupt_i_frame_buffer_),
289 IsDecodeErrorStatus());
290 }
291
292 // Decode |i_frame_buffer_| and then a frame with a larger width and verify
293 // the output size was adjusted.
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_LargerWidth)294 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
295 DecodeIFrameThenTestFile("vp8-I-frame-640x240", 640, 240);
296 }
297
298 // Decode |i_frame_buffer_| and then a frame with a smaller width and verify
299 // the output size was adjusted.
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_SmallerWidth)300 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
301 DecodeIFrameThenTestFile("vp8-I-frame-160x240", 160, 240);
302 }
303
304 // Decode |i_frame_buffer_| and then a frame with a larger height and verify
305 // the output size was adjusted.
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_LargerHeight)306 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
307 DecodeIFrameThenTestFile("vp8-I-frame-320x480", 320, 480);
308 }
309
310 // Decode |i_frame_buffer_| and then a frame with a smaller height and verify
311 // the output size was adjusted.
TEST_F(FFmpegVideoDecoderTest,DecodeFrame_SmallerHeight)312 TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
313 DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120);
314 }
315
316 // Test resetting when decoder has initialized but not decoded.
TEST_F(FFmpegVideoDecoderTest,Reset_Initialized)317 TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
318 Initialize();
319 Reset();
320 }
321
322 // Test resetting when decoder has decoded single frame.
TEST_F(FFmpegVideoDecoderTest,Reset_Decoding)323 TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
324 Initialize();
325 EnterDecodingState();
326 Reset();
327 }
328
329 // Test resetting when decoder has hit end of stream.
TEST_F(FFmpegVideoDecoderTest,Reset_EndOfStream)330 TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
331 Initialize();
332 EnterDecodingState();
333 EnterEndOfStreamState();
334 Reset();
335 }
336
337 // Test destruction when decoder has initialized but not decoded.
TEST_F(FFmpegVideoDecoderTest,Destroy_Initialized)338 TEST_F(FFmpegVideoDecoderTest, Destroy_Initialized) {
339 Initialize();
340 Destroy();
341 }
342
343 // Test destruction when decoder has decoded single frame.
TEST_F(FFmpegVideoDecoderTest,Destroy_Decoding)344 TEST_F(FFmpegVideoDecoderTest, Destroy_Decoding) {
345 Initialize();
346 EnterDecodingState();
347 Destroy();
348 }
349
350 // Test destruction when decoder has hit end of stream.
TEST_F(FFmpegVideoDecoderTest,Destroy_EndOfStream)351 TEST_F(FFmpegVideoDecoderTest, Destroy_EndOfStream) {
352 Initialize();
353 EnterDecodingState();
354 EnterEndOfStreamState();
355 Destroy();
356 }
357
358 } // namespace media
359