Chromium Code Reviews| 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 2e6cc1219fe39d2e69014d36060bfadf4ef796a6..2db0fb61461ec0bed97dcebc48bec07981834f2e 100644 |
| --- a/media/filters/video_renderer_base_unittest.cc |
| +++ b/media/filters/video_renderer_base_unittest.cc |
| @@ -2,16 +2,16 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -#include <algorithm> |
| - |
| #include "base/bind.h" |
| #include "base/callback.h" |
| +#include "base/callback_helpers.h" |
| +#include "base/message_loop.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 "base/timer.h" |
| +#include "media/base/bind_to_loop.h" |
| #include "media/base/data_buffer.h" |
| #include "media/base/gmock_callback_support.h" |
| #include "media/base/limits.h" |
| @@ -34,24 +34,79 @@ namespace media { |
| static const int kFrameDuration = 10; |
| static const int kVideoDuration = kFrameDuration * 100; |
| -static const int kEndOfStream = -1; |
| static const gfx::Size kNaturalSize(16u, 16u); |
| +class WaitableMessageLoopEvent { |
|
scherkus (not reviewing)
2012/12/05 00:03:46
if we like this approach I'm going to move this in
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
SGTM
|
| + public: |
| + WaitableMessageLoopEvent() |
| + : message_loop_(MessageLoop::current()), |
| + signaled_(false), |
| + status_(PIPELINE_OK) { |
| + } |
| + |
| + ~WaitableMessageLoopEvent() {} |
| + |
| + void Reset() { signaled_ = false; } |
|
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
nit: Should status_ be reset as well?
scherkus (not reviewing)
2012/12/05 21:52:03
Done.
|
| + bool IsSignaled() { return signaled_; } |
|
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
remove since it isn't being used.
scherkus (not reviewing)
2012/12/05 21:52:03
Done.
|
| + |
| + // Returns a thread-safe closure that will signal |this| when executed. |
| + base::Closure GetClosure() { |
| + return BindToLoop(message_loop_->message_loop_proxy(), base::Bind( |
| + &WaitableMessageLoopEvent::OnCallback, base::Unretained(this), |
| + PIPELINE_OK)); |
| + } |
| + |
| + PipelineStatusCB GetPipelineStatusCB() { |
| + return BindToLoop(message_loop_->message_loop_proxy(), base::Bind( |
| + &WaitableMessageLoopEvent::OnCallback, base::Unretained(this))); |
| + } |
| + |
| + // Convenience function for running the message loop until |this| has been |
| + // signaled. |
| + void RunAndWait() { |
| + RunAndWaitForStatus(PIPELINE_OK); |
| + } |
| + |
| + void RunAndWaitForStatus(PipelineStatus expected) { |
| + base::Timer timer(false, false); |
| + timer.Start(FROM_HERE, TestTimeouts::action_timeout(), base::Bind( |
| + &WaitableMessageLoopEvent::OnTimeout, base::Unretained(this))); |
| + |
| + DCHECK(!signaled_); |
| + message_loop_->Run(); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
this is now the only place where a message loop is
|
| + DCHECK(signaled_); |
| + EXPECT_EQ(expected, status_); |
| + } |
| + |
| + private: |
| + void OnCallback(PipelineStatus status) { |
| + DCHECK_EQ(message_loop_, MessageLoop::current()); |
| + signaled_ = true; |
| + status_ = status; |
| + message_loop_->QuitWhenIdle(); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
here + OnTimeout() are now the only places where a
|
| + } |
| + |
| + void OnTimeout() { |
| + DCHECK_EQ(message_loop_, MessageLoop::current()); |
| + ADD_FAILURE() << "Timed out waiting for message loop to quit"; |
| + message_loop_->QuitWhenIdle(); |
| + } |
| + |
| + MessageLoop* message_loop_; |
| + bool signaled_; |
| + PipelineStatus status_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WaitableMessageLoopEvent); |
| +}; |
| + |
| class VideoRendererBaseTest : public ::testing::Test { |
| public: |
| VideoRendererBaseTest() |
| : decoder_(new MockVideoDecoder()), |
| demuxer_stream_(new MockDemuxerStream()), |
| - cv_(&lock_), |
| - event_(false, false), |
| - timeout_(TestTimeouts::action_timeout()), |
| - prerolling_(false), |
| - next_frame_timestamp_(0), |
| - paint_cv_(&lock_), |
| - paint_was_called_(false), |
| - should_queue_read_cb_(false) { |
| + next_frame_timestamp_(0) { |
| renderer_ = new VideoRendererBase( |
| - base::Bind(&VideoRendererBaseTest::Paint, base::Unretained(this)), |
| + base::Bind(&VideoRendererBaseTest::OnPaint, base::Unretained(this)), |
|
scherkus (not reviewing)
2012/12/05 00:03:46
we no longer use paints to signal anything and ins
|
| base::Bind(&VideoRendererBaseTest::OnSetOpaque, base::Unretained(this)), |
| true); |
| @@ -65,26 +120,21 @@ class VideoRendererBaseTest : public ::testing::Test { |
| .Times(AnyNumber()); |
| EXPECT_CALL(*this, OnTimeUpdate(_)) |
| .Times(AnyNumber()); |
| + EXPECT_CALL(*this, OnPaint()) |
| + .Times(AnyNumber()); |
| EXPECT_CALL(*this, OnSetOpaque(_)) |
| .Times(AnyNumber()); |
| } |
| - virtual ~VideoRendererBaseTest() { |
| - read_queue_.clear(); |
| - |
| - if (renderer_) { |
| - Stop(); |
| - } |
| - } |
| + virtual ~VideoRendererBaseTest() {} |
| // Callbacks passed into VideoRendererBase(). |
| + MOCK_CONST_METHOD0(OnPaint, void()); |
| MOCK_CONST_METHOD1(OnSetOpaque, void(bool)); |
| // Callbacks passed into Initialize(). |
| MOCK_METHOD1(OnTimeUpdate, void(base::TimeDelta)); |
| MOCK_METHOD1(OnNaturalSizeChanged, void(const gfx::Size&)); |
| - MOCK_METHOD0(OnEnded, void()); |
| - MOCK_METHOD1(OnError, void(PipelineStatus)); |
| void Initialize() { |
| Initialize(kVideoDuration); |
| @@ -93,9 +143,6 @@ class VideoRendererBaseTest : public ::testing::Test { |
| void Initialize(int duration) { |
| duration_ = duration; |
| - // TODO(scherkus): really, really, really need to inject a thread into |
| - // VideoRendererBase... it makes mocking much harder. |
| - |
| // Monitor reads from the decoder. |
| EXPECT_CALL(*decoder_, Read(_)) |
| .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::FrameRequested)); |
| @@ -118,87 +165,70 @@ class VideoRendererBaseTest : public ::testing::Test { |
| EXPECT_CALL(*this, OnNaturalSizeChanged(kNaturalSize)); |
| // Start prerolling. |
| - Preroll(0); |
| + QueuePrerollFrames(0); |
| + Preroll(0, PIPELINE_OK); |
| } |
| - void InitializeRenderer(PipelineStatus expected_status) { |
| + void InitializeRenderer(PipelineStatus expected) { |
| + SCOPED_TRACE(base::StringPrintf("Initialize(%d)", expected)); |
|
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
nit: s/Initialize/InitializeRenderer/ ?
scherkus (not reviewing)
2012/12/05 21:52:03
Done.
|
| VideoRendererBase::VideoDecoderList decoders; |
| decoders.push_back(decoder_); |
| + |
| + WaitableMessageLoopEvent event; |
| renderer_->Initialize( |
| demuxer_stream_, |
| decoders, |
| - NewExpectedStatusCB(expected_status), |
| + event.GetPipelineStatusCB(), |
| base::Bind(&MockStatisticsCB::OnStatistics, |
| base::Unretained(&statistics_cb_object_)), |
| base::Bind(&VideoRendererBaseTest::OnTimeUpdate, |
| base::Unretained(this)), |
| base::Bind(&VideoRendererBaseTest::OnNaturalSizeChanged, |
| base::Unretained(this)), |
| - base::Bind(&VideoRendererBaseTest::OnEnded, base::Unretained(this)), |
| - base::Bind(&VideoRendererBaseTest::OnError, base::Unretained(this)), |
| + ended_event_.GetClosure(), |
| + error_event_.GetPipelineStatusCB(), |
| base::Bind(&VideoRendererBaseTest::GetTime, base::Unretained(this)), |
| base::Bind(&VideoRendererBaseTest::GetDuration, |
| base::Unretained(this))); |
| - } |
| - |
| - // Instead of immediately satisfying a decoder Read request, queue it up. |
| - void QueueReadCB() { |
| - should_queue_read_cb_ = true; |
| - } |
| - |
| - void SatisfyQueuedReadCB() { |
| - base::AutoLock l(lock_); |
| - CHECK(should_queue_read_cb_ && !queued_read_cb_.is_null()); |
| - should_queue_read_cb_ = false; |
| - VideoDecoder::ReadCB read_cb(queued_read_cb_); |
| - queued_read_cb_.Reset(); |
| - base::AutoUnlock u(lock_); |
| - read_cb.Run(VideoDecoder::kOk, VideoFrame::CreateEmptyFrame()); |
| - } |
| - |
| - void StartPrerolling(int timestamp, PipelineStatus expected_status) { |
| - EXPECT_FALSE(prerolling_); |
| - |
| - next_frame_timestamp_ = 0; |
| - prerolling_ = true; |
| - renderer_->Preroll(base::TimeDelta::FromMilliseconds(timestamp), |
| - base::Bind(&VideoRendererBaseTest::OnPrerollComplete, |
| - base::Unretained(this), expected_status)); |
| + event.RunAndWaitForStatus(expected); |
| } |
| void Play() { |
| SCOPED_TRACE("Play()"); |
| - renderer_->Play(NewWaitableClosure()); |
| - WaitForClosure(); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Play(event.GetClosure()); |
| + event.RunAndWait(); |
| } |
| // Preroll to the given timestamp. |
| - // |
| - // Use |kEndOfStream| to preroll end of stream frames. |
| - void Preroll(int timestamp) { |
| - SCOPED_TRACE(base::StringPrintf("Preroll(%d)", timestamp)); |
| - bool end_of_stream = (timestamp == kEndOfStream); |
| - int preroll_timestamp = end_of_stream ? 0 : timestamp; |
| - StartPrerolling(preroll_timestamp, PIPELINE_OK); |
| - FinishPrerolling(end_of_stream); |
| + void Preroll(int timestamp, PipelineStatus expected) { |
| + SCOPED_TRACE(base::StringPrintf("Preroll(%d, %d)", timestamp, expected)); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Preroll( |
| + base::TimeDelta::FromMilliseconds(timestamp), |
| + event.GetPipelineStatusCB()); |
| + event.RunAndWaitForStatus(expected); |
| } |
| void Pause() { |
| SCOPED_TRACE("Pause()"); |
| - renderer_->Pause(NewWaitableClosure()); |
| - WaitForClosure(); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Pause(event.GetClosure()); |
| + event.RunAndWait(); |
| } |
| void Flush() { |
| SCOPED_TRACE("Flush()"); |
| - renderer_->Flush(NewWaitableClosure()); |
| - WaitForClosure(); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Flush(event.GetClosure()); |
| + event.RunAndWait(); |
| } |
| void Stop() { |
| SCOPED_TRACE("Stop()"); |
| - renderer_->Stop(NewWaitableClosure()); |
| - WaitForClosure(); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Stop(event.GetClosure()); |
| + event.RunAndWait(); |
| } |
| void Shutdown() { |
| @@ -207,122 +237,79 @@ class VideoRendererBaseTest : public ::testing::Test { |
| Stop(); |
| } |
| - void DeliverNextFrame(bool end_of_stream) { |
| - base::AutoLock l(lock_); |
| - DeliverNextFrame_Locked(end_of_stream); |
| - } |
| - |
| - // Delivers the next frame to the video renderer. If |end_of_stream| |
| - // is true then an "end or stream" frame will be returned. Otherwise |
| - // A frame with |next_frame_timestamp_| will be returned. |
| - void DeliverNextFrame_Locked(bool end_of_stream) { |
| - lock_.AssertAcquired(); |
| - |
| - VideoDecoder::ReadCB read_cb; |
| - std::swap(read_cb, read_cb_); |
| + // Queues a VideoFrame with |next_frame_timestamp_|. |
| + void QueueNextFrame() { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| DCHECK_LT(next_frame_timestamp_, duration_); |
| int timestamp = next_frame_timestamp_; |
| next_frame_timestamp_ += kFrameDuration; |
| - // Unlock to deliver the frame to avoid re-entrancy issues. |
| - base::AutoUnlock ul(lock_); |
| - if (end_of_stream) { |
| - read_cb.Run(VideoDecoder::kOk, VideoFrame::CreateEmptyFrame()); |
| - } else { |
| - read_cb.Run(VideoDecoder::kOk, CreateFrame(timestamp)); |
| - } |
| + decode_results_.push_back(std::make_pair( |
| + VideoDecoder::kOk, CreateFrame(timestamp))); |
| } |
| - void DecoderError() { |
| - // Lock+swap to avoid re-entrancy issues. |
| - VideoDecoder::ReadCB read_cb; |
| - { |
| - base::AutoLock l(lock_); |
| - std::swap(read_cb, read_cb_); |
| - } |
| - |
| - read_cb.Run(VideoDecoder::kDecodeError, NULL); |
| + void QueueEndOfStream() { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| + decode_results_.push_back(std::make_pair( |
| + VideoDecoder::kOk, VideoFrame::CreateEmptyFrame())); |
| } |
| - void AbortRead() { |
| - // Lock+swap to avoid re-entrancy issues. |
| - VideoDecoder::ReadCB read_cb; |
| - { |
| - base::AutoLock l(lock_); |
| - std::swap(read_cb, read_cb_); |
| - } |
| + void QueueDecodeError() { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| + scoped_refptr<VideoFrame> null_frame; |
| + decode_results_.push_back(std::make_pair( |
| + VideoDecoder::kDecodeError, null_frame)); |
| + } |
| - read_cb.Run(VideoDecoder::kOk, NULL); |
| + void QueueAbortedRead() { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| + scoped_refptr<VideoFrame> null_frame; |
| + decode_results_.push_back(std::make_pair( |
| + VideoDecoder::kOk, null_frame)); |
| } |
| - void ExpectCurrentFrame(bool present) { |
| - scoped_refptr<VideoFrame> frame; |
| - renderer_->GetCurrentFrame(&frame); |
| - if (present) { |
| - EXPECT_TRUE(frame); |
| - } else { |
| - EXPECT_FALSE(frame); |
| + void QueuePrerollFrames(int timestamp) { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| + next_frame_timestamp_ = 0; |
| + while (next_frame_timestamp_ < timestamp) { |
| + QueueNextFrame(); |
| + } |
| + |
| + // Queue the frame at |timestamp| plus additional ones for prerolling. |
| + for (int i = 0; i < limits::kMaxVideoFrames; ++i) { |
| + QueueNextFrame(); |
| } |
| - renderer_->PutCurrentFrame(frame); |
| } |
| - void ExpectCurrentTimestamp(int timestamp) { |
| + scoped_refptr<VideoFrame> GetCurrentFrame() { |
| scoped_refptr<VideoFrame> frame; |
| renderer_->GetCurrentFrame(&frame); |
| - EXPECT_EQ(timestamp, frame->GetTimestamp().InMilliseconds()); |
| renderer_->PutCurrentFrame(frame); |
| + return frame; |
| } |
| - base::Closure NewWaitableClosure() { |
| - return base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event_)); |
| + int GetCurrentTimestamp() { |
|
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
nit: s/GetCurrentTimestamp/GetCurrentTimestampInMs
scherkus (not reviewing)
2012/12/05 21:52:03
Done.
|
| + scoped_refptr<VideoFrame> frame = GetCurrentFrame(); |
| + if (!frame) |
| + return -1; |
| + return frame->GetTimestamp().InMilliseconds(); |
| } |
| - void WaitForClosure() { |
| - ASSERT_TRUE(event_.TimedWait(timeout_)); |
| - event_.Reset(); |
| + void WaitForError(PipelineStatus expected) { |
| + SCOPED_TRACE(base::StringPrintf("WaitForError(%d)", expected)); |
| + error_event_.RunAndWaitForStatus(expected); |
| } |
| - // Creates a frame with given timestamp. |
| - scoped_refptr<VideoFrame> CreateFrame(int timestamp) { |
| - scoped_refptr<VideoFrame> frame = |
| - VideoFrame::CreateFrame(VideoFrame::RGB32, kNaturalSize, |
| - gfx::Rect(kNaturalSize), kNaturalSize, |
| - base::TimeDelta::FromMilliseconds(timestamp)); |
| - return frame; |
| + void WaitForEnded() { |
| + SCOPED_TRACE("WaitForEnded()"); |
| + ended_event_.RunAndWait(); |
| } |
| - // Advances clock to |timestamp| and waits for the frame at |timestamp| to get |
| - // rendered using |read_cb_| as the signal that the frame has rendered. |
| - void RenderFrame(int timestamp) { |
| + void SetTime(int timestamp) { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| base::AutoLock l(lock_); |
| time_ = base::TimeDelta::FromMilliseconds(timestamp); |
| - paint_was_called_ = false; |
| - if (read_cb_.is_null()) { |
| - cv_.TimedWait(timeout_); |
| - CHECK(!read_cb_.is_null()) << "Timed out waiting for read to occur."; |
| - } |
| - WaitForPaint_Locked(); |
| - } |
| - |
| - // Advances clock to |timestamp| (which should be the timestamp of the last |
| - // frame plus duration) and waits for the ended signal before returning. |
| - void RenderLastFrame(int timestamp) { |
| - EXPECT_CALL(*this, OnEnded()) |
| - .WillOnce(Invoke(&event_, &base::WaitableEvent::Signal)); |
| - { |
| - base::AutoLock l(lock_); |
| - time_ = base::TimeDelta::FromMilliseconds(timestamp); |
| - } |
| - CHECK(event_.TimedWait(timeout_)) << "Timed out waiting for ended signal."; |
| - } |
| - |
| - base::WaitableEvent* event() { return &event_; } |
| - const base::TimeDelta& timeout() { return timeout_; } |
| - |
| - void VerifyNotPrerolling() { |
| - base::AutoLock l(lock_); |
| - ASSERT_FALSE(prerolling_); |
| } |
| protected: |
| @@ -332,11 +319,15 @@ class VideoRendererBaseTest : public ::testing::Test { |
| scoped_refptr<MockDemuxerStream> demuxer_stream_; |
| MockStatisticsCB statistics_cb_object_; |
| - // Receives all the buffers that renderer had provided to |decoder_|. |
| - std::deque<scoped_refptr<VideoFrame> > read_queue_; |
| - |
| private: |
| - // Called by VideoRendererBase for accessing the current time. |
| + scoped_refptr<VideoFrame> CreateFrame(int timestamp) { |
| + scoped_refptr<VideoFrame> frame = |
| + VideoFrame::CreateFrame(VideoFrame::RGB32, kNaturalSize, |
| + gfx::Rect(kNaturalSize), kNaturalSize, |
| + base::TimeDelta::FromMilliseconds(timestamp)); |
| + return frame; |
| + } |
| + |
| base::TimeDelta GetTime() { |
| base::AutoLock l(lock_); |
| return time_; |
| @@ -346,105 +337,68 @@ class VideoRendererBaseTest : public ::testing::Test { |
| return base::TimeDelta::FromMilliseconds(duration_); |
| } |
| - // Called by VideoRendererBase when it wants a frame. |
| - void FrameRequested(const VideoDecoder::ReadCB& callback) { |
| - base::AutoLock l(lock_); |
| - if (should_queue_read_cb_) { |
| - CHECK(queued_read_cb_.is_null()); |
| - queued_read_cb_ = callback; |
| - return; |
| - } |
| + void FrameRequested(const VideoDecoder::ReadCB& read_cb) { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| + |
| CHECK(read_cb_.is_null()); |
| - read_cb_ = callback; |
| - cv_.Signal(); |
| + read_cb_ = read_cb; |
| + |
| + if (decode_results_.empty()) |
| + return; |
| + |
| + SatisfyPendingRead(); |
| } |
| - void FlushRequested(const base::Closure& callback) { |
| - // Lock+swap to avoid re-entrancy issues. |
| - VideoDecoder::ReadCB read_cb; |
| - { |
| - base::AutoLock l(lock_); |
| - std::swap(read_cb, read_cb_); |
| - } |
| + void SatisfyPendingRead() { |
| + CHECK(!read_cb_.is_null()); |
| + CHECK(!decode_results_.empty()); |
| - // Abort pending read. |
| - if (!read_cb.is_null()) |
| - read_cb.Run(VideoDecoder::kOk, NULL); |
| + base::Closure closure = base::Bind( |
| + read_cb_, decode_results_.front().first, |
| + decode_results_.front().second); |
| - callback.Run(); |
| - } |
| + read_cb_.Reset(); |
| + decode_results_.pop_front(); |
| - void OnPrerollComplete(PipelineStatus expected_status, |
| - PipelineStatus status) { |
| - base::AutoLock l(lock_); |
| - EXPECT_EQ(status, expected_status); |
| - EXPECT_TRUE(prerolling_); |
| - prerolling_ = false; |
| - cv_.Signal(); |
| + message_loop_.PostTask(FROM_HERE, closure); |
| } |
| - void FinishPrerolling(bool end_of_stream) { |
| - // 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. |
| - base::AutoLock l(lock_); |
| - EXPECT_TRUE(prerolling_); |
| - paint_was_called_ = false; |
| - while (prerolling_) { |
| - if (!read_cb_.is_null()) { |
| - DeliverNextFrame_Locked(end_of_stream); |
| - } else { |
| - // We want to wait iff we're still prerolling but have no pending read. |
| - cv_.TimedWait(timeout_); |
| - CHECK(!prerolling_ || !read_cb_.is_null()) |
| - << "Timed out waiting for preroll or read to occur."; |
| - } |
| - } |
| - EXPECT_TRUE(read_cb_.is_null()); |
| - WaitForPaint_Locked(); |
| - } |
| + void FlushRequested(const base::Closure& callback) { |
| + DCHECK_EQ(&message_loop_, MessageLoop::current()); |
| - void Paint() { |
| - base::AutoLock l(lock_); |
| - paint_was_called_ = true; |
| - paint_cv_.Signal(); |
| - } |
| + decode_results_.clear(); |
| + if (!read_cb_.is_null()) { |
| + QueueAbortedRead(); |
| + SatisfyPendingRead(); |
| + } |
| - void WaitForPaint_Locked() { |
| - lock_.AssertAcquired(); |
| - if (paint_was_called_) |
| - return; |
| - paint_cv_.TimedWait(timeout_); |
| - EXPECT_TRUE(paint_was_called_); |
| + message_loop_.PostTask(FROM_HERE, callback); |
| } |
| + MessageLoop message_loop_; |
| + |
| + // Used to protect |time_|. |
| base::Lock lock_; |
| - base::ConditionVariable cv_; |
| - base::WaitableEvent event_; |
| - base::TimeDelta timeout_; |
| + base::TimeDelta time_; |
| - // Used in conjunction with |lock_| and |cv_| for satisfying reads. |
| - bool prerolling_; |
| + // Used for satisfying reads. |
| VideoDecoder::ReadCB read_cb_; |
| int next_frame_timestamp_; |
| int duration_; |
| - base::TimeDelta time_; |
| - // Used in conjunction with |lock_| to wait for Paint() calls. |
| - base::ConditionVariable paint_cv_; |
| - bool paint_was_called_; |
| + WaitableMessageLoopEvent error_event_; |
| + WaitableMessageLoopEvent ended_event_; |
| - // Holding queue for Read callbacks for exercising delayed demux/decode. |
| - bool should_queue_read_cb_; |
| - VideoDecoder::ReadCB queued_read_cb_; |
| + std::deque<std::pair< |
| + VideoDecoder::Status, scoped_refptr<VideoFrame> > > decode_results_; |
| DISALLOW_COPY_AND_ASSIGN(VideoRendererBaseTest); |
| }; |
| TEST_F(VideoRendererBaseTest, Initialize) { |
| Initialize(); |
| - ExpectCurrentTimestamp(0); |
| + EXPECT_EQ(0, GetCurrentTimestamp()); |
| Shutdown(); |
| } |
| @@ -458,22 +412,15 @@ TEST_F(VideoRendererBaseTest, EndOfStream_DefaultFrameDuration) { |
| Initialize(); |
| Play(); |
| - // Finish rendering up to the next-to-last frame. |
| - int timestamp = kFrameDuration; |
| - for (int i = 1; i < limits::kMaxVideoFrames; ++i) { |
| - RenderFrame(timestamp); |
| - timestamp += kFrameDuration; |
| - } |
| - |
| - // Deliver the end of stream frame. |
| - DeliverNextFrame(true); |
| - |
| // Verify that the ended callback fires when the default last frame duration |
| // has elapsed. |
| - int end_timestamp = |
| - timestamp + VideoRendererBase::kMaxLastFrameDuration().InMilliseconds(); |
| + int end_timestamp = kFrameDuration * limits::kMaxVideoFrames + |
| + VideoRendererBase::kMaxLastFrameDuration().InMilliseconds(); |
| EXPECT_LT(end_timestamp, kVideoDuration); |
| - RenderLastFrame(end_timestamp); |
| + |
| + QueueEndOfStream(); |
| + SetTime(end_timestamp); |
| + WaitForEnded(); |
| Shutdown(); |
| } |
| @@ -486,40 +433,39 @@ TEST_F(VideoRendererBaseTest, EndOfStream_ClipDuration) { |
| // Render all frames except for the last |limits::kMaxVideoFrames| frames |
| // and deliver all the frames between the start and |duration|. The preroll |
| // inside Initialize() makes this a little confusing, but |timestamp| is |
| - // the current render time and DeliverNextFrame() delivers a frame with a |
| + // the current render time and QueueNextFrame() delivers a frame with a |
| // timestamp that is |timestamp| + limits::kMaxVideoFrames * kFrameDuration. |
| int timestamp = kFrameDuration; |
| int end_timestamp = duration - limits::kMaxVideoFrames * kFrameDuration; |
| for (; timestamp < end_timestamp; timestamp += kFrameDuration) { |
| - RenderFrame(timestamp); |
| - DeliverNextFrame(false); |
| + QueueNextFrame(); |
| } |
| - // Render the next frame so that a Read() will get requested. |
| - RenderFrame(timestamp); |
| - |
| - // Deliver the end of stream frame and wait for the last frame to be rendered. |
| - DeliverNextFrame(true); |
| - RenderLastFrame(duration); |
| + // Queue the end of stream frame and wait for the last frame to be rendered. |
| + QueueEndOfStream(); |
| + SetTime(duration); |
| + WaitForEnded(); |
| Shutdown(); |
| } |
| -TEST_F(VideoRendererBaseTest, DecoderError) { |
| +TEST_F(VideoRendererBaseTest, DecodeError_Playing) { |
| Initialize(); |
| Play(); |
| - RenderFrame(kFrameDuration); |
| - EXPECT_CALL(*this, OnError(PIPELINE_ERROR_DECODE)); |
| - DecoderError(); |
| + |
| + QueueDecodeError(); |
| + SetTime(kVideoDuration); |
| + WaitForError(PIPELINE_ERROR_DECODE); |
| Shutdown(); |
| } |
| -TEST_F(VideoRendererBaseTest, DecoderErrorDuringPreroll) { |
| +TEST_F(VideoRendererBaseTest, DecodeError_DuringPreroll) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - StartPrerolling(kFrameDuration * 6, PIPELINE_ERROR_DECODE); |
| - DecoderError(); |
| + |
| + QueueDecodeError(); |
| + Preroll(kFrameDuration * 6, PIPELINE_ERROR_DECODE); |
| Shutdown(); |
| } |
| @@ -527,8 +473,10 @@ TEST_F(VideoRendererBaseTest, Preroll_Exact) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - Preroll(kFrameDuration * 6); |
| - ExpectCurrentTimestamp(kFrameDuration * 6); |
| + QueuePrerollFrames(kFrameDuration * 6); |
| + |
| + Preroll(kFrameDuration * 6, PIPELINE_OK); |
| + EXPECT_EQ(kFrameDuration * 6, GetCurrentTimestamp()); |
| Shutdown(); |
| } |
| @@ -536,8 +484,10 @@ TEST_F(VideoRendererBaseTest, Preroll_RightBefore) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - Preroll(kFrameDuration * 6 - 1); |
| - ExpectCurrentTimestamp(kFrameDuration * 5); |
| + QueuePrerollFrames(kFrameDuration * 6); |
| + |
| + Preroll(kFrameDuration * 6 - 1, PIPELINE_OK); |
| + EXPECT_EQ(kFrameDuration * 5, GetCurrentTimestamp()); |
| Shutdown(); |
| } |
| @@ -545,21 +495,23 @@ TEST_F(VideoRendererBaseTest, Preroll_RightAfter) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - Preroll(kFrameDuration * 6 + 1); |
| - ExpectCurrentTimestamp(kFrameDuration * 6); |
| + QueuePrerollFrames(kFrameDuration * 6); |
| + |
| + Preroll(kFrameDuration * 6 + 1, PIPELINE_OK); |
| + EXPECT_EQ(kFrameDuration * 6, GetCurrentTimestamp()); |
| Shutdown(); |
| } |
| TEST_F(VideoRendererBaseTest, GetCurrentFrame_Initialized) { |
| Initialize(); |
| - ExpectCurrentFrame(true); // Due to prerolling. |
| + EXPECT_TRUE(GetCurrentFrame()); // Due to prerolling. |
| Shutdown(); |
| } |
| TEST_F(VideoRendererBaseTest, GetCurrentFrame_Playing) { |
| Initialize(); |
| Play(); |
| - ExpectCurrentFrame(true); |
| + EXPECT_TRUE(GetCurrentFrame()); |
| Shutdown(); |
| } |
| @@ -567,7 +519,7 @@ TEST_F(VideoRendererBaseTest, GetCurrentFrame_Paused) { |
| Initialize(); |
| Play(); |
| Pause(); |
| - ExpectCurrentFrame(true); |
| + EXPECT_TRUE(GetCurrentFrame()); |
| Shutdown(); |
| } |
| @@ -576,7 +528,7 @@ TEST_F(VideoRendererBaseTest, GetCurrentFrame_Flushed) { |
| Play(); |
| Pause(); |
| Flush(); |
| - ExpectCurrentFrame(false); |
| + EXPECT_FALSE(GetCurrentFrame()); |
| Shutdown(); |
| } |
| @@ -593,14 +545,13 @@ TEST_F(VideoRendererBaseTest, MAYBE_GetCurrentFrame_EndOfStream) { |
| Flush(); |
| // Preroll only end of stream frames. |
| - Preroll(kEndOfStream); |
| - ExpectCurrentFrame(false); |
| + QueueEndOfStream(); |
| + Preroll(0, PIPELINE_OK); |
| + EXPECT_FALSE(GetCurrentFrame()); |
| // Start playing, we should immediately get notified of end of stream. |
| - EXPECT_CALL(*this, OnEnded()) |
| - .WillOnce(Invoke(event(), &base::WaitableEvent::Signal)); |
| Play(); |
| - CHECK(event()->TimedWait(timeout())) << "Timed out waiting for ended signal."; |
| + WaitForEnded(); |
| Shutdown(); |
| } |
| @@ -608,14 +559,14 @@ TEST_F(VideoRendererBaseTest, MAYBE_GetCurrentFrame_EndOfStream) { |
| TEST_F(VideoRendererBaseTest, GetCurrentFrame_Shutdown) { |
| Initialize(); |
| Shutdown(); |
| - ExpectCurrentFrame(false); |
| + EXPECT_FALSE(GetCurrentFrame()); |
| } |
| // Stop() is called immediately during an error. |
| TEST_F(VideoRendererBaseTest, GetCurrentFrame_Error) { |
| Initialize(); |
| Stop(); |
| - ExpectCurrentFrame(false); |
| + EXPECT_FALSE(GetCurrentFrame()); |
| } |
| // Verify that shutdown can only proceed after we return the current frame. |
| @@ -631,11 +582,12 @@ TEST_F(VideoRendererBaseTest, Shutdown_DuringPaint) { |
| Pause(); |
| // Start flushing -- it won't complete until we return the frame. |
| - renderer_->Flush(NewWaitableClosure()); |
| + WaitableMessageLoopEvent event; |
| + renderer_->Flush(event.GetClosure()); |
| // Return the frame and wait. |
| renderer_->PutCurrentFrame(frame); |
| - WaitForClosure(); |
| + event.RunAndWait(); |
| Stop(); |
| } |
| @@ -645,26 +597,28 @@ TEST_F(VideoRendererBaseTest, StopDuringOutstandingRead) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - QueueReadCB(); |
| - StartPrerolling(kFrameDuration * 6, PIPELINE_OK); // Force-decode some more. |
| - renderer_->Stop(NewWaitableClosure()); |
| - SatisfyQueuedReadCB(); |
| - WaitForClosure(); // Finish the Stop(). |
| + QueuePrerollFrames(kFrameDuration * 6); |
| + Preroll(kFrameDuration * 6, PIPELINE_OK); // Force-decode some more. |
| + |
| + WaitableMessageLoopEvent event; |
| + renderer_->Stop(event.GetClosure()); |
| + QueueEndOfStream(); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
I need to think more about this test -- I don't th
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
Yeah. You need to make sure there is a pending rea
|
| + event.RunAndWait(); |
| } |
| TEST_F(VideoRendererBaseTest, AbortPendingRead_Playing) { |
| Initialize(); |
| Play(); |
| - // Render a frame to trigger a Read(). |
| - RenderFrame(kFrameDuration); |
| - |
| - AbortRead(); |
| + // Advance time a bit to trigger a Read(). |
| + SetTime(kFrameDuration); |
| + QueueAbortedRead(); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
ditto here -- but I'm also not sure what we're sup
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
Aborted reads when the ChunkDemuxer can't satisfy
|
| Pause(); |
| Flush(); |
| - Preroll(kFrameDuration * 6); |
| - ExpectCurrentTimestamp(kFrameDuration * 6); |
| + QueuePrerollFrames(kFrameDuration * 6); |
| + Preroll(kFrameDuration * 6, PIPELINE_OK); |
| + EXPECT_EQ(kFrameDuration * 6, GetCurrentTimestamp()); |
| Shutdown(); |
| } |
| @@ -672,8 +626,8 @@ TEST_F(VideoRendererBaseTest, AbortPendingRead_Flush) { |
| Initialize(); |
| Play(); |
| - // Render a frame to trigger a Read(). |
| - RenderFrame(kFrameDuration); |
| + // Advance time a bit to trigger a Read(). |
| + SetTime(kFrameDuration); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
ditto
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
I think this one is ok assuming it executes the !r
|
| Pause(); |
| Flush(); |
| @@ -684,9 +638,9 @@ TEST_F(VideoRendererBaseTest, AbortPendingRead_Preroll) { |
| Initialize(); |
| Pause(); |
| Flush(); |
| - StartPrerolling(kFrameDuration * 6, PIPELINE_OK); |
| - AbortRead(); |
| - VerifyNotPrerolling(); |
| + |
| + QueueAbortedRead(); |
| + Preroll(kFrameDuration * 6, PIPELINE_OK); |
|
scherkus (not reviewing)
2012/12/05 00:03:46
this one makes sense -- since we don't call QueueP
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
As I mentioned above, the abort tests have more to
|
| Shutdown(); |
| } |
| @@ -696,6 +650,8 @@ TEST_F(VideoRendererBaseTest, VideoDecoder_InitFailure) { |
| EXPECT_CALL(*decoder_, Initialize(_, _, _)) |
| .WillOnce(RunCallback<1>(PIPELINE_ERROR_DECODE)); |
| InitializeRenderer(PIPELINE_ERROR_DECODE); |
| + |
| + Stop(); |
|
acolwell GONE FROM CHROMIUM
2012/12/05 18:15:25
Why is stop needed? The initialization failed.
scherkus (not reviewing)
2012/12/05 21:52:03
VRB DCHECKs in the dtor that the state is uninitia
acolwell GONE FROM CHROMIUM
2012/12/05 23:00:54
ok. TODO for removal w/ VRB changes?
|
| } |
| } // namespace media |