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