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 <string>
6#include <vector>
7
8#include "base/bind.h"
9#include "base/callback_helpers.h"
10#include "base/memory/singleton.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/string_util.h"
13#include "media/base/decoder_buffer.h"
14#include "media/base/gmock_callback_support.h"
15#include "media/base/limits.h"
16#include "media/base/mock_filters.h"
17#include "media/base/test_data_util.h"
18#include "media/base/test_helpers.h"
19#include "media/base/video_decoder.h"
20#include "media/base/video_frame.h"
21#include "media/base/video_util.h"
22#include "media/ffmpeg/ffmpeg_common.h"
23#include "media/filters/ffmpeg_glue.h"
24#include "media/filters/ffmpeg_video_decoder.h"
25#include "testing/gmock/include/gmock/gmock.h"
26
27using ::testing::_;
28using ::testing::AtLeast;
29using ::testing::InSequence;
30using ::testing::IsNull;
31using ::testing::Return;
32using ::testing::SaveArg;
33using ::testing::StrictMock;
34
35namespace media {
36
37static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
38static const gfx::Size kCodedSize(320, 240);
39static const gfx::Rect kVisibleRect(320, 240);
40static const gfx::Size kNaturalSize(320, 240);
41
42ACTION_P(ReturnBuffer, buffer) {
43  arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
44}
45
46class FFmpegVideoDecoderTest : public testing::Test {
47 public:
48  FFmpegVideoDecoderTest()
49      : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
50        decode_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
51                              base::Unretained(this))) {
52    FFmpegGlue::InitializeFFmpeg();
53
54    // Initialize various test buffers.
55    frame_buffer_.reset(new uint8[kCodedSize.GetArea()]);
56    end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
57    i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240");
58    corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame");
59  }
60
61  virtual ~FFmpegVideoDecoderTest() {
62    Stop();
63  }
64
65  void Initialize() {
66    InitializeWithConfig(TestVideoConfig::Normal());
67  }
68
69  void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
70                                     PipelineStatus status) {
71    decoder_->Initialize(config, NewExpectedStatusCB(status));
72    message_loop_.RunUntilIdle();
73  }
74
75  void InitializeWithConfig(const VideoDecoderConfig& config) {
76    InitializeWithConfigAndStatus(config, PIPELINE_OK);
77  }
78
79  void Reinitialize() {
80    InitializeWithConfig(TestVideoConfig::Large());
81  }
82
83  void Reset() {
84    decoder_->Reset(NewExpectedClosure());
85    message_loop_.RunUntilIdle();
86  }
87
88  void Stop() {
89    decoder_->Stop(NewExpectedClosure());
90    message_loop_.RunUntilIdle();
91  }
92
93  // Sets up expectations and actions to put FFmpegVideoDecoder in an active
94  // decoding state.
95  void EnterDecodingState() {
96    VideoDecoder::Status status;
97    scoped_refptr<VideoFrame> video_frame;
98    DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
99
100    EXPECT_EQ(VideoDecoder::kOk, status);
101    ASSERT_TRUE(video_frame.get());
102    EXPECT_FALSE(video_frame->end_of_stream());
103  }
104
105  // Sets up expectations and actions to put FFmpegVideoDecoder in an end
106  // of stream state.
107  void EnterEndOfStreamState() {
108    VideoDecoder::Status status;
109    scoped_refptr<VideoFrame> video_frame;
110    DecodeSingleFrame(end_of_stream_buffer_, &status, &video_frame);
111    EXPECT_EQ(VideoDecoder::kOk, status);
112    ASSERT_TRUE(video_frame.get());
113    EXPECT_TRUE(video_frame->end_of_stream());
114  }
115
116  typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
117  typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
118
119  // Decodes all buffers in |input_buffers| and push all successfully decoded
120  // output frames (excluding EOS frames) into |output_frames|.
121  // Returns the last decode status returned by the decoder.
122  VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers,
123                                            OutputFrames* output_frames) {
124    InputBuffers::const_iterator input_iter = input_buffers.begin();
125
126    for (;;) {
127      // Prepare input buffer.
128      scoped_refptr<DecoderBuffer> buffer;
129      if (input_iter != input_buffers.end()) {
130        buffer = *input_iter;
131        ++input_iter;
132      } else {
133        buffer = end_of_stream_buffer_;
134      }
135
136      VideoDecoder::Status status;
137      scoped_refptr<VideoFrame> frame;
138      Decode(buffer, &status, &frame);
139
140      switch (status) {
141        case VideoDecoder::kOk:
142          DCHECK(frame);
143          if (!frame->end_of_stream()) {
144            output_frames->push_back(frame);
145            continue;
146          } else {  // EOS
147            return status;
148          }
149        case VideoDecoder::kNotEnoughData:
150          DCHECK(!frame);
151          continue;
152        case VideoDecoder::kDecodeError:
153        case VideoDecoder::kDecryptError:
154          DCHECK(!frame);
155          return status;
156      }
157    }
158  }
159
160  // Decodes the single compressed frame in |buffer| and writes the
161  // uncompressed output to |video_frame|. This method works with single
162  // and multithreaded decoders. End of stream buffers are used to trigger
163  // the frame to be returned in the multithreaded decoder case.
164  void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
165                         VideoDecoder::Status* status,
166                         scoped_refptr<VideoFrame>* video_frame) {
167    InputBuffers input_buffers;
168    input_buffers.push_back(buffer);
169
170    OutputFrames output_frames;
171    *status = DecodeMultipleFrames(input_buffers, &output_frames);
172
173    if (*status != VideoDecoder::kOk)
174      return;
175
176    ASSERT_LE(output_frames.size(), 1U);
177    if (output_frames.size() == 1U)
178      *video_frame = output_frames[0];
179    else
180      *video_frame = VideoFrame::CreateEOSFrame();
181  }
182
183  // Decodes |i_frame_buffer_| and then decodes the data contained in
184  // the file named |test_file_name|. This function expects both buffers
185  // to decode to frames that are the same size.
186  void DecodeIFrameThenTestFile(const std::string& test_file_name,
187                                int expected_width,
188                                int expected_height) {
189    Initialize();
190    scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
191
192    InputBuffers input_buffers;
193    input_buffers.push_back(i_frame_buffer_);
194    input_buffers.push_back(buffer);
195
196    OutputFrames output_frames;
197    VideoDecoder::Status status =
198        DecodeMultipleFrames(input_buffers, &output_frames);
199
200    EXPECT_EQ(VideoDecoder::kOk, status);
201    ASSERT_EQ(2U, output_frames.size());
202
203    gfx::Size original_size = kVisibleRect.size();
204    EXPECT_EQ(original_size.width(),
205              output_frames[0]->visible_rect().size().width());
206    EXPECT_EQ(original_size.height(),
207              output_frames[0]->visible_rect().size().height());
208    EXPECT_EQ(expected_width,
209              output_frames[1]->visible_rect().size().width());
210    EXPECT_EQ(expected_height,
211              output_frames[1]->visible_rect().size().height());
212  }
213
214  void Decode(const scoped_refptr<DecoderBuffer>& buffer,
215              VideoDecoder::Status* status,
216              scoped_refptr<VideoFrame>* video_frame) {
217    EXPECT_CALL(*this, FrameReady(_, _))
218        .WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame)));
219
220    decoder_->Decode(buffer, decode_cb_);
221
222    message_loop_.RunUntilIdle();
223  }
224
225  MOCK_METHOD2(FrameReady, void(VideoDecoder::Status,
226                                const scoped_refptr<VideoFrame>&));
227
228  base::MessageLoop message_loop_;
229  scoped_ptr<FFmpegVideoDecoder> decoder_;
230
231  VideoDecoder::DecodeCB decode_cb_;
232
233  // Various buffers for testing.
234  scoped_ptr<uint8_t[]> frame_buffer_;
235  scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
236  scoped_refptr<DecoderBuffer> i_frame_buffer_;
237  scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
238
239 private:
240  DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
241};
242
243TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
244  Initialize();
245}
246
247TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedDecoder) {
248  // Test avcodec_find_decoder() returning NULL.
249  InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
250                                DECODER_ERROR_NOT_SUPPORTED);
251}
252
253TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedPixelFormat) {
254  // Ensure decoder handles unsupported pixel formats without crashing.
255  VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
256                            VideoFrame::UNKNOWN,
257                            kCodedSize, kVisibleRect, kNaturalSize,
258                            NULL, 0, false);
259  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
260}
261
262TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
263  // Specify Theora w/o extra data so that avcodec_open2() fails.
264  VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
265                            kVideoFormat,
266                            kCodedSize, kVisibleRect, kNaturalSize,
267                            NULL, 0, false);
268  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
269}
270
271TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorZero) {
272  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
273  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
274                            kVideoFormat,
275                            kCodedSize, kVisibleRect, natural_size,
276                            NULL, 0, false);
277  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
278}
279
280TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorZero) {
281  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
282  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
283                            kVideoFormat,
284                            kCodedSize, kVisibleRect, natural_size,
285                            NULL, 0, false);
286  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
287}
288
289TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorNegative) {
290  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
291  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
292                            kVideoFormat,
293                            kCodedSize, kVisibleRect, natural_size,
294                            NULL, 0, false);
295  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
296}
297
298TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorNegative) {
299  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
300  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
301                            kVideoFormat,
302                            kCodedSize, kVisibleRect, natural_size,
303                            NULL, 0, false);
304  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
305}
306
307TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorTooLarge) {
308  int width = kVisibleRect.size().width();
309  int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
310  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
311  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
312                            kVideoFormat,
313                            kCodedSize, kVisibleRect, natural_size,
314                            NULL, 0, false);
315  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
316}
317
318TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorTooLarge) {
319  int den = kVisibleRect.size().width() + 1;
320  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
321  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
322                            kVideoFormat,
323                            kCodedSize, kVisibleRect, natural_size,
324                            NULL, 0, false);
325  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
326}
327
328TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
329  Initialize();
330  Reinitialize();
331}
332
333TEST_F(FFmpegVideoDecoderTest, Reinitialize_Failure) {
334  Initialize();
335  InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
336                                DECODER_ERROR_NOT_SUPPORTED);
337}
338
339TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
340  Initialize();
341  EnterDecodingState();
342  Reinitialize();
343}
344
345TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
346  Initialize();
347  EnterDecodingState();
348  Reset();
349  Reinitialize();
350}
351
352TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
353  Initialize();
354
355  // Simulate decoding a single frame.
356  VideoDecoder::Status status;
357  scoped_refptr<VideoFrame> video_frame;
358  DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
359
360  EXPECT_EQ(VideoDecoder::kOk, status);
361  ASSERT_TRUE(video_frame.get());
362  EXPECT_FALSE(video_frame->end_of_stream());
363}
364
365// Verify current behavior for 0 byte frames. FFmpeg simply ignores
366// the 0 byte frames.
367TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
368  Initialize();
369
370  scoped_refptr<DecoderBuffer> zero_byte_buffer = new DecoderBuffer(0);
371
372  InputBuffers input_buffers;
373  input_buffers.push_back(i_frame_buffer_);
374  input_buffers.push_back(zero_byte_buffer);
375  input_buffers.push_back(i_frame_buffer_);
376
377  OutputFrames output_frames;
378  VideoDecoder::Status status =
379      DecodeMultipleFrames(input_buffers, &output_frames);
380
381  EXPECT_EQ(VideoDecoder::kOk, status);
382  ASSERT_EQ(2U, output_frames.size());
383
384  EXPECT_FALSE(output_frames[0]->end_of_stream());
385  EXPECT_FALSE(output_frames[1]->end_of_stream());
386}
387
388TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
389  Initialize();
390
391  VideoDecoder::Status status;
392  scoped_refptr<VideoFrame> frame;
393
394  // The error is only raised on the second decode attempt, so we expect at
395  // least one successful decode but we don't expect valid frame to be decoded.
396  // During the second decode attempt an error is raised.
397  Decode(corrupt_i_frame_buffer_, &status, &frame);
398  DCHECK(!frame);
399  DCHECK_EQ(VideoDecoder::kNotEnoughData, status);
400  Decode(i_frame_buffer_, &status, &frame);
401  DCHECK(!frame);
402  DCHECK_EQ(VideoDecoder::kDecodeError, status);
403
404  // After a decode error occurred, all following decodes will return
405  // kDecodeError.
406  Decode(i_frame_buffer_, &status, &frame);
407  DCHECK(!frame);
408  DCHECK_EQ(VideoDecoder::kDecodeError, status);
409}
410
411// Multi-threaded decoders have different behavior than single-threaded
412// decoders at the end of the stream. Multithreaded decoders hide errors
413// that happen on the last |codec_context_->thread_count| frames to avoid
414// prematurely signalling EOS. This test just exposes that behavior so we can
415// detect if it changes.
416TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
417  Initialize();
418
419  VideoDecoder::Status status;
420  scoped_refptr<VideoFrame> video_frame;
421  DecodeSingleFrame(corrupt_i_frame_buffer_, &status, &video_frame);
422
423  EXPECT_EQ(VideoDecoder::kOk, status);
424  ASSERT_TRUE(video_frame.get());
425  EXPECT_TRUE(video_frame->end_of_stream());
426}
427
428// Decode |i_frame_buffer_| and then a frame with a larger width and verify
429// the output size was adjusted.
430TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
431  DecodeIFrameThenTestFile("vp8-I-frame-640x240", 640, 240);
432}
433
434// Decode |i_frame_buffer_| and then a frame with a smaller width and verify
435// the output size was adjusted.
436TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
437  DecodeIFrameThenTestFile("vp8-I-frame-160x240", 160, 240);
438}
439
440// Decode |i_frame_buffer_| and then a frame with a larger height and verify
441// the output size was adjusted.
442TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
443  DecodeIFrameThenTestFile("vp8-I-frame-320x480", 320, 480);
444}
445
446// Decode |i_frame_buffer_| and then a frame with a smaller height and verify
447// the output size was adjusted.
448TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
449  DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120);
450}
451
452// Test resetting when decoder has initialized but not decoded.
453TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
454  Initialize();
455  Reset();
456}
457
458// Test resetting when decoder has decoded single frame.
459TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
460  Initialize();
461  EnterDecodingState();
462  Reset();
463}
464
465// Test resetting when decoder has hit end of stream.
466TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
467  Initialize();
468  EnterDecodingState();
469  EnterEndOfStreamState();
470  Reset();
471}
472
473// Test stopping when decoder has initialized but not decoded.
474TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
475  Initialize();
476  Stop();
477}
478
479// Test stopping when decoder has decoded single frame.
480TEST_F(FFmpegVideoDecoderTest, Stop_Decoding) {
481  Initialize();
482  EnterDecodingState();
483  Stop();
484}
485
486// Test stopping when decoder has hit end of stream.
487TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
488  Initialize();
489  EnterDecodingState();
490  EnterEndOfStreamState();
491  Stop();
492}
493
494}  // namespace media
495