| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/message_loop/message_loop.h" |
| #include "media/base/decoder_buffer.h" |
| #include "media/base/mock_filters.h" |
| #include "media/base/test_helpers.h" |
| #include "media/base/video_frame.h" |
| #include "media/filters/fake_video_decoder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| |
| static const int kDecodingDelay = 9; |
| static const int kTotalBuffers = 12; |
| static const int kDurationMs = 30; |
| |
| class FakeVideoDecoderTest : public testing::Test { |
| public: |
| FakeVideoDecoderTest() |
| : decoder_(new FakeVideoDecoder(kDecodingDelay)), |
| num_input_buffers_(0), |
| num_decoded_frames_(0), |
| decode_status_(VideoDecoder::kNotEnoughData), |
| is_decode_pending_(false), |
| is_reset_pending_(false), |
| is_stop_pending_(false) {} |
| |
| virtual ~FakeVideoDecoderTest() { |
| StopAndExpect(OK); |
| } |
| |
| void InitializeWithConfig(const VideoDecoderConfig& config) { |
| decoder_->Initialize(config, NewExpectedStatusCB(PIPELINE_OK)); |
| message_loop_.RunUntilIdle(); |
| current_config_ = config; |
| } |
| |
| void Initialize() { |
| InitializeWithConfig(TestVideoConfig::Normal()); |
| } |
| |
| void EnterPendingInitState() { |
| decoder_->HoldNextInit(); |
| Initialize(); |
| } |
| |
| void SatisfyInit() { |
| decoder_->SatisfyInit(); |
| message_loop_.RunUntilIdle(); |
| } |
| |
| // Callback for VideoDecoder::Read(). |
| void FrameReady(VideoDecoder::Status status, |
| const scoped_refptr<VideoFrame>& frame) { |
| DCHECK(is_decode_pending_); |
| ASSERT_TRUE(status == VideoDecoder::kOk || |
| status == VideoDecoder::kNotEnoughData); |
| is_decode_pending_ = false; |
| decode_status_ = status; |
| frame_decoded_ = frame; |
| |
| if (frame && !frame->end_of_stream()) |
| num_decoded_frames_++; |
| } |
| |
| enum CallbackResult { |
| PENDING, |
| OK, |
| NOT_ENOUGH_DATA, |
| ABROTED, |
| EOS |
| }; |
| |
| void ExpectReadResult(CallbackResult result) { |
| switch (result) { |
| case PENDING: |
| EXPECT_TRUE(is_decode_pending_); |
| ASSERT_FALSE(frame_decoded_); |
| break; |
| case OK: |
| EXPECT_FALSE(is_decode_pending_); |
| ASSERT_EQ(VideoDecoder::kOk, decode_status_); |
| ASSERT_TRUE(frame_decoded_); |
| EXPECT_FALSE(frame_decoded_->end_of_stream()); |
| break; |
| case NOT_ENOUGH_DATA: |
| EXPECT_FALSE(is_decode_pending_); |
| ASSERT_EQ(VideoDecoder::kNotEnoughData, decode_status_); |
| ASSERT_FALSE(frame_decoded_); |
| break; |
| case ABROTED: |
| EXPECT_FALSE(is_decode_pending_); |
| ASSERT_EQ(VideoDecoder::kOk, decode_status_); |
| EXPECT_FALSE(frame_decoded_); |
| break; |
| case EOS: |
| EXPECT_FALSE(is_decode_pending_); |
| ASSERT_EQ(VideoDecoder::kOk, decode_status_); |
| ASSERT_TRUE(frame_decoded_); |
| EXPECT_TRUE(frame_decoded_->end_of_stream()); |
| break; |
| } |
| } |
| |
| void Decode() { |
| scoped_refptr<DecoderBuffer> buffer; |
| |
| if (num_input_buffers_ < kTotalBuffers) { |
| buffer = CreateFakeVideoBufferForTest( |
| current_config_, |
| base::TimeDelta::FromMilliseconds(kDurationMs * num_input_buffers_), |
| base::TimeDelta::FromMilliseconds(kDurationMs)); |
| num_input_buffers_++; |
| } else { |
| buffer = DecoderBuffer::CreateEOSBuffer(); |
| } |
| |
| decode_status_ = VideoDecoder::kDecodeError; |
| frame_decoded_ = NULL; |
| is_decode_pending_ = true; |
| |
| decoder_->Decode( |
| buffer, |
| base::Bind(&FakeVideoDecoderTest::FrameReady, base::Unretained(this))); |
| message_loop_.RunUntilIdle(); |
| } |
| |
| void ReadOneFrame() { |
| do { |
| Decode(); |
| } while (decode_status_ == VideoDecoder::kNotEnoughData && |
| !is_decode_pending_); |
| } |
| |
| void ReadUntilEOS() { |
| do { |
| ReadOneFrame(); |
| } while (frame_decoded_ && !frame_decoded_->end_of_stream()); |
| } |
| |
| void EnterPendingReadState() { |
| // Pass the initial NOT_ENOUGH_DATA stage. |
| ReadOneFrame(); |
| decoder_->HoldNextRead(); |
| ReadOneFrame(); |
| ExpectReadResult(PENDING); |
| } |
| |
| void SatisfyReadAndExpect(CallbackResult result) { |
| decoder_->SatisfyRead(); |
| message_loop_.RunUntilIdle(); |
| ExpectReadResult(result); |
| } |
| |
| void SatisfyRead() { |
| SatisfyReadAndExpect(OK); |
| } |
| |
| // Callback for VideoDecoder::Reset(). |
| void OnDecoderReset() { |
| DCHECK(is_reset_pending_); |
| is_reset_pending_ = false; |
| } |
| |
| void ExpectResetResult(CallbackResult result) { |
| switch (result) { |
| case PENDING: |
| EXPECT_TRUE(is_reset_pending_); |
| break; |
| case OK: |
| EXPECT_FALSE(is_reset_pending_); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void ResetAndExpect(CallbackResult result) { |
| is_reset_pending_ = true; |
| decoder_->Reset(base::Bind(&FakeVideoDecoderTest::OnDecoderReset, |
| base::Unretained(this))); |
| message_loop_.RunUntilIdle(); |
| ExpectResetResult(result); |
| } |
| |
| void EnterPendingResetState() { |
| decoder_->HoldNextReset(); |
| ResetAndExpect(PENDING); |
| } |
| |
| void SatisfyReset() { |
| decoder_->SatisfyReset(); |
| message_loop_.RunUntilIdle(); |
| ExpectResetResult(OK); |
| } |
| |
| // Callback for VideoDecoder::Stop(). |
| void OnDecoderStopped() { |
| DCHECK(is_stop_pending_); |
| is_stop_pending_ = false; |
| } |
| |
| void ExpectStopResult(CallbackResult result) { |
| switch (result) { |
| case PENDING: |
| EXPECT_TRUE(is_stop_pending_); |
| break; |
| case OK: |
| EXPECT_FALSE(is_stop_pending_); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void StopAndExpect(CallbackResult result) { |
| is_stop_pending_ = true; |
| decoder_->Stop(base::Bind(&FakeVideoDecoderTest::OnDecoderStopped, |
| base::Unretained(this))); |
| message_loop_.RunUntilIdle(); |
| ExpectStopResult(result); |
| } |
| |
| void EnterPendingStopState() { |
| decoder_->HoldNextStop(); |
| StopAndExpect(PENDING); |
| } |
| |
| void SatisfyStop() { |
| decoder_->SatisfyStop(); |
| message_loop_.RunUntilIdle(); |
| ExpectStopResult(OK); |
| } |
| |
| base::MessageLoop message_loop_; |
| VideoDecoderConfig current_config_; |
| |
| scoped_ptr<FakeVideoDecoder> decoder_; |
| |
| int num_input_buffers_; |
| int num_decoded_frames_; |
| |
| // Callback result/status. |
| VideoDecoder::Status decode_status_; |
| scoped_refptr<VideoFrame> frame_decoded_; |
| bool is_decode_pending_; |
| bool is_reset_pending_; |
| bool is_stop_pending_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FakeVideoDecoderTest); |
| }; |
| |
| TEST_F(FakeVideoDecoderTest, Initialize) { |
| Initialize(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Read_AllFrames) { |
| Initialize(); |
| ReadUntilEOS(); |
| EXPECT_EQ(kTotalBuffers, num_decoded_frames_); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Read_DecodingDelay) { |
| Initialize(); |
| |
| while (num_input_buffers_ < kTotalBuffers) { |
| ReadOneFrame(); |
| EXPECT_EQ(num_input_buffers_, num_decoded_frames_ + kDecodingDelay); |
| } |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Read_ZeroDelay) { |
| decoder_.reset(new FakeVideoDecoder(0)); |
| Initialize(); |
| |
| while (num_input_buffers_ < kTotalBuffers) { |
| ReadOneFrame(); |
| EXPECT_EQ(num_input_buffers_, num_decoded_frames_); |
| } |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Read_Pending_NotEnoughData) { |
| Initialize(); |
| decoder_->HoldNextRead(); |
| ReadOneFrame(); |
| ExpectReadResult(PENDING); |
| SatisfyReadAndExpect(NOT_ENOUGH_DATA); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Read_Pending_OK) { |
| Initialize(); |
| ReadOneFrame(); |
| EnterPendingReadState(); |
| SatisfyReadAndExpect(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Reinitialize) { |
| Initialize(); |
| ReadOneFrame(); |
| InitializeWithConfig(TestVideoConfig::Large()); |
| ReadOneFrame(); |
| } |
| |
| // Reinitializing the decoder during the middle of the decoding process can |
| // cause dropped frames. |
| TEST_F(FakeVideoDecoderTest, Reinitialize_FrameDropped) { |
| Initialize(); |
| ReadOneFrame(); |
| Initialize(); |
| ReadUntilEOS(); |
| EXPECT_LT(num_decoded_frames_, kTotalBuffers); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Reset) { |
| Initialize(); |
| ReadOneFrame(); |
| ResetAndExpect(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Reset_DuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| ResetAndExpect(PENDING); |
| SatisfyRead(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Reset_Pending) { |
| Initialize(); |
| EnterPendingResetState(); |
| SatisfyReset(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Reset_PendingDuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingResetState(); |
| SatisfyRead(); |
| SatisfyReset(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop) { |
| Initialize(); |
| ReadOneFrame(); |
| ExpectReadResult(OK); |
| StopAndExpect(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_DuringPendingInitialization) { |
| EnterPendingInitState(); |
| EnterPendingStopState(); |
| SatisfyInit(); |
| SatisfyStop(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_DuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| StopAndExpect(PENDING); |
| SatisfyRead(); |
| ExpectStopResult(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_DuringPendingReset) { |
| Initialize(); |
| EnterPendingResetState(); |
| StopAndExpect(PENDING); |
| SatisfyReset(); |
| ExpectStopResult(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_DuringPendingReadAndPendingReset) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingResetState(); |
| StopAndExpect(PENDING); |
| SatisfyRead(); |
| SatisfyReset(); |
| ExpectStopResult(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_Pending) { |
| Initialize(); |
| decoder_->HoldNextStop(); |
| StopAndExpect(PENDING); |
| decoder_->SatisfyStop(); |
| message_loop_.RunUntilIdle(); |
| ExpectStopResult(OK); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingStopState(); |
| SatisfyRead(); |
| SatisfyStop(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingReset) { |
| Initialize(); |
| EnterPendingResetState(); |
| EnterPendingStopState(); |
| SatisfyReset(); |
| SatisfyStop(); |
| } |
| |
| TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingReadAndPendingReset) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingResetState(); |
| EnterPendingStopState(); |
| SatisfyRead(); |
| SatisfyReset(); |
| SatisfyStop(); |
| } |
| |
| } // namespace media |