This source file includes following definitions.
- ACTION_P
 
- decode_cb_
 
- Initialize
 
- InitializeWithConfigAndStatus
 
- InitializeWithConfig
 
- Reinitialize
 
- Reset
 
- Stop
 
- EnterDecodingState
 
- EnterEndOfStreamState
 
- DecodeMultipleFrames
 
- DecodeSingleFrame
 
- DecodeIFrameThenTestFile
 
- Decode
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
- TEST_F
 
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "media/base/decoder_buffer.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/limits.h"
#include "media/base/mock_filters.h"
#include "media/base/test_data_util.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::InSequence;
using ::testing::IsNull;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace media {
static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
static const gfx::Size kCodedSize(320, 240);
static const gfx::Rect kVisibleRect(320, 240);
static const gfx::Size kNaturalSize(320, 240);
ACTION_P(ReturnBuffer, buffer) {
  arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
}
class FFmpegVideoDecoderTest : public testing::Test {
 public:
  FFmpegVideoDecoderTest()
      : decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
        decode_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
                              base::Unretained(this))) {
    FFmpegGlue::InitializeFFmpeg();
    
    frame_buffer_.reset(new uint8[kCodedSize.GetArea()]);
    end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
    i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240");
    corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame");
  }
  virtual ~FFmpegVideoDecoderTest() {
    Stop();
  }
  void Initialize() {
    InitializeWithConfig(TestVideoConfig::Normal());
  }
  void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
                                     PipelineStatus status) {
    decoder_->Initialize(config, NewExpectedStatusCB(status));
    message_loop_.RunUntilIdle();
  }
  void InitializeWithConfig(const VideoDecoderConfig& config) {
    InitializeWithConfigAndStatus(config, PIPELINE_OK);
  }
  void Reinitialize() {
    InitializeWithConfig(TestVideoConfig::Large());
  }
  void Reset() {
    decoder_->Reset(NewExpectedClosure());
    message_loop_.RunUntilIdle();
  }
  void Stop() {
    decoder_->Stop();
    message_loop_.RunUntilIdle();
  }
  
  
  void EnterDecodingState() {
    VideoDecoder::Status status;
    scoped_refptr<VideoFrame> video_frame;
    DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
    EXPECT_EQ(VideoDecoder::kOk, status);
    ASSERT_TRUE(video_frame.get());
    EXPECT_FALSE(video_frame->end_of_stream());
  }
  
  
  void EnterEndOfStreamState() {
    VideoDecoder::Status status;
    scoped_refptr<VideoFrame> video_frame;
    DecodeSingleFrame(end_of_stream_buffer_, &status, &video_frame);
    EXPECT_EQ(VideoDecoder::kOk, status);
    ASSERT_TRUE(video_frame.get());
    EXPECT_TRUE(video_frame->end_of_stream());
  }
  typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
  typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
  
  
  
  VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers,
                                            OutputFrames* output_frames) {
    InputBuffers::const_iterator input_iter = input_buffers.begin();
    for (;;) {
      
      scoped_refptr<DecoderBuffer> buffer;
      if (input_iter != input_buffers.end()) {
        buffer = *input_iter;
        ++input_iter;
      } else {
        buffer = end_of_stream_buffer_;
      }
      VideoDecoder::Status status;
      scoped_refptr<VideoFrame> frame;
      Decode(buffer, &status, &frame);
      switch (status) {
        case VideoDecoder::kOk:
          DCHECK(frame);
          if (!frame->end_of_stream()) {
            output_frames->push_back(frame);
            continue;
          } else {  
            return status;
          }
        case VideoDecoder::kNotEnoughData:
          DCHECK(!frame);
          continue;
        case VideoDecoder::kAborted:
          NOTREACHED();
        case VideoDecoder::kDecodeError:
        case VideoDecoder::kDecryptError:
          DCHECK(!frame);
          return status;
      }
    }
  }
  
  
  
  
  void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
                         VideoDecoder::Status* status,
                         scoped_refptr<VideoFrame>* video_frame) {
    InputBuffers input_buffers;
    input_buffers.push_back(buffer);
    OutputFrames output_frames;
    *status = DecodeMultipleFrames(input_buffers, &output_frames);
    if (*status != VideoDecoder::kOk)
      return;
    ASSERT_LE(output_frames.size(), 1U);
    if (output_frames.size() == 1U)
      *video_frame = output_frames[0];
    else
      *video_frame = VideoFrame::CreateEOSFrame();
  }
  
  
  
  void DecodeIFrameThenTestFile(const std::string& test_file_name,
                                int expected_width,
                                int expected_height) {
    Initialize();
    scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
    InputBuffers input_buffers;
    input_buffers.push_back(i_frame_buffer_);
    input_buffers.push_back(buffer);
    OutputFrames output_frames;
    VideoDecoder::Status status =
        DecodeMultipleFrames(input_buffers, &output_frames);
    EXPECT_EQ(VideoDecoder::kOk, status);
    ASSERT_EQ(2U, output_frames.size());
    gfx::Size original_size = kVisibleRect.size();
    EXPECT_EQ(original_size.width(),
              output_frames[0]->visible_rect().size().width());
    EXPECT_EQ(original_size.height(),
              output_frames[0]->visible_rect().size().height());
    EXPECT_EQ(expected_width,
              output_frames[1]->visible_rect().size().width());
    EXPECT_EQ(expected_height,
              output_frames[1]->visible_rect().size().height());
  }
  void Decode(const scoped_refptr<DecoderBuffer>& buffer,
              VideoDecoder::Status* status,
              scoped_refptr<VideoFrame>* video_frame) {
    EXPECT_CALL(*this, FrameReady(_, _))
        .WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame)));
    decoder_->Decode(buffer, decode_cb_);
    message_loop_.RunUntilIdle();
  }
  MOCK_METHOD2(FrameReady, void(VideoDecoder::Status,
                                const scoped_refptr<VideoFrame>&));
  base::MessageLoop message_loop_;
  scoped_ptr<FFmpegVideoDecoder> decoder_;
  VideoDecoder::DecodeCB decode_cb_;
  
  scoped_ptr<uint8_t[]> frame_buffer_;
  scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
  scoped_refptr<DecoderBuffer> i_frame_buffer_;
  scoped_refptr<DecoderBuffer> corrupt_i_frame_buffer_;
 private:
  DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoderTest);
};
TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) {
  Initialize();
}
TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedDecoder) {
  
  InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
                                DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedPixelFormat) {
  
  VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
                            VideoFrame::UNKNOWN,
                            kCodedSize, kVisibleRect, kNaturalSize,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
  
  VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, kNaturalSize,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorZero) {
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorZero) {
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorNegative) {
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorNegative) {
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorTooLarge) {
  int width = kVisibleRect.size().width();
  int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorTooLarge) {
  int den = kVisibleRect.size().width() + 1;
  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN,
                            kVideoFormat,
                            kCodedSize, kVisibleRect, natural_size,
                            NULL, 0, false);
  InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_Normal) {
  Initialize();
  Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_Failure) {
  Initialize();
  InitializeWithConfigAndStatus(TestVideoConfig::Invalid(),
                                DECODER_ERROR_NOT_SUPPORTED);
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterDecodeFrame) {
  Initialize();
  EnterDecodingState();
  Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, Reinitialize_AfterReset) {
  Initialize();
  EnterDecodingState();
  Reset();
  Reinitialize();
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_Normal) {
  Initialize();
  
  VideoDecoder::Status status;
  scoped_refptr<VideoFrame> video_frame;
  DecodeSingleFrame(i_frame_buffer_, &status, &video_frame);
  EXPECT_EQ(VideoDecoder::kOk, status);
  ASSERT_TRUE(video_frame.get());
  EXPECT_FALSE(video_frame->end_of_stream());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
  Initialize();
  scoped_refptr<DecoderBuffer> zero_byte_buffer = new DecoderBuffer(0);
  InputBuffers input_buffers;
  input_buffers.push_back(i_frame_buffer_);
  input_buffers.push_back(zero_byte_buffer);
  input_buffers.push_back(i_frame_buffer_);
  OutputFrames output_frames;
  VideoDecoder::Status status =
      DecodeMultipleFrames(input_buffers, &output_frames);
  EXPECT_EQ(VideoDecoder::kOk, status);
  ASSERT_EQ(2U, output_frames.size());
  EXPECT_FALSE(output_frames[0]->end_of_stream());
  EXPECT_FALSE(output_frames[1]->end_of_stream());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
  Initialize();
  VideoDecoder::Status status;
  scoped_refptr<VideoFrame> frame;
  
  
  
  Decode(corrupt_i_frame_buffer_, &status, &frame);
  DCHECK(!frame);
  DCHECK_EQ(VideoDecoder::kNotEnoughData, status);
  Decode(i_frame_buffer_, &status, &frame);
  DCHECK(!frame);
  DCHECK_EQ(VideoDecoder::kDecodeError, status);
  
  
  Decode(i_frame_buffer_, &status, &frame);
  DCHECK(!frame);
  DCHECK_EQ(VideoDecoder::kDecodeError, status);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeErrorAtEndOfStream) {
  Initialize();
  VideoDecoder::Status status;
  scoped_refptr<VideoFrame> video_frame;
  DecodeSingleFrame(corrupt_i_frame_buffer_, &status, &video_frame);
  EXPECT_EQ(VideoDecoder::kOk, status);
  ASSERT_TRUE(video_frame.get());
  EXPECT_TRUE(video_frame->end_of_stream());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerWidth) {
  DecodeIFrameThenTestFile("vp8-I-frame-640x240", 640, 240);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerWidth) {
  DecodeIFrameThenTestFile("vp8-I-frame-160x240", 160, 240);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_LargerHeight) {
  DecodeIFrameThenTestFile("vp8-I-frame-320x480", 320, 480);
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) {
  DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120);
}
TEST_F(FFmpegVideoDecoderTest, Reset_Initialized) {
  Initialize();
  Reset();
}
TEST_F(FFmpegVideoDecoderTest, Reset_Decoding) {
  Initialize();
  EnterDecodingState();
  Reset();
}
TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
  Initialize();
  EnterDecodingState();
  EnterEndOfStreamState();
  Reset();
}
TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
  Initialize();
  Stop();
}
TEST_F(FFmpegVideoDecoderTest, Stop_Decoding) {
  Initialize();
  EnterDecodingState();
  Stop();
}
TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
  Initialize();
  EnterDecodingState();
  EnterEndOfStreamState();
  Stop();
}
}