Index: media/filters/video_renderer_impl_unittest.cc |
diff --git a/media/filters/video_renderer_impl_unittest.cc b/media/filters/video_renderer_impl_unittest.cc |
deleted file mode 100644 |
index ec0c52eb5f9fe68199ec5f2b9620ae34330c2c6a..0000000000000000000000000000000000000000 |
--- a/media/filters/video_renderer_impl_unittest.cc |
+++ /dev/null |
@@ -1,502 +0,0 @@ |
-// 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 <utility> |
- |
-#include "base/bind.h" |
-#include "base/callback.h" |
-#include "base/callback_helpers.h" |
-#include "base/debug/stack_trace.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/stl_util.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_split.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/synchronization/lock.h" |
-#include "media/base/data_buffer.h" |
-#include "media/base/gmock_callback_support.h" |
-#include "media/base/limits.h" |
-#include "media/base/mock_filters.h" |
-#include "media/base/test_helpers.h" |
-#include "media/base/video_frame.h" |
-#include "media/filters/video_renderer_impl.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-using ::testing::_; |
-using ::testing::AnyNumber; |
-using ::testing::Invoke; |
-using ::testing::NiceMock; |
-using ::testing::Return; |
-using ::testing::SaveArg; |
-using ::testing::StrictMock; |
- |
-namespace media { |
- |
-ACTION_P(RunClosure, closure) { |
- closure.Run(); |
-} |
- |
-MATCHER_P(HasTimestamp, ms, "") { |
- *result_listener << "has timestamp " << arg->timestamp().InMilliseconds(); |
- return arg->timestamp().InMilliseconds() == ms; |
-} |
- |
-class VideoRendererImplTest : public ::testing::Test { |
- public: |
- VideoRendererImplTest() |
- : decoder_(new MockVideoDecoder()), |
- demuxer_stream_(DemuxerStream::VIDEO) { |
- ScopedVector<VideoDecoder> decoders; |
- decoders.push_back(decoder_); |
- |
- renderer_.reset(new VideoRendererImpl(message_loop_.message_loop_proxy(), |
- decoders.Pass(), true, |
- new MediaLog())); |
- |
- demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal()); |
- |
- // We expect these to be called but we don't care how/when. |
- EXPECT_CALL(demuxer_stream_, Read(_)).WillRepeatedly( |
- RunCallback<0>(DemuxerStream::kOk, |
- scoped_refptr<DecoderBuffer>(new DecoderBuffer(0)))); |
- } |
- |
- virtual ~VideoRendererImplTest() {} |
- |
- void Initialize() { |
- InitializeWithLowDelay(false); |
- } |
- |
- void InitializeWithLowDelay(bool low_delay) { |
- // Monitor decodes from the decoder. |
- EXPECT_CALL(*decoder_, Decode(_, _)) |
- .WillRepeatedly(Invoke(this, &VideoRendererImplTest::DecodeRequested)); |
- |
- EXPECT_CALL(*decoder_, Reset(_)) |
- .WillRepeatedly(Invoke(this, &VideoRendererImplTest::FlushRequested)); |
- |
- // Initialize, we shouldn't have any reads. |
- InitializeRenderer(PIPELINE_OK, low_delay); |
- } |
- |
- void InitializeRenderer(PipelineStatus expected, bool low_delay) { |
- SCOPED_TRACE(base::StringPrintf("InitializeRenderer(%d)", expected)); |
- WaitableMessageLoopEvent event; |
- CallInitialize(event.GetPipelineStatusCB(), low_delay, expected); |
- event.RunAndWaitForStatus(expected); |
- } |
- |
- void CallInitialize(const PipelineStatusCB& status_cb, |
- bool low_delay, |
- PipelineStatus decoder_status) { |
- if (low_delay) |
- demuxer_stream_.set_liveness(DemuxerStream::LIVENESS_LIVE); |
- EXPECT_CALL(*decoder_, Initialize(_, _, _, _)).WillOnce( |
- DoAll(SaveArg<3>(&output_cb_), RunCallback<2>(decoder_status))); |
- renderer_->Initialize( |
- &demuxer_stream_, status_cb, media::SetDecryptorReadyCB(), |
- base::Bind(&VideoRendererImplTest::OnStatisticsUpdate, |
- base::Unretained(this)), |
- base::Bind(&StrictMock<MockCB>::BufferingStateChange, |
- base::Unretained(&mock_cb_)), |
- base::Bind(&StrictMock<MockCB>::Display, base::Unretained(&mock_cb_)), |
- ended_event_.GetClosure(), error_event_.GetPipelineStatusCB(), |
- base::Bind(&VideoRendererImplTest::GetTime, base::Unretained(this))); |
- } |
- |
- void StartPlayingFrom(int milliseconds) { |
- SCOPED_TRACE(base::StringPrintf("StartPlayingFrom(%d)", milliseconds)); |
- renderer_->StartPlayingFrom( |
- base::TimeDelta::FromMilliseconds(milliseconds)); |
- message_loop_.RunUntilIdle(); |
- } |
- |
- void Flush() { |
- SCOPED_TRACE("Flush()"); |
- WaitableMessageLoopEvent event; |
- renderer_->Flush(event.GetClosure()); |
- event.RunAndWait(); |
- } |
- |
- void Destroy() { |
- SCOPED_TRACE("Destroy()"); |
- renderer_.reset(); |
- message_loop_.RunUntilIdle(); |
- } |
- |
- // Parses a string representation of video frames and generates corresponding |
- // VideoFrame objects in |decode_results_|. |
- // |
- // Syntax: |
- // nn - Queue a decoder buffer with timestamp nn * 1000us |
- // abort - Queue an aborted read |
- // error - Queue a decoder error |
- // |
- // Examples: |
- // A clip that is four frames long: "0 10 20 30" |
- // A clip that has a decode error: "60 70 error" |
- void QueueFrames(const std::string& str) { |
- std::vector<std::string> tokens; |
- base::SplitString(str, ' ', &tokens); |
- for (size_t i = 0; i < tokens.size(); ++i) { |
- if (tokens[i] == "abort") { |
- scoped_refptr<VideoFrame> null_frame; |
- decode_results_.push_back( |
- std::make_pair(VideoDecoder::kAborted, null_frame)); |
- continue; |
- } |
- |
- if (tokens[i] == "error") { |
- scoped_refptr<VideoFrame> null_frame; |
- decode_results_.push_back( |
- std::make_pair(VideoDecoder::kDecodeError, null_frame)); |
- continue; |
- } |
- |
- int timestamp_in_ms = 0; |
- if (base::StringToInt(tokens[i], ×tamp_in_ms)) { |
- gfx::Size natural_size = TestVideoConfig::NormalCodedSize(); |
- scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame( |
- VideoFrame::YV12, |
- natural_size, |
- gfx::Rect(natural_size), |
- natural_size, |
- base::TimeDelta::FromMilliseconds(timestamp_in_ms)); |
- decode_results_.push_back(std::make_pair(VideoDecoder::kOk, frame)); |
- continue; |
- } |
- |
- CHECK(false) << "Unrecognized decoder buffer token: " << tokens[i]; |
- } |
- } |
- |
- bool IsReadPending() { |
- return !decode_cb_.is_null(); |
- } |
- |
- void WaitForError(PipelineStatus expected) { |
- SCOPED_TRACE(base::StringPrintf("WaitForError(%d)", expected)); |
- error_event_.RunAndWaitForStatus(expected); |
- } |
- |
- void WaitForEnded() { |
- SCOPED_TRACE("WaitForEnded()"); |
- ended_event_.RunAndWait(); |
- } |
- |
- void WaitForPendingRead() { |
- SCOPED_TRACE("WaitForPendingRead()"); |
- if (!decode_cb_.is_null()) |
- return; |
- |
- DCHECK(wait_for_pending_decode_cb_.is_null()); |
- |
- WaitableMessageLoopEvent event; |
- wait_for_pending_decode_cb_ = event.GetClosure(); |
- event.RunAndWait(); |
- |
- DCHECK(!decode_cb_.is_null()); |
- DCHECK(wait_for_pending_decode_cb_.is_null()); |
- } |
- |
- void SatisfyPendingRead() { |
- CHECK(!decode_cb_.is_null()); |
- CHECK(!decode_results_.empty()); |
- |
- // Post tasks for OutputCB and DecodeCB. |
- scoped_refptr<VideoFrame> frame = decode_results_.front().second; |
- if (frame.get()) |
- message_loop_.PostTask(FROM_HERE, base::Bind(output_cb_, frame)); |
- message_loop_.PostTask( |
- FROM_HERE, base::Bind(base::ResetAndReturn(&decode_cb_), |
- decode_results_.front().first)); |
- decode_results_.pop_front(); |
- } |
- |
- void SatisfyPendingReadWithEndOfStream() { |
- DCHECK(!decode_cb_.is_null()); |
- |
- // Return EOS buffer to trigger EOS frame. |
- EXPECT_CALL(demuxer_stream_, Read(_)) |
- .WillOnce(RunCallback<0>(DemuxerStream::kOk, |
- DecoderBuffer::CreateEOSBuffer())); |
- |
- // Satify pending |decode_cb_| to trigger a new DemuxerStream::Read(). |
- message_loop_.PostTask( |
- FROM_HERE, |
- base::Bind(base::ResetAndReturn(&decode_cb_), VideoDecoder::kOk)); |
- |
- WaitForPendingRead(); |
- |
- message_loop_.PostTask( |
- FROM_HERE, |
- base::Bind(base::ResetAndReturn(&decode_cb_), VideoDecoder::kOk)); |
- } |
- |
- void AdvanceTimeInMs(int time_ms) { |
- DCHECK_EQ(&message_loop_, base::MessageLoop::current()); |
- base::AutoLock l(lock_); |
- time_ += base::TimeDelta::FromMilliseconds(time_ms); |
- } |
- |
- protected: |
- // Fixture members. |
- scoped_ptr<VideoRendererImpl> renderer_; |
- MockVideoDecoder* decoder_; // Owned by |renderer_|. |
- NiceMock<MockDemuxerStream> demuxer_stream_; |
- |
- // Use StrictMock<T> to catch missing/extra callbacks. |
- class MockCB { |
- public: |
- MOCK_METHOD1(Display, void(const scoped_refptr<VideoFrame>&)); |
- MOCK_METHOD1(BufferingStateChange, void(BufferingState)); |
- }; |
- StrictMock<MockCB> mock_cb_; |
- |
- private: |
- base::TimeDelta GetTime() { |
- base::AutoLock l(lock_); |
- return time_; |
- } |
- |
- void DecodeRequested(const scoped_refptr<DecoderBuffer>& buffer, |
- const VideoDecoder::DecodeCB& decode_cb) { |
- DCHECK_EQ(&message_loop_, base::MessageLoop::current()); |
- CHECK(decode_cb_.is_null()); |
- decode_cb_ = decode_cb; |
- |
- // Wake up WaitForPendingRead() if needed. |
- if (!wait_for_pending_decode_cb_.is_null()) |
- base::ResetAndReturn(&wait_for_pending_decode_cb_).Run(); |
- |
- if (decode_results_.empty()) |
- return; |
- |
- SatisfyPendingRead(); |
- } |
- |
- void FlushRequested(const base::Closure& callback) { |
- DCHECK_EQ(&message_loop_, base::MessageLoop::current()); |
- decode_results_.clear(); |
- if (!decode_cb_.is_null()) { |
- QueueFrames("abort"); |
- SatisfyPendingRead(); |
- } |
- |
- message_loop_.PostTask(FROM_HERE, callback); |
- } |
- |
- void OnStatisticsUpdate(const PipelineStatistics& stats) {} |
- |
- base::MessageLoop message_loop_; |
- |
- // Used to protect |time_|. |
- base::Lock lock_; |
- base::TimeDelta time_; |
- |
- // Used for satisfying reads. |
- VideoDecoder::OutputCB output_cb_; |
- VideoDecoder::DecodeCB decode_cb_; |
- base::TimeDelta next_frame_timestamp_; |
- |
- WaitableMessageLoopEvent error_event_; |
- WaitableMessageLoopEvent ended_event_; |
- |
- // Run during DecodeRequested() to unblock WaitForPendingRead(). |
- base::Closure wait_for_pending_decode_cb_; |
- |
- std::deque<std::pair< |
- VideoDecoder::Status, scoped_refptr<VideoFrame> > > decode_results_; |
- |
- DISALLOW_COPY_AND_ASSIGN(VideoRendererImplTest); |
-}; |
- |
-TEST_F(VideoRendererImplTest, DoNothing) { |
- // Test that creation and deletion doesn't depend on calls to Initialize() |
- // and/or Destroy(). |
-} |
- |
-TEST_F(VideoRendererImplTest, DestroyWithoutInitialize) { |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, Initialize) { |
- Initialize(); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, InitializeAndStartPlayingFrom) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, DestroyWhileInitializing) { |
- CallInitialize(NewExpectedStatusCB(PIPELINE_ERROR_ABORT), false, PIPELINE_OK); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, DestroyWhileFlushing) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING)); |
- renderer_->Flush(NewExpectedClosure()); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, Play) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, FlushWithNothingBuffered) { |
- Initialize(); |
- StartPlayingFrom(0); |
- |
- // We shouldn't expect a buffering state change since we never reached |
- // BUFFERING_HAVE_ENOUGH. |
- Flush(); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, DecodeError_Playing) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- |
- QueueFrames("error"); |
- SatisfyPendingRead(); |
- WaitForError(PIPELINE_ERROR_DECODE); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, DecodeError_DuringStartPlayingFrom) { |
- Initialize(); |
- QueueFrames("error"); |
- StartPlayingFrom(0); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, StartPlayingFrom_Exact) { |
- Initialize(); |
- QueueFrames("50 60 70 80 90"); |
- |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(60))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(60); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, StartPlayingFrom_RightBefore) { |
- Initialize(); |
- QueueFrames("50 60 70 80 90"); |
- |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(50))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(59); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, StartPlayingFrom_RightAfter) { |
- Initialize(); |
- QueueFrames("50 60 70 80 90"); |
- |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(60))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(61); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, StartPlayingFrom_LowDelay) { |
- // In low-delay mode only one frame is required to finish preroll. |
- InitializeWithLowDelay(true); |
- QueueFrames("0"); |
- |
- // Expect some amount of have enough/nothing due to only requiring one frame. |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)) |
- .Times(AnyNumber()); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING)) |
- .Times(AnyNumber()); |
- StartPlayingFrom(0); |
- |
- QueueFrames("10"); |
- SatisfyPendingRead(); |
- |
- WaitableMessageLoopEvent event; |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(10))) |
- .WillOnce(RunClosure(event.GetClosure())); |
- AdvanceTimeInMs(10); |
- event.RunAndWait(); |
- |
- Destroy(); |
-} |
- |
-// Verify that a late decoder response doesn't break invariants in the renderer. |
-TEST_F(VideoRendererImplTest, DestroyDuringOutstandingRead) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- |
- // Check that there is an outstanding Read() request. |
- EXPECT_TRUE(IsReadPending()); |
- |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, VideoDecoder_InitFailure) { |
- InitializeRenderer(DECODER_ERROR_NOT_SUPPORTED, false); |
- Destroy(); |
-} |
- |
-TEST_F(VideoRendererImplTest, Underflow) { |
- Initialize(); |
- QueueFrames("0 10 20 30"); |
- EXPECT_CALL(mock_cb_, Display(HasTimestamp(0))); |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); |
- StartPlayingFrom(0); |
- |
- // Advance time slightly. Frames should be dropped and we should NOT signal |
- // having nothing. |
- AdvanceTimeInMs(100); |
- |
- // Advance time more. Now we should signal having nothing. |
- { |
- SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); |
- WaitableMessageLoopEvent event; |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING)) |
- .WillOnce(RunClosure(event.GetClosure())); |
- AdvanceTimeInMs(3000); // Must match kTimeToDeclareHaveNothing. |
- event.RunAndWait(); |
- } |
- |
- // Receiving end of stream should signal having enough. |
- { |
- SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); |
- WaitableMessageLoopEvent event; |
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)) |
- .WillOnce(RunClosure(event.GetClosure())); |
- SatisfyPendingReadWithEndOfStream(); |
- event.RunAndWait(); |
- } |
- |
- WaitForEnded(); |
- Destroy(); |
-} |
- |
-} // namespace media |