| // 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.h" |
| #include "base/run_loop.h" |
| #include "cobalt/media/base/decoder_buffer.h" |
| #include "cobalt/media/base/mock_filters.h" |
| #include "cobalt/media/base/test_helpers.h" |
| #include "cobalt/media/base/video_frame.h" |
| #include "cobalt/media/filters/fake_video_decoder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cobalt { |
| namespace media { |
| |
| static const int kTotalBuffers = 12; |
| static const int kDurationMs = 30; |
| |
| struct FakeVideoDecoderTestParams { |
| FakeVideoDecoderTestParams(int decoding_delay, int max_decode_requests) |
| : decoding_delay(decoding_delay), |
| max_decode_requests(max_decode_requests) {} |
| int decoding_delay; |
| int max_decode_requests; |
| }; |
| |
| class FakeVideoDecoderTest |
| : public testing::Test, |
| public testing::WithParamInterface<FakeVideoDecoderTestParams> { |
| public: |
| FakeVideoDecoderTest() |
| : decoder_(new FakeVideoDecoder( |
| GetParam().decoding_delay, GetParam().max_decode_requests, |
| base::Bind(&FakeVideoDecoderTest::OnBytesDecoded, |
| base::Unretained(this)))), |
| num_input_buffers_(0), |
| num_decoded_frames_(0), |
| num_bytes_decoded_(0), |
| total_bytes_in_buffers_(0), |
| last_decode_status_(DecodeStatus::OK), |
| pending_decode_requests_(0), |
| is_reset_pending_(false) {} |
| |
| virtual ~FakeVideoDecoderTest() { Destroy(); } |
| |
| void InitializeWithConfigAndExpectResult(const VideoDecoderConfig& config, |
| bool success) { |
| decoder_->Initialize( |
| config, false, NULL, NewExpectedBoolCB(success), |
| base::Bind(&FakeVideoDecoderTest::FrameReady, base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| current_config_ = config; |
| } |
| |
| void Initialize() { |
| InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), true); |
| } |
| |
| void EnterPendingInitState() { |
| decoder_->HoldNextInit(); |
| Initialize(); |
| } |
| |
| void SatisfyInit() { |
| decoder_->SatisfyInit(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Callback for VideoDecoder::Decode(). |
| void DecodeDone(DecodeStatus status) { |
| DCHECK_GT(pending_decode_requests_, 0); |
| --pending_decode_requests_; |
| last_decode_status_ = status; |
| } |
| |
| void FrameReady(const scoped_refptr<VideoFrame>& frame) { |
| DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); |
| last_decoded_frame_ = frame; |
| num_decoded_frames_++; |
| } |
| |
| void OnBytesDecoded(int count) { num_bytes_decoded_ += count; } |
| |
| enum CallbackResult { PENDING, OK, NOT_ENOUGH_DATA, ABORTED }; |
| |
| void ExpectReadResult(CallbackResult result) { |
| switch (result) { |
| case PENDING: |
| EXPECT_GT(pending_decode_requests_, 0); |
| break; |
| case OK: |
| EXPECT_EQ(0, pending_decode_requests_); |
| ASSERT_EQ(DecodeStatus::OK, last_decode_status_); |
| ASSERT_TRUE(last_decoded_frame_.get()); |
| break; |
| case NOT_ENOUGH_DATA: |
| EXPECT_EQ(0, pending_decode_requests_); |
| ASSERT_EQ(DecodeStatus::OK, last_decode_status_); |
| ASSERT_FALSE(last_decoded_frame_.get()); |
| break; |
| case ABORTED: |
| EXPECT_EQ(0, pending_decode_requests_); |
| ASSERT_EQ(DecodeStatus::ABORTED, last_decode_status_); |
| EXPECT_FALSE(last_decoded_frame_.get()); |
| 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)); |
| total_bytes_in_buffers_ += buffer->data_size(); |
| } else { |
| buffer = DecoderBuffer::CreateEOSBuffer(); |
| } |
| |
| ++num_input_buffers_; |
| ++pending_decode_requests_; |
| |
| decoder_->Decode(buffer, base::Bind(&FakeVideoDecoderTest::DecodeDone, |
| base::Unretained(this))); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void ReadOneFrame() { |
| last_decoded_frame_ = NULL; |
| do { |
| Decode(); |
| } while (!last_decoded_frame_.get() && pending_decode_requests_ == 0); |
| } |
| |
| void ReadAllFrames() { |
| do { |
| Decode(); |
| } while (num_input_buffers_ <= kTotalBuffers); // All input buffers + EOS. |
| } |
| |
| void EnterPendingReadState() { |
| // Pass the initial NOT_ENOUGH_DATA stage. |
| ReadOneFrame(); |
| decoder_->HoldDecode(); |
| ReadOneFrame(); |
| ExpectReadResult(PENDING); |
| } |
| |
| void SatisfyDecodeAndExpect(CallbackResult result) { |
| decoder_->SatisfyDecode(); |
| base::RunLoop().RunUntilIdle(); |
| ExpectReadResult(result); |
| } |
| |
| void SatisfyRead() { SatisfyDecodeAndExpect(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))); |
| base::RunLoop().RunUntilIdle(); |
| ExpectResetResult(result); |
| } |
| |
| void EnterPendingResetState() { |
| decoder_->HoldNextReset(); |
| ResetAndExpect(PENDING); |
| } |
| |
| void SatisfyReset() { |
| decoder_->SatisfyReset(); |
| base::RunLoop().RunUntilIdle(); |
| ExpectResetResult(OK); |
| } |
| |
| void Destroy() { |
| decoder_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // All pending callbacks must have been fired. |
| DCHECK_EQ(pending_decode_requests_, 0); |
| DCHECK(!is_reset_pending_); |
| } |
| |
| base::MessageLoop message_loop_; |
| VideoDecoderConfig current_config_; |
| |
| std::unique_ptr<FakeVideoDecoder> decoder_; |
| |
| int num_input_buffers_; |
| int num_decoded_frames_; |
| int num_bytes_decoded_; |
| int total_bytes_in_buffers_; |
| |
| // Callback result/status. |
| DecodeStatus last_decode_status_; |
| scoped_refptr<VideoFrame> last_decoded_frame_; |
| int pending_decode_requests_; |
| bool is_reset_pending_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FakeVideoDecoderTest); |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(NoParallelDecode, FakeVideoDecoderTest, |
| ::testing::Values(FakeVideoDecoderTestParams(9, 1), |
| FakeVideoDecoderTestParams(0, 1))); |
| INSTANTIATE_TEST_CASE_P(ParallelDecode, FakeVideoDecoderTest, |
| ::testing::Values(FakeVideoDecoderTestParams(9, 3), |
| FakeVideoDecoderTestParams(0, 3))); |
| |
| TEST_P(FakeVideoDecoderTest, Initialize) { Initialize(); } |
| |
| TEST_P(FakeVideoDecoderTest, SimulateFailureToInitialize) { |
| decoder_->SimulateFailureToInit(); |
| InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), false); |
| Decode(); |
| EXPECT_EQ(last_decode_status_, DecodeStatus::DECODE_ERROR); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_AllFrames) { |
| Initialize(); |
| ReadAllFrames(); |
| EXPECT_EQ(kTotalBuffers, num_decoded_frames_); |
| EXPECT_EQ(total_bytes_in_buffers_, num_bytes_decoded_); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_DecodingDelay) { |
| Initialize(); |
| |
| while (num_input_buffers_ < kTotalBuffers) { |
| ReadOneFrame(); |
| EXPECT_EQ(num_input_buffers_, |
| num_decoded_frames_ + GetParam().decoding_delay); |
| } |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_ZeroDelay) { |
| decoder_.reset(new FakeVideoDecoder( |
| 0, 1, base::Bind(&FakeVideoDecoderTest::OnBytesDecoded, |
| base::Unretained(this)))); |
| Initialize(); |
| |
| while (num_input_buffers_ < kTotalBuffers) { |
| ReadOneFrame(); |
| EXPECT_EQ(num_input_buffers_, num_decoded_frames_); |
| } |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_Pending_NotEnoughData) { |
| if (GetParam().decoding_delay < 1) return; |
| |
| Initialize(); |
| decoder_->HoldDecode(); |
| ReadOneFrame(); |
| ExpectReadResult(PENDING); |
| SatisfyDecodeAndExpect(NOT_ENOUGH_DATA); |
| |
| // Verify that FrameReady() hasn't been called. |
| EXPECT_FALSE(last_decoded_frame_.get()); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_Pending_OK) { |
| Initialize(); |
| EnterPendingReadState(); |
| SatisfyDecodeAndExpect(OK); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Read_Parallel) { |
| if (GetParam().max_decode_requests < 2) return; |
| |
| Initialize(); |
| decoder_->HoldDecode(); |
| for (int i = 0; i < GetParam().max_decode_requests; ++i) { |
| ReadOneFrame(); |
| ExpectReadResult(PENDING); |
| } |
| EXPECT_EQ(GetParam().max_decode_requests, pending_decode_requests_); |
| SatisfyDecodeAndExpect(GetParam().max_decode_requests > |
| GetParam().decoding_delay |
| ? OK |
| : NOT_ENOUGH_DATA); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, ReadWithHold_DecodingDelay) { |
| Initialize(); |
| |
| // Hold all decodes and satisfy one decode at a time. |
| decoder_->HoldDecode(); |
| int num_decodes_satisfied = 0; |
| while (num_decoded_frames_ == 0) { |
| while (pending_decode_requests_ < decoder_->GetMaxDecodeRequests()) |
| Decode(); |
| decoder_->SatisfySingleDecode(); |
| ++num_decodes_satisfied; |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| DCHECK_EQ(num_decoded_frames_, 1); |
| DCHECK_EQ(num_decodes_satisfied, GetParam().decoding_delay + 1); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Reinitialize) { |
| Initialize(); |
| ReadOneFrame(); |
| InitializeWithConfigAndExpectResult(TestVideoConfig::Large(), true); |
| ReadOneFrame(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, SimulateFailureToReinitialize) { |
| Initialize(); |
| ReadOneFrame(); |
| decoder_->SimulateFailureToInit(); |
| InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), false); |
| Decode(); |
| EXPECT_EQ(last_decode_status_, DecodeStatus::DECODE_ERROR); |
| } |
| |
| // Reinitializing the decoder during the middle of the decoding process can |
| // cause dropped frames. |
| TEST_P(FakeVideoDecoderTest, Reinitialize_FrameDropped) { |
| if (GetParam().decoding_delay < 1) return; |
| |
| Initialize(); |
| ReadOneFrame(); |
| Initialize(); |
| ReadAllFrames(); |
| EXPECT_LT(num_decoded_frames_, kTotalBuffers); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Reset) { |
| Initialize(); |
| ReadOneFrame(); |
| ResetAndExpect(OK); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Reset_DuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| ResetAndExpect(PENDING); |
| SatisfyDecodeAndExpect(ABORTED); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Reset_Pending) { |
| Initialize(); |
| EnterPendingResetState(); |
| SatisfyReset(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Reset_PendingDuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingResetState(); |
| SatisfyDecodeAndExpect(ABORTED); |
| SatisfyReset(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Destroy) { |
| Initialize(); |
| ReadOneFrame(); |
| ExpectReadResult(OK); |
| Destroy(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingInitialization) { |
| EnterPendingInitState(); |
| Destroy(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingRead) { |
| Initialize(); |
| EnterPendingReadState(); |
| Destroy(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingReset) { |
| Initialize(); |
| EnterPendingResetState(); |
| Destroy(); |
| } |
| |
| TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingReadAndPendingReset) { |
| Initialize(); |
| EnterPendingReadState(); |
| EnterPendingResetState(); |
| Destroy(); |
| } |
| |
| } // namespace media |
| } // namespace cobalt |