OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/bind.h" |
| 6 #include "base/callback_helpers.h" |
| 7 #include "base/message_loop/message_loop.h" |
| 8 #include "base/run_loop.h" |
| 9 #include "base/test/simple_test_tick_clock.h" |
| 10 #include "media/base/null_video_sink.h" |
| 11 #include "media/base/test_helpers.h" |
| 12 #include "testing/gmock/include/gmock/gmock.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 |
| 15 using testing::_; |
| 16 using testing::DoAll; |
| 17 using testing::Return; |
| 18 |
| 19 namespace media { |
| 20 |
| 21 ACTION_P(RunClosure, closure) { |
| 22 closure.Run(); |
| 23 } |
| 24 |
| 25 class NullVideoSinkTest : public testing::Test, |
| 26 public VideoRendererSink::RenderCallback { |
| 27 public: |
| 28 NullVideoSinkTest() { |
| 29 // Never use null TimeTicks since they have special connotations. |
| 30 tick_clock_.Advance(base::TimeDelta::FromMicroseconds(12345)); |
| 31 } |
| 32 ~NullVideoSinkTest() override {} |
| 33 |
| 34 scoped_ptr<NullVideoSink> ConstructSink(bool clockless, |
| 35 base::TimeDelta interval) { |
| 36 scoped_ptr<NullVideoSink> new_sink(new NullVideoSink( |
| 37 clockless, interval, |
| 38 base::Bind(&NullVideoSinkTest::FrameReceived, base::Unretained(this)), |
| 39 message_loop_.task_runner())); |
| 40 new_sink->set_tick_clock_for_testing(&tick_clock_); |
| 41 return new_sink; |
| 42 } |
| 43 |
| 44 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) { |
| 45 const gfx::Size natural_size(8, 8); |
| 46 return VideoFrame::CreateFrame(VideoFrame::YV12, natural_size, |
| 47 gfx::Rect(natural_size), natural_size, |
| 48 timestamp); |
| 49 } |
| 50 |
| 51 // VideoRendererSink::RenderCallback implementation. |
| 52 MOCK_METHOD2(Render, |
| 53 scoped_refptr<VideoFrame>(base::TimeTicks, base::TimeTicks)); |
| 54 MOCK_METHOD0(OnFrameDropped, void()); |
| 55 |
| 56 MOCK_METHOD1(FrameReceived, void(const scoped_refptr<VideoFrame>&)); |
| 57 |
| 58 protected: |
| 59 base::MessageLoop message_loop_; |
| 60 base::SimpleTestTickClock tick_clock_; |
| 61 |
| 62 DISALLOW_COPY_AND_ASSIGN(NullVideoSinkTest); |
| 63 }; |
| 64 |
| 65 TEST_F(NullVideoSinkTest, BasicFunctionality) { |
| 66 const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(25); |
| 67 |
| 68 scoped_ptr<NullVideoSink> sink = ConstructSink(false, kInterval); |
| 69 scoped_refptr<VideoFrame> test_frame = CreateFrame(base::TimeDelta()); |
| 70 |
| 71 // The sink shouldn't have to be started to use the paint method. |
| 72 EXPECT_CALL(*this, FrameReceived(test_frame)); |
| 73 sink->PaintFrameUsingOldRenderingPath(test_frame); |
| 74 |
| 75 { |
| 76 SCOPED_TRACE("Waiting for sink startup."); |
| 77 sink->Start(this); |
| 78 const base::TimeTicks current_time = tick_clock_.NowTicks(); |
| 79 const base::TimeTicks current_interval_end = current_time + kInterval; |
| 80 EXPECT_CALL(*this, Render(current_time, current_interval_end)) |
| 81 .WillOnce(Return(test_frame)); |
| 82 base::RunLoop run_loop; |
| 83 EXPECT_CALL(*this, FrameReceived(test_frame)) |
| 84 .WillOnce(RunClosure(run_loop.QuitClosure())); |
| 85 run_loop.Run(); |
| 86 } |
| 87 |
| 88 // A second call returning the same frame should not result in a new call to |
| 89 // FrameReceived(). |
| 90 { |
| 91 SCOPED_TRACE("Waiting for second render call."); |
| 92 WaitableMessageLoopEvent event; |
| 93 EXPECT_CALL(*this, Render(_, _)) |
| 94 .WillOnce(Return(test_frame)) |
| 95 .WillOnce(Return(nullptr)); |
| 96 EXPECT_CALL(*this, FrameReceived(test_frame)).Times(0); |
| 97 EXPECT_CALL(*this, FrameReceived(scoped_refptr<VideoFrame>())) |
| 98 .WillOnce(RunClosure(event.GetClosure())); |
| 99 event.RunAndWait(); |
| 100 } |
| 101 |
| 102 { |
| 103 SCOPED_TRACE("Waiting for stop event."); |
| 104 WaitableMessageLoopEvent event; |
| 105 sink->set_stop_callback(event.GetClosure()); |
| 106 sink->Stop(); |
| 107 event.RunAndWait(); |
| 108 } |
| 109 } |
| 110 |
| 111 TEST_F(NullVideoSinkTest, ClocklessFunctionality) { |
| 112 // Construct the sink with a huge interval, it should still complete quickly. |
| 113 const base::TimeDelta interval = base::TimeDelta::FromSeconds(10); |
| 114 scoped_ptr<NullVideoSink> sink = ConstructSink(true, interval); |
| 115 |
| 116 scoped_refptr<VideoFrame> test_frame = CreateFrame(base::TimeDelta()); |
| 117 sink->Start(this); |
| 118 |
| 119 EXPECT_CALL(*this, FrameReceived(test_frame)).Times(1); |
| 120 EXPECT_CALL(*this, FrameReceived(scoped_refptr<VideoFrame>())).Times(1); |
| 121 |
| 122 const int kTestRuns = 6; |
| 123 const base::TimeTicks now = base::TimeTicks::Now(); |
| 124 const base::TimeTicks current_time = tick_clock_.NowTicks(); |
| 125 |
| 126 // Use a RunLoop instead of WaitableMessageLoopEvent() since it will only quit |
| 127 // the loop when it's idle, instead of quitting immediately which is required |
| 128 // when clockless playback is enabled (otherwise the loop is never idle). |
| 129 base::RunLoop run_loop; |
| 130 for (int i = 0; i < kTestRuns; ++i) { |
| 131 if (i < kTestRuns - 1) { |
| 132 EXPECT_CALL(*this, Render(current_time + i * interval, |
| 133 current_time + (i + 1) * interval)) |
| 134 .WillOnce(Return(test_frame)); |
| 135 } else { |
| 136 EXPECT_CALL(*this, Render(current_time + i * interval, |
| 137 current_time + (i + 1) * interval)) |
| 138 .WillOnce(DoAll(RunClosure(run_loop.QuitClosure()), Return(nullptr))); |
| 139 } |
| 140 } |
| 141 |
| 142 run_loop.Run(); |
| 143 ASSERT_LT(base::TimeTicks::Now() - now, kTestRuns * interval); |
| 144 sink->Stop(); |
| 145 } |
| 146 |
| 147 } |
OLD | NEW |