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.
5 #include "remoting/host/video_frame_pump.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "remoting/base/auto_thread.h"
12 #include "remoting/base/auto_thread_task_runner.h"
13 #include "remoting/codec/video_encoder.h"
14 #include "remoting/codec/video_encoder_verbatim.h"
15 #include "remoting/host/desktop_capturer_proxy.h"
16 #include "remoting/host/fake_desktop_capturer.h"
17 #include "remoting/host/host_mock_objects.h"
18 #include "remoting/proto/control.pb.h"
19 #include "remoting/proto/video.pb.h"
20 #include "remoting/protocol/protocol_mock_objects.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
24 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
26 using ::remoting::protocol::MockVideoStub
;
29 using ::testing::AtLeast
;
30 using ::testing::DoAll
;
31 using ::testing::Expectation
;
32 using ::testing::InvokeWithoutArgs
;
33 using ::testing::Return
;
43 scoped_ptr
<webrtc::DesktopFrame
> CreateNullFrame(
44 webrtc::DesktopCapturer::Callback
*) {
48 scoped_ptr
<webrtc::DesktopFrame
> CreateUnchangedFrame(
49 webrtc::DesktopCapturer::Callback
*) {
50 const webrtc::DesktopSize
kSize(800, 640);
51 // updated_region() is already empty by default in new BasicDesktopFrames.
52 return make_scoped_ptr(new webrtc::BasicDesktopFrame(kSize
));
57 static const int kWidth
= 640;
58 static const int kHeight
= 480;
60 class ThreadCheckVideoEncoder
: public VideoEncoderVerbatim
{
62 ThreadCheckVideoEncoder(
63 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
64 : task_runner_(task_runner
) {
66 ~ThreadCheckVideoEncoder() override
{
67 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
70 scoped_ptr
<VideoPacket
> Encode(const webrtc::DesktopFrame
& frame
) override
{
71 return make_scoped_ptr(new VideoPacket());
75 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
77 DISALLOW_COPY_AND_ASSIGN(ThreadCheckVideoEncoder
);
80 class ThreadCheckDesktopCapturer
: public webrtc::DesktopCapturer
{
82 ThreadCheckDesktopCapturer(
83 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
84 : task_runner_(task_runner
), callback_(nullptr) {}
85 ~ThreadCheckDesktopCapturer() override
{
86 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
89 void Start(Callback
* callback
) override
{
90 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
91 EXPECT_FALSE(callback_
);
92 EXPECT_TRUE(callback
);
97 void Capture(const webrtc::DesktopRegion
& rect
) override
{
98 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
100 scoped_ptr
<webrtc::DesktopFrame
> frame(
101 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth
, kHeight
)));
102 frame
->mutable_updated_region()->SetRect(
103 webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10));
104 callback_
->OnCaptureCompleted(frame
.release());
108 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
109 webrtc::DesktopCapturer::Callback
* callback_
;
111 DISALLOW_COPY_AND_ASSIGN(ThreadCheckDesktopCapturer
);
114 class VideoFramePumpTest
: public testing::Test
{
116 void SetUp() override
;
117 void TearDown() override
;
119 void StartVideoFramePump(
120 scoped_ptr
<webrtc::DesktopCapturer
> capturer
,
121 scoped_ptr
<VideoEncoder
> encoder
);
124 base::MessageLoop message_loop_
;
125 base::RunLoop run_loop_
;
126 scoped_refptr
<AutoThreadTaskRunner
> capture_task_runner_
;
127 scoped_refptr
<AutoThreadTaskRunner
> encode_task_runner_
;
128 scoped_refptr
<AutoThreadTaskRunner
> main_task_runner_
;
129 scoped_ptr
<VideoFramePump
> pump_
;
131 MockVideoStub video_stub_
;
134 void VideoFramePumpTest::SetUp() {
135 main_task_runner_
= new AutoThreadTaskRunner(
136 message_loop_
.task_runner(), run_loop_
.QuitClosure());
137 capture_task_runner_
= AutoThread::Create("capture", main_task_runner_
);
138 encode_task_runner_
= AutoThread::Create("encode", main_task_runner_
);
141 void VideoFramePumpTest::TearDown() {
144 // Release the task runners, so that the test can quit.
145 capture_task_runner_
= nullptr;
146 encode_task_runner_
= nullptr;
147 main_task_runner_
= nullptr;
149 // Run the MessageLoop until everything has torn down.
153 // This test mocks capturer, encoder and network layer to simulate one capture
155 TEST_F(VideoFramePumpTest
, StartAndStop
) {
156 scoped_ptr
<ThreadCheckDesktopCapturer
> capturer(
157 new ThreadCheckDesktopCapturer(capture_task_runner_
));
158 scoped_ptr
<ThreadCheckVideoEncoder
> encoder(
159 new ThreadCheckVideoEncoder(encode_task_runner_
));
161 base::RunLoop run_loop
;
163 // When the first ProcessVideoPacket is received we stop the VideoFramePump.
164 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
167 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
)))
168 .RetiresOnSaturation();
170 // Start video frame capture.
171 pump_
.reset(new VideoFramePump(encode_task_runner_
,
172 make_scoped_ptr(new DesktopCapturerProxy(
173 capture_task_runner_
, capturer
.Pass())),
174 encoder
.Pass(), &video_stub_
));
176 // Run MessageLoop until the first frame is received.
180 // Tests that the pump handles null frames returned by the capturer.
181 TEST_F(VideoFramePumpTest
, NullFrame
) {
182 scoped_ptr
<FakeDesktopCapturer
> capturer(new FakeDesktopCapturer
);
183 scoped_ptr
<MockVideoEncoder
> encoder(new MockVideoEncoder
);
185 base::RunLoop run_loop
;
187 // Set up the capturer to return null frames.
188 capturer
->set_frame_generator(base::Bind(&CreateNullFrame
));
190 // Expect that the VideoEncoder::Encode() method is never called.
191 EXPECT_CALL(*encoder
, EncodePtr(_
)).Times(0);
193 // When the first ProcessVideoPacket is received we stop the VideoFramePump.
194 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
195 .WillOnce(DoAll(FinishSend(),
196 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
)))
197 .RetiresOnSaturation();
199 // Start video frame capture.
200 pump_
.reset(new VideoFramePump(encode_task_runner_
,
201 make_scoped_ptr(new DesktopCapturerProxy(
202 capture_task_runner_
, capturer
.Pass())),
203 encoder
.Pass(), &video_stub_
));
205 // Run MessageLoop until the first frame is received..
209 // Tests how the pump handles unchanged frames returned by the capturer.
210 TEST_F(VideoFramePumpTest
, UnchangedFrame
) {
211 scoped_ptr
<FakeDesktopCapturer
> capturer(new FakeDesktopCapturer
);
212 scoped_ptr
<MockVideoEncoder
> encoder(new MockVideoEncoder
);
214 base::RunLoop run_loop
;
216 // Set up the capturer to return unchanged frames.
217 capturer
->set_frame_generator(base::Bind(&CreateUnchangedFrame
));
219 // Expect that the VideoEncoder::Encode() method is called.
220 EXPECT_CALL(*encoder
, EncodePtr(_
)).WillRepeatedly(Return(nullptr));
222 // When the first ProcessVideoPacket is received we stop the VideoFramePump.
223 // TODO(wez): Verify that the generated packet has no content here.
224 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
225 .WillOnce(DoAll(FinishSend(),
226 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
)))
227 .RetiresOnSaturation();
229 // Start video frame capture.
230 pump_
.reset(new VideoFramePump(encode_task_runner_
,
231 make_scoped_ptr(new DesktopCapturerProxy(
232 capture_task_runner_
, capturer
.Pass())),
233 encoder
.Pass(), &video_stub_
));
235 // Run MessageLoop until the first frame is received..
239 } // namespace remoting