| // Copyright (c) 2010 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/callback.h" |
| #include "base/stl_util-inl.h" |
| #include "media/base/callback.h" |
| #include "media/base/data_buffer.h" |
| #include "media/base/limits.h" |
| #include "media/base/mock_filter_host.h" |
| #include "media/base/mock_filters.h" |
| #include "media/base/video_frame.h" |
| #include "media/filters/video_renderer_base.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::InSequence; |
| using ::testing::Invoke; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::StrictMock; |
| |
| namespace media { |
| ACTION(OnStop) { |
| AutoCallbackRunner auto_runner(arg0); |
| } |
| |
| // Mocked subclass of VideoRendererBase for testing purposes. |
| class MockVideoRendererBase : public VideoRendererBase { |
| public: |
| MockVideoRendererBase() {} |
| virtual ~MockVideoRendererBase() {} |
| |
| // VideoRendererBase implementation. |
| MOCK_METHOD1(OnInitialize, bool(VideoDecoder* decoder)); |
| MOCK_METHOD1(OnStop, void(FilterCallback* callback)); |
| MOCK_METHOD0(OnFrameAvailable, void()); |
| |
| // Used for verifying check points during tests. |
| MOCK_METHOD1(CheckPoint, void(int id)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockVideoRendererBase); |
| }; |
| |
| class VideoRendererBaseTest : public ::testing::Test { |
| public: |
| VideoRendererBaseTest() |
| : renderer_(new MockVideoRendererBase()), |
| decoder_(new MockVideoDecoder()) { |
| renderer_->set_host(&host_); |
| |
| // Queue all reads from the decoder. |
| EXPECT_CALL(*decoder_, ProduceVideoFrame(_)) |
| .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::EnqueueCallback)); |
| |
| // Sets the essential media format keys for this decoder. |
| decoder_media_format_.SetAsString(MediaFormat::kMimeType, |
| mime_type::kUncompressedVideo); |
| decoder_media_format_.SetAsInteger(MediaFormat::kSurfaceType, |
| VideoFrame::TYPE_SYSTEM_MEMORY); |
| decoder_media_format_.SetAsInteger(MediaFormat::kSurfaceFormat, |
| VideoFrame::YV12); |
| decoder_media_format_.SetAsInteger(MediaFormat::kWidth, kWidth); |
| decoder_media_format_.SetAsInteger(MediaFormat::kHeight, kHeight); |
| EXPECT_CALL(*decoder_, media_format()) |
| .WillRepeatedly(ReturnRef(decoder_media_format_)); |
| } |
| |
| virtual ~VideoRendererBaseTest() { |
| read_queue_.clear(); |
| |
| // Expect a call into the subclass. |
| EXPECT_CALL(*renderer_, OnStop(NotNull())) |
| .WillOnce(DoAll(OnStop(), Return())) |
| .RetiresOnSaturation(); |
| |
| EXPECT_CALL(callback_, OnFilterCallback()); |
| EXPECT_CALL(callback_, OnCallbackDestroyed()); |
| renderer_->Stop(callback_.NewCallback()); |
| } |
| |
| protected: |
| static const size_t kWidth; |
| static const size_t kHeight; |
| |
| // Fixture members. |
| scoped_refptr<MockVideoRendererBase> renderer_; |
| scoped_refptr<MockVideoDecoder> decoder_; |
| StrictMock<MockFilterHost> host_; |
| StrictMock<MockFilterCallback> callback_; |
| MediaFormat decoder_media_format_; |
| |
| // Receives all the buffers that renderer had provided to |decoder_|. |
| std::deque<scoped_refptr<VideoFrame> > read_queue_; |
| |
| private: |
| void EnqueueCallback(scoped_refptr<VideoFrame> frame) { |
| read_queue_.push_back(frame); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(VideoRendererBaseTest); |
| }; |
| |
| const size_t VideoRendererBaseTest::kWidth = 16u; |
| const size_t VideoRendererBaseTest::kHeight = 16u; |
| |
| // Test initialization where the decoder's media format is malformed. |
| TEST_F(VideoRendererBaseTest, Initialize_BadMediaFormat) { |
| InSequence s; |
| |
| // Don't set a media format. |
| MediaFormat media_format; |
| scoped_refptr<MockVideoDecoder> bad_decoder = new MockVideoDecoder(); |
| EXPECT_CALL(*bad_decoder, media_format()) |
| .WillRepeatedly(ReturnRef(media_format)); |
| |
| // We expect to receive an error. |
| EXPECT_CALL(host_, SetError(PIPELINE_ERROR_INITIALIZATION_FAILED)); |
| |
| // We expect our callback to be executed. |
| EXPECT_CALL(callback_, OnFilterCallback()); |
| EXPECT_CALL(callback_, OnCallbackDestroyed()); |
| |
| // Initialize, we expect to have no reads. |
| renderer_->Initialize(bad_decoder, callback_.NewCallback()); |
| EXPECT_EQ(0u, read_queue_.size()); |
| } |
| |
| // Test initialization where the subclass failed for some reason. |
| TEST_F(VideoRendererBaseTest, Initialize_Failed) { |
| InSequence s; |
| |
| // We expect the video size to be set. |
| EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight)); |
| |
| // Our subclass will fail when asked to initialize. |
| EXPECT_CALL(*renderer_, OnInitialize(_)) |
| .WillOnce(Return(false)); |
| |
| // We expect to receive an error. |
| EXPECT_CALL(host_, SetError(PIPELINE_ERROR_INITIALIZATION_FAILED)); |
| |
| // We expect our callback to be executed. |
| EXPECT_CALL(callback_, OnFilterCallback()); |
| EXPECT_CALL(callback_, OnCallbackDestroyed()); |
| |
| // Initialize, we expect to have no reads. |
| renderer_->Initialize(decoder_, callback_.NewCallback()); |
| EXPECT_EQ(0u, read_queue_.size()); |
| } |
| |
| // Test successful initialization and preroll. |
| TEST_F(VideoRendererBaseTest, Initialize_Successful) { |
| // Who knows how many times ThreadMain() will execute! |
| // |
| // TODO(scherkus): really, really, really need to inject a thread into |
| // VideoRendererBase... it makes mocking much harder. |
| EXPECT_CALL(host_, GetTime()).WillRepeatedly(Return(base::TimeDelta())); |
| |
| // Expects the video renderer to get duration from the host. |
| EXPECT_CALL(host_, GetDuration()) |
| .WillRepeatedly(Return(base::TimeDelta())); |
| |
| InSequence s; |
| |
| // We expect the video size to be set. |
| EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight)); |
| |
| // Then our subclass will be asked to initialize. |
| EXPECT_CALL(*renderer_, OnInitialize(_)) |
| .WillOnce(Return(true)); |
| |
| // After finishing initialization, we expect our callback to be executed. |
| EXPECT_CALL(callback_, OnFilterCallback()); |
| EXPECT_CALL(callback_, OnCallbackDestroyed()); |
| |
| // Initialize, we shouldn't have any reads. |
| renderer_->Initialize(decoder_, callback_.NewCallback()); |
| EXPECT_EQ(0u, read_queue_.size()); |
| |
| // Verify the following expectations haven't run until we complete the reads. |
| EXPECT_CALL(*renderer_, CheckPoint(0)); |
| |
| // We'll expect to get notified once due preroll completing. |
| EXPECT_CALL(*renderer_, OnFrameAvailable()); |
| |
| MockFilterCallback seek_callback; |
| EXPECT_CALL(seek_callback, OnFilterCallback()); |
| EXPECT_CALL(seek_callback, OnCallbackDestroyed()); |
| |
| // Now seek to trigger prerolling. |
| renderer_->Seek(base::TimeDelta(), seek_callback.NewCallback()); |
| |
| // Verify our seek callback hasn't been executed yet. |
| renderer_->CheckPoint(0); |
| |
| // Now satisfy the read requests. Our callback should be executed after |
| // exiting this loop. |
| for (unsigned int i = 0; i < Limits::kMaxVideoFrames; i++) { |
| const base::TimeDelta kZero; |
| scoped_refptr<VideoFrame> frame; |
| VideoFrame::CreateFrame(VideoFrame::RGB32, kWidth, kHeight, kZero, |
| kZero, &frame); |
| decoder_->VideoFrameReady(frame); |
| } |
| |
| MockFilterCallback play_callback; |
| EXPECT_CALL(play_callback, OnFilterCallback()); |
| EXPECT_CALL(play_callback, OnCallbackDestroyed()); |
| |
| renderer_->Play(play_callback.NewCallback()); |
| |
| StrictMock<MockFilterCallback> pause_callback; |
| EXPECT_CALL(pause_callback, OnFilterCallback()); |
| EXPECT_CALL(pause_callback, OnCallbackDestroyed()); |
| renderer_->Pause(pause_callback.NewCallback()); |
| |
| EXPECT_CALL(*decoder_, ProvidesBuffer()) |
| .WillRepeatedly(Return(true)); |
| |
| StrictMock<MockFilterCallback> flush_callback; |
| EXPECT_CALL(flush_callback, OnFilterCallback()); |
| EXPECT_CALL(flush_callback, OnCallbackDestroyed()); |
| renderer_->Flush(flush_callback.NewCallback()); |
| } |
| |
| } // namespace media |