| Index: media/filters/video_renderer_base_unittest.cc
|
| diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc
|
| index c2b3423347046562ffffe95f82ee05f53868bdcd..f7a64907dd39a12f58e3565fd48d311923173228 100644
|
| --- a/media/filters/video_renderer_base_unittest.cc
|
| +++ b/media/filters/video_renderer_base_unittest.cc
|
| @@ -4,7 +4,13 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| +#include "base/format_macros.h"
|
| #include "base/stl_util.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/synchronization/condition_variable.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/test/test_timeouts.h"
|
| #include "media/base/data_buffer.h"
|
| #include "media/base/limits.h"
|
| #include "media/base/mock_callback.h"
|
| @@ -24,6 +30,7 @@ using ::testing::ReturnRef;
|
| using ::testing::StrictMock;
|
|
|
| namespace media {
|
| +
|
| ACTION(OnStop) {
|
| arg0.Run();
|
| }
|
| @@ -51,13 +58,12 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| VideoRendererBaseTest()
|
| : renderer_(new MockVideoRendererBase()),
|
| decoder_(new MockVideoDecoder()),
|
| - seeking_(false) {
|
| + cv_(&lock_),
|
| + event_(false, false),
|
| + seeking_(false),
|
| + pending_reads_(0) {
|
| renderer_->set_host(&host_);
|
|
|
| - // Queue all reads from the decoder.
|
| - EXPECT_CALL(*decoder_, ProduceVideoFrame(_))
|
| - .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::EnqueueCallback));
|
| -
|
| EXPECT_CALL(*decoder_, natural_size()).WillRepeatedly(Return(kNaturalSize));
|
|
|
| EXPECT_CALL(stats_callback_object_, OnStatistics(_))
|
| @@ -67,13 +73,8 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| virtual ~VideoRendererBaseTest() {
|
| read_queue_.clear();
|
|
|
| - if (renderer_.get()) {
|
| - // Expect a call into the subclass.
|
| - EXPECT_CALL(*renderer_, OnStop(_))
|
| - .WillOnce(DoAll(OnStop(), Return()))
|
| - .RetiresOnSaturation();
|
| -
|
| - renderer_->Stop(NewExpectedClosure());
|
| + if (renderer_) {
|
| + Stop();
|
| }
|
| }
|
|
|
| @@ -88,6 +89,10 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| EXPECT_CALL(host_, GetDuration())
|
| .WillRepeatedly(Return(base::TimeDelta()));
|
|
|
| + // Monitor reads from the decoder.
|
| + EXPECT_CALL(*decoder_, ProduceVideoFrame(_))
|
| + .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::FrameRequested));
|
| +
|
| InSequence s;
|
|
|
| // We expect the video size to be set.
|
| @@ -100,7 +105,6 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| // Initialize, we shouldn't have any reads.
|
| renderer_->Initialize(decoder_,
|
| NewExpectedClosure(), NewStatisticsCallback());
|
| - EXPECT_EQ(0u, read_queue_.size());
|
|
|
| // Now seek to trigger prerolling.
|
| Seek(0);
|
| @@ -117,41 +121,59 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| expected_status));
|
| }
|
|
|
| - void FinishSeeking() {
|
| - EXPECT_CALL(*renderer_, OnFrameAvailable());
|
| - EXPECT_TRUE(seeking_);
|
| -
|
| - // Satisfy the read requests. The callback must be executed in order
|
| - // to exit the loop since VideoRendererBase can read a few extra frames
|
| - // after |timestamp| in order to preroll.
|
| - for (int64 i = 0; seeking_; ++i) {
|
| - CreateFrame(i * kDuration, kDuration);
|
| - }
|
| + void Play() {
|
| + SCOPED_TRACE("Play()");
|
| + renderer_->Play(NewWaitableClosure());
|
| + WaitForClosure();
|
| }
|
|
|
| void Seek(int64 timestamp) {
|
| + SCOPED_TRACE(base::StringPrintf("Seek(%" PRId64 ")", timestamp));
|
| StartSeeking(timestamp, PIPELINE_OK);
|
| +
|
| + // TODO(scherkus): switch to FinishSeeking_DISABLED() (see comments below).
|
| FinishSeeking();
|
| }
|
|
|
| + void Pause() {
|
| + SCOPED_TRACE("Pause()");
|
| + renderer_->Pause(NewWaitableClosure());
|
| + WaitForClosure();
|
| + }
|
| +
|
| void Flush() {
|
| - renderer_->Pause(NewExpectedClosure());
|
| + SCOPED_TRACE("Flush()");
|
| + renderer_->Flush(NewWaitableClosure());
|
| + WaitForClosure();
|
| + }
|
| +
|
| + void Stop() {
|
| + SCOPED_TRACE("Stop()");
|
|
|
| - renderer_->Flush(NewExpectedClosure());
|
| + // Expect a call into the subclass.
|
| + EXPECT_CALL(*renderer_, OnStop(_))
|
| + .WillOnce(DoAll(OnStop(), Return()))
|
| + .RetiresOnSaturation();
|
| +
|
| + renderer_->Stop(NewWaitableClosure());
|
| + WaitForClosure();
|
| }
|
|
|
| - void CreateError() {
|
| - decoder_->VideoFrameReadyForTest(NULL);
|
| + void Shutdown() {
|
| + Pause();
|
| + Flush();
|
| + Stop();
|
| }
|
|
|
| - void CreateFrame(int64 timestamp, int64 duration) {
|
| - const base::TimeDelta kZero;
|
| - scoped_refptr<VideoFrame> frame =
|
| - VideoFrame::CreateFrame(VideoFrame::RGB32, kNaturalSize.width(),
|
| - kNaturalSize.height(),
|
| - base::TimeDelta::FromMicroseconds(timestamp),
|
| - base::TimeDelta::FromMicroseconds(duration));
|
| - decoder_->VideoFrameReadyForTest(frame);
|
| + void ExpectCurrentFrame(bool present) {
|
| + scoped_refptr<VideoFrame> frame;
|
| + renderer_->GetCurrentFrame(&frame);
|
| + if (present) {
|
| + EXPECT_TRUE(frame);
|
| + } else {
|
| + EXPECT_FALSE(frame);
|
| + }
|
| + renderer_->PutCurrentFrame(frame);
|
| }
|
|
|
| void ExpectCurrentTimestamp(int64 timestamp) {
|
| @@ -161,6 +183,32 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| renderer_->PutCurrentFrame(frame);
|
| }
|
|
|
| + base::Closure NewWaitableClosure() {
|
| + return base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event_));
|
| + }
|
| +
|
| + void WaitForClosure() {
|
| + base::TimeDelta timeout =
|
| + base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms());
|
| + ASSERT_TRUE(event_.TimedWait(timeout));
|
| + event_.Reset();
|
| + }
|
| +
|
| + // Delivers a NULL frame to VideoRendererBase, signalling a decode error.
|
| + void DeliverErrorToRenderer() {
|
| + decoder_->VideoFrameReadyForTest(NULL);
|
| + }
|
| +
|
| + // Delivers a frame with given timestamp and duration to VideoRendererBase.
|
| + void DeliverFrameToRenderer(int64 timestamp, int64 duration) {
|
| + scoped_refptr<VideoFrame> frame =
|
| + VideoFrame::CreateFrame(VideoFrame::RGB32, kNaturalSize.width(),
|
| + kNaturalSize.height(),
|
| + base::TimeDelta::FromMicroseconds(timestamp),
|
| + base::TimeDelta::FromMicroseconds(duration));
|
| + decoder_->VideoFrameReadyForTest(frame);
|
| + }
|
| +
|
| protected:
|
| static const gfx::Size kNaturalSize;
|
| static const int64 kDuration;
|
| @@ -180,17 +228,82 @@ class VideoRendererBaseTest : public ::testing::Test {
|
| std::deque<scoped_refptr<VideoFrame> > read_queue_;
|
|
|
| private:
|
| - void EnqueueCallback(scoped_refptr<VideoFrame> frame) {
|
| - read_queue_.push_back(frame);
|
| + // Called by VideoRendererBase when it wants a frame.
|
| + void FrameRequested(scoped_refptr<VideoFrame> video_frame) {
|
| + base::AutoLock l(lock_);
|
| + ++pending_reads_;
|
| + cv_.Signal();
|
| }
|
|
|
| void OnSeekComplete(PipelineStatus expected_status, PipelineStatus status) {
|
| + base::AutoLock l(lock_);
|
| EXPECT_EQ(status, expected_status);
|
| EXPECT_TRUE(seeking_);
|
| seeking_ = false;
|
| + cv_.Signal();
|
| + }
|
| +
|
| + // TODO(scherkus): remove this as soon as we move away from our current buffer
|
| + // recycling implementation, which assumes the decoder will continously
|
| + // deliver frames during prerolling -- even if VideoRendererBase didn't ask
|
| + // for them!
|
| + void FinishSeeking() {
|
| + EXPECT_CALL(*renderer_, OnFrameAvailable());
|
| + EXPECT_TRUE(seeking_);
|
| +
|
| + // Satisfy the read requests. The callback must be executed in order
|
| + // to exit the loop since VideoRendererBase can read a few extra frames
|
| + // after |timestamp| in order to preroll.
|
| + int64 i = 0;
|
| + base::AutoLock l(lock_);
|
| + while (seeking_) {
|
| + // Unlock to deliver the frame to avoid re-entrancy issues.
|
| + base::AutoUnlock ul(lock_);
|
| + DeliverFrameToRenderer(i * kDuration, kDuration);
|
| + ++i;
|
| + }
|
| }
|
|
|
| + // TODO(scherkus): this is the proper read/prerolling behaviour we want to
|
| + // have where VideoRendererBase requests a frame and the decoder responds.
|
| + void FinishSeeking_DISABLED() {
|
| + EXPECT_CALL(*renderer_, OnFrameAvailable());
|
| + EXPECT_TRUE(seeking_);
|
| +
|
| + base::TimeDelta timeout =
|
| + base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms());
|
| +
|
| + // Satisfy the read requests. The callback must be executed in order
|
| + // to exit the loop since VideoRendererBase can read a few extra frames
|
| + // after |timestamp| in order to preroll.
|
| + int64 i = 0;
|
| + base::AutoLock l(lock_);
|
| + while (seeking_) {
|
| + if (pending_reads_ > 0) {
|
| + --pending_reads_;
|
| +
|
| + // Unlock to deliver the frame to avoid re-entrancy issues.
|
| + base::AutoUnlock ul(lock_);
|
| + DeliverFrameToRenderer(i * kDuration, kDuration);
|
| + ++i;
|
| + } else if (pending_reads_ == 0) {
|
| + // We want to wait iff we're still seeking but have no pending reads.
|
| + cv_.TimedWait(timeout);
|
| + CHECK(!seeking_ || pending_reads_ > 0)
|
| + << "Timed out waiting for seek or read to occur.";
|
| + }
|
| + }
|
| +
|
| + EXPECT_EQ(0, pending_reads_);
|
| + }
|
| +
|
| + base::Lock lock_;
|
| + base::ConditionVariable cv_;
|
| + base::WaitableEvent event_;
|
| +
|
| + // Used in conjunction with |lock_| and |cv_| for satisfying reads.
|
| bool seeking_;
|
| + int pending_reads_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(VideoRendererBaseTest);
|
| };
|
| @@ -222,110 +335,135 @@ TEST_F(VideoRendererBaseTest, Initialize_Failed) {
|
| TEST_F(VideoRendererBaseTest, Initialize_Successful) {
|
| Initialize();
|
| ExpectCurrentTimestamp(0);
|
| - Flush();
|
| + Shutdown();
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Play) {
|
| Initialize();
|
| - renderer_->Play(NewExpectedClosure());
|
| - Flush();
|
| + Play();
|
| + Shutdown();
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Error_Playing) {
|
| Initialize();
|
| - renderer_->Play(NewExpectedClosure());
|
| + Play();
|
|
|
| EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
|
| - CreateError();
|
| - Flush();
|
| + DeliverErrorToRenderer();
|
| + Shutdown();
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Error_Seeking) {
|
| Initialize();
|
| + Pause();
|
| Flush();
|
| +
|
| StartSeeking(0, PIPELINE_ERROR_DECODE);
|
| - CreateError();
|
| - Flush();
|
| + DeliverErrorToRenderer();
|
| + Shutdown();
|
| +}
|
| +
|
| +TEST_F(VideoRendererBaseTest, Error_DuringPaint) {
|
| + Initialize();
|
| + Play();
|
| +
|
| + // Grab the frame.
|
| + scoped_refptr<VideoFrame> frame;
|
| + renderer_->GetCurrentFrame(&frame);
|
| + EXPECT_TRUE(frame);
|
| +
|
| + // Deliver an error.
|
| + EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
|
| + DeliverErrorToRenderer();
|
| +
|
| + // Return the frame then try getting it again -- it should be NULL.
|
| + renderer_->PutCurrentFrame(frame);
|
| + ExpectCurrentFrame(false);
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Seek_Exact) {
|
| Initialize();
|
| + Pause();
|
| Flush();
|
| Seek(kDuration * 6);
|
| ExpectCurrentTimestamp(kDuration * 6);
|
| - Flush();
|
| + Shutdown();
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Seek_RightBefore) {
|
| Initialize();
|
| + Pause();
|
| Flush();
|
| Seek(kDuration * 6 - 1);
|
| ExpectCurrentTimestamp(kDuration * 5);
|
| - Flush();
|
| + Shutdown();
|
| }
|
|
|
| TEST_F(VideoRendererBaseTest, Seek_RightAfter) {
|
| Initialize();
|
| + Pause();
|
| Flush();
|
| Seek(kDuration * 6 + 1);
|
| ExpectCurrentTimestamp(kDuration * 6);
|
| - Flush();
|
| + Shutdown();
|
| }
|
|
|
| -// Verify behavior for GetCurrentFrame() calls that happen after a
|
| -// decoder error.
|
| -TEST_F(VideoRendererBaseTest, GetCurrentFrame_AfterError) {
|
| +TEST_F(VideoRendererBaseTest, GetCurrentFrame_Initialized) {
|
| Initialize();
|
| - renderer_->Play(NewExpectedClosure());
|
| -
|
| - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
|
| - CreateError();
|
| -
|
| - scoped_refptr<VideoFrame> frame;
|
| - renderer_->GetCurrentFrame(&frame);
|
| - EXPECT_TRUE(!frame.get());
|
| - renderer_->PutCurrentFrame(frame);
|
| + ExpectCurrentFrame(true); // Due to prerolling.
|
| + Shutdown();
|
| }
|
|
|
| -// Verify behavior for GetCurrentFrame() when an error occurs in the middle
|
| -// of a paint operation.
|
| -TEST_F(VideoRendererBaseTest, Error_DuringPaint) {
|
| +TEST_F(VideoRendererBaseTest, GetCurrentFrame_Playing) {
|
| Initialize();
|
| - renderer_->Play(NewExpectedClosure());
|
| -
|
| - scoped_refptr<VideoFrame> frame;
|
| - renderer_->GetCurrentFrame(&frame);
|
| -
|
| - EXPECT_TRUE(frame.get());
|
| -
|
| - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE));
|
| - CreateError();
|
| -
|
| - renderer_->PutCurrentFrame(frame);
|
| -
|
| - renderer_->GetCurrentFrame(&frame);
|
| - EXPECT_TRUE(!frame.get());
|
| - renderer_->PutCurrentFrame(frame);
|
| + Play();
|
| + ExpectCurrentFrame(true);
|
| + Shutdown();
|
| }
|
|
|
| +TEST_F(VideoRendererBaseTest, GetCurrentFrame_Paused) {
|
| + Initialize();
|
| + Play();
|
| + Pause();
|
| + ExpectCurrentFrame(true);
|
| + Shutdown();
|
| +}
|
|
|
| -// Verify behavior for GetCurrentFrame() calls that happen after
|
| -// the renderer has been stopped.
|
| -TEST_F(VideoRendererBaseTest, GetCurrentFrame_AfterStop) {
|
| +TEST_F(VideoRendererBaseTest, GetCurrentFrame_Flushed) {
|
| Initialize();
|
| + Play();
|
| + Pause();
|
| + Flush();
|
| + ExpectCurrentFrame(false);
|
| + Shutdown();
|
| +}
|
|
|
| - EXPECT_CALL(*renderer_, OnStop(_))
|
| - .WillOnce(DoAll(OnStop(), Return()))
|
| - .RetiresOnSaturation();
|
| +TEST_F(VideoRendererBaseTest, GetCurrentFrame_Shutdown) {
|
| + Initialize();
|
| + Shutdown();
|
| + ExpectCurrentFrame(false);
|
| +}
|
|
|
| - renderer_->Stop(NewExpectedClosure());
|
| +// Verify that shutdown can only proceed after we return the current frame.
|
| +TEST_F(VideoRendererBaseTest, Shutdown_DuringPaint) {
|
| + Initialize();
|
| + Play();
|
|
|
| + // Grab the frame.
|
| scoped_refptr<VideoFrame> frame;
|
| renderer_->GetCurrentFrame(&frame);
|
| - EXPECT_TRUE(!frame.get());
|
| + EXPECT_TRUE(frame);
|
| +
|
| + Pause();
|
| +
|
| + // Start flushing -- it won't complete until we return the frame.
|
| + renderer_->Flush(NewWaitableClosure());
|
| +
|
| + // Return the frame and wait.
|
| renderer_->PutCurrentFrame(frame);
|
| + WaitForClosure();
|
|
|
| - renderer_ = NULL;
|
| + Stop();
|
| }
|
|
|
| } // namespace media
|
|
|