1 // Copyright 2014 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 "base/base64.h"
6 #include "base/files/file_util.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/rand_util.h"
9 #include "base/run_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "jingle/glue/thread_wrapper.h"
14 #include "net/base/test_data_directory.h"
15 #include "net/url_request/url_request_context_getter.h"
16 #include "remoting/base/rsa_key_pair.h"
17 #include "remoting/client/audio_player.h"
18 #include "remoting/client/chromoting_client.h"
19 #include "remoting/client/client_context.h"
20 #include "remoting/client/client_user_interface.h"
21 #include "remoting/client/video_renderer.h"
22 #include "remoting/host/chromoting_host.h"
23 #include "remoting/host/chromoting_host_context.h"
24 #include "remoting/host/fake_desktop_environment.h"
25 #include "remoting/host/video_frame_pump.h"
26 #include "remoting/protocol/jingle_session_manager.h"
27 #include "remoting/protocol/libjingle_transport_factory.h"
28 #include "remoting/protocol/me2me_host_authenticator_factory.h"
29 #include "remoting/protocol/negotiating_client_authenticator.h"
30 #include "remoting/protocol/session_config.h"
31 #include "remoting/signaling/fake_signal_strategy.h"
32 #include "remoting/test/fake_network_dispatcher.h"
33 #include "remoting/test/fake_port_allocator.h"
34 #include "remoting/test/fake_socket_factory.h"
35 #include "testing/gtest/include/gtest/gtest.h"
39 using protocol::ChannelConfig
;
41 const char kHostJid
[] = "host_jid@example.com/host";
42 const char kHostOwner
[] = "jane.doe@example.com";
43 const char kClientJid
[] = "jane.doe@example.com/client";
45 struct NetworkPerformanceParams
{
46 NetworkPerformanceParams(int bandwidth
,
48 double latency_average_ms
,
49 double latency_stddev_ms
,
50 double out_of_order_rate
)
51 : bandwidth(bandwidth
),
52 max_buffers(max_buffers
),
53 latency_average(base::TimeDelta::FromMillisecondsD(latency_average_ms
)),
54 latency_stddev(base::TimeDelta::FromMillisecondsD(latency_stddev_ms
)),
55 out_of_order_rate(out_of_order_rate
) {}
59 base::TimeDelta latency_average
;
60 base::TimeDelta latency_stddev
;
61 double out_of_order_rate
;
64 class FakeCursorShapeStub
: public protocol::CursorShapeStub
{
66 FakeCursorShapeStub() {}
67 ~FakeCursorShapeStub() override
{}
69 // protocol::CursorShapeStub interface.
70 void SetCursorShape(const protocol::CursorShapeInfo
& cursor_shape
) override
{};
73 class ProtocolPerfTest
74 : public testing::Test
,
75 public testing::WithParamInterface
<NetworkPerformanceParams
>,
76 public ClientUserInterface
,
78 public protocol::VideoStub
,
79 public HostStatusObserver
{
82 : host_thread_("host"),
83 capture_thread_("capture"),
84 encode_thread_("encode") {
85 VideoFramePump::EnableTimestampsForTests();
86 host_thread_
.StartWithOptions(
87 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
88 capture_thread_
.Start();
89 encode_thread_
.Start();
92 virtual ~ProtocolPerfTest() {
93 host_thread_
.task_runner()->DeleteSoon(FROM_HERE
, host_
.release());
94 host_thread_
.task_runner()->DeleteSoon(FROM_HERE
,
95 host_signaling_
.release());
96 message_loop_
.RunUntilIdle();
99 // ClientUserInterface interface.
100 void OnConnectionState(protocol::ConnectionToHost::State state
,
101 protocol::ErrorCode error
) override
{
102 if (state
== protocol::ConnectionToHost::CONNECTED
) {
103 client_connected_
= true;
105 connecting_loop_
->Quit();
108 void OnConnectionReady(bool ready
) override
{}
109 void OnRouteChanged(const std::string
& channel_name
,
110 const protocol::TransportRoute
& route
) override
{}
111 void SetCapabilities(const std::string
& capabilities
) override
{}
112 void SetPairingResponse(
113 const protocol::PairingResponse
& pairing_response
) override
{}
114 void DeliverHostMessage(const protocol::ExtensionMessage
& message
) override
{}
115 protocol::ClipboardStub
* GetClipboardStub() override
{ return nullptr; }
116 protocol::CursorShapeStub
* GetCursorShapeStub() override
{
117 return &cursor_shape_stub_
;
120 // VideoRenderer interface.
121 void OnSessionConfig(const protocol::SessionConfig
& config
) override
{}
122 ChromotingStats
* GetStats() override
{ return nullptr; }
123 protocol::VideoStub
* GetVideoStub() override
{ return this; }
125 // protocol::VideoStub interface.
126 void ProcessVideoPacket(scoped_ptr
<VideoPacket
> video_packet
,
127 const base::Closure
& done
) override
{
128 if (video_packet
->data().empty()) {
129 // Ignore keep-alive packets
134 last_video_packet_
= video_packet
.Pass();
136 if (!on_frame_task_
.is_null())
137 on_frame_task_
.Run();
142 // HostStatusObserver interface.
143 void OnClientConnected(const std::string
& jid
) override
{
144 message_loop_
.PostTask(
146 base::Bind(&ProtocolPerfTest::OnHostConnectedMainThread
,
147 base::Unretained(this)));
151 void WaitConnected() {
152 client_connected_
= false;
153 host_connected_
= false;
155 connecting_loop_
.reset(new base::RunLoop());
156 connecting_loop_
->Run();
158 ASSERT_TRUE(client_connected_
&& host_connected_
);
161 void OnHostConnectedMainThread() {
162 host_connected_
= true;
163 if (client_connected_
)
164 connecting_loop_
->Quit();
167 void ReceiveFrame(base::TimeDelta
* latency
) {
168 waiting_frames_loop_
.reset(new base::RunLoop());
169 on_frame_task_
= waiting_frames_loop_
->QuitClosure();
170 waiting_frames_loop_
->Run();
173 base::TimeTicks timestamp
=
174 base::TimeTicks::FromInternalValue(last_video_packet_
->timestamp());
175 *latency
= base::TimeTicks::Now() - timestamp
;
179 void ReceiveFrames(int frames
, base::TimeDelta
* max_latency
) {
181 *max_latency
= base::TimeDelta();
183 for (int i
= 0; i
< frames
; ++i
) {
184 base::TimeDelta latency
;
186 ReceiveFrame(&latency
);
188 if (max_latency
&& latency
> *max_latency
) {
189 *max_latency
= latency
;
194 // Creates test host and client and starts connection between them. Caller
195 // should call WaitConnected() to wait until connection is established. The
196 // host is started on |host_thread_| while the client works on the main
198 void StartHostAndClient(protocol::ChannelConfig::Codec video_codec
) {
199 fake_network_dispatcher_
= new FakeNetworkDispatcher();
201 client_signaling_
.reset(new FakeSignalStrategy(kClientJid
));
203 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
205 protocol_config_
= protocol::CandidateSessionConfig::CreateDefault();
206 protocol_config_
->DisableAudioChannel();
207 protocol_config_
->mutable_video_configs()->clear();
208 protocol_config_
->mutable_video_configs()->push_back(
209 protocol::ChannelConfig(
210 protocol::ChannelConfig::TRANSPORT_STREAM
, 2, video_codec
));
212 host_thread_
.task_runner()->PostTask(
214 base::Bind(&ProtocolPerfTest::StartHost
, base::Unretained(this)));
218 DCHECK(host_thread_
.task_runner()->BelongsToCurrentThread());
220 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
222 host_signaling_
.reset(new FakeSignalStrategy(kHostJid
));
223 host_signaling_
->ConnectTo(client_signaling_
.get());
225 protocol::NetworkSettings
network_settings(
226 protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING
);
228 scoped_ptr
<FakePortAllocator
> port_allocator(
229 FakePortAllocator::Create(fake_network_dispatcher_
));
230 port_allocator
->socket_factory()->SetBandwidth(GetParam().bandwidth
,
231 GetParam().max_buffers
);
232 port_allocator
->socket_factory()->SetLatency(GetParam().latency_average
,
233 GetParam().latency_stddev
);
234 port_allocator
->socket_factory()->set_out_of_order_rate(
235 GetParam().out_of_order_rate
);
236 scoped_ptr
<protocol::TransportFactory
> host_transport_factory(
237 new protocol::LibjingleTransportFactory(
238 host_signaling_
.get(), port_allocator
.Pass(), network_settings
,
239 protocol::TransportRole::SERVER
));
241 scoped_ptr
<protocol::SessionManager
> session_manager(
242 new protocol::JingleSessionManager(host_transport_factory
.Pass()));
244 // Encoder runs on a separate thread, main thread is used for everything
246 host_
.reset(new ChromotingHost(host_signaling_
.get(),
247 &desktop_environment_factory_
,
248 session_manager
.Pass(),
249 host_thread_
.task_runner(),
250 host_thread_
.task_runner(),
251 capture_thread_
.task_runner(),
252 encode_thread_
.task_runner(),
253 host_thread_
.task_runner(),
254 host_thread_
.task_runner()));
256 base::FilePath
certs_dir(net::GetTestCertsDirectory());
258 std::string host_cert
;
259 ASSERT_TRUE(base::ReadFileToString(
260 certs_dir
.AppendASCII("unittest.selfsigned.der"), &host_cert
));
262 base::FilePath key_path
= certs_dir
.AppendASCII("unittest.key.bin");
263 std::string key_string
;
264 ASSERT_TRUE(base::ReadFileToString(key_path
, &key_string
));
265 std::string key_base64
;
266 base::Base64Encode(key_string
, &key_base64
);
267 scoped_refptr
<RsaKeyPair
> key_pair
= RsaKeyPair::FromString(key_base64
);
268 ASSERT_TRUE(key_pair
.get());
271 protocol::SharedSecretHash host_secret
;
272 host_secret
.hash_function
= protocol::AuthenticationMethod::NONE
;
273 host_secret
.value
= "123456";
274 scoped_ptr
<protocol::AuthenticatorFactory
> auth_factory
=
275 protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
276 true, kHostOwner
, host_cert
, key_pair
, host_secret
, nullptr);
277 host_
->SetAuthenticatorFactory(auth_factory
.Pass());
279 host_
->AddStatusObserver(this);
280 host_
->set_protocol_config(protocol_config_
->Clone());
281 host_
->Start(kHostOwner
);
283 message_loop_
.PostTask(FROM_HERE
,
284 base::Bind(&ProtocolPerfTest::StartClientAfterHost
,
285 base::Unretained(this)));
288 void StartClientAfterHost() {
289 client_signaling_
->ConnectTo(host_signaling_
.get());
291 protocol::NetworkSettings
network_settings(
292 protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING
);
294 // Initialize client.
295 client_context_
.reset(
296 new ClientContext(base::ThreadTaskRunnerHandle::Get()));
298 scoped_ptr
<FakePortAllocator
> port_allocator(
299 FakePortAllocator::Create(fake_network_dispatcher_
));
300 port_allocator
->socket_factory()->SetBandwidth(GetParam().bandwidth
,
301 GetParam().max_buffers
);
302 port_allocator
->socket_factory()->SetLatency(GetParam().latency_average
,
303 GetParam().latency_stddev
);
304 port_allocator
->socket_factory()->set_out_of_order_rate(
305 GetParam().out_of_order_rate
);
306 scoped_ptr
<protocol::TransportFactory
> client_transport_factory(
307 new protocol::LibjingleTransportFactory(
308 client_signaling_
.get(), port_allocator
.Pass(), network_settings
,
309 protocol::TransportRole::CLIENT
));
311 std::vector
<protocol::AuthenticationMethod
> auth_methods
;
312 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
313 protocol::AuthenticationMethod::NONE
));
314 scoped_ptr
<protocol::Authenticator
> client_authenticator(
315 new protocol::NegotiatingClientAuthenticator(
316 std::string(), // client_pairing_id
317 std::string(), // client_pairing_secret
318 std::string(), // authentication_tag
319 base::Bind(&ProtocolPerfTest::FetchPin
, base::Unretained(this)),
323 new ChromotingClient(client_context_
.get(), this, this, nullptr));
324 client_
->SetProtocolConfigForTests(protocol_config_
->Clone());
326 client_signaling_
.get(), client_authenticator
.Pass(),
327 client_transport_factory
.Pass(), kHostJid
, std::string());
331 bool pairing_supported
,
332 const protocol::SecretFetchedCallback
& secret_fetched_callback
) {
333 secret_fetched_callback
.Run("123456");
336 base::MessageLoopForIO message_loop_
;
338 scoped_refptr
<FakeNetworkDispatcher
> fake_network_dispatcher_
;
340 base::Thread host_thread_
;
341 base::Thread capture_thread_
;
342 base::Thread encode_thread_
;
343 FakeDesktopEnvironmentFactory desktop_environment_factory_
;
345 FakeCursorShapeStub cursor_shape_stub_
;
347 scoped_ptr
<protocol::CandidateSessionConfig
> protocol_config_
;
349 scoped_ptr
<FakeSignalStrategy
> host_signaling_
;
350 scoped_ptr
<FakeSignalStrategy
> client_signaling_
;
352 scoped_ptr
<ChromotingHost
> host_
;
353 scoped_ptr
<ClientContext
> client_context_
;
354 scoped_ptr
<ChromotingClient
> client_
;
356 scoped_ptr
<base::RunLoop
> connecting_loop_
;
357 scoped_ptr
<base::RunLoop
> waiting_frames_loop_
;
359 bool client_connected_
;
360 bool host_connected_
;
362 base::Closure on_frame_task_
;
364 scoped_ptr
<VideoPacket
> last_video_packet_
;
367 DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest
);
370 INSTANTIATE_TEST_CASE_P(
373 ::testing::Values(NetworkPerformanceParams(0, 0, 0, 0, 0.0)));
375 INSTANTIATE_TEST_CASE_P(
378 ::testing::Values(NetworkPerformanceParams(0, 0, 300, 30, 0.0),
379 NetworkPerformanceParams(0, 0, 30, 10, 0.0)));
381 INSTANTIATE_TEST_CASE_P(
384 ::testing::Values(NetworkPerformanceParams(0, 0, 2, 0, 0.01),
385 NetworkPerformanceParams(0, 0, 30, 1, 0.01),
386 NetworkPerformanceParams(0, 0, 30, 1, 0.1),
387 NetworkPerformanceParams(0, 0, 300, 20, 0.01),
388 NetworkPerformanceParams(0, 0, 300, 20, 0.1)));
390 INSTANTIATE_TEST_CASE_P(
395 NetworkPerformanceParams(800000000, 800000000, 2, 1, 0.0),
397 NetworkPerformanceParams(1000000, 300000, 30, 5, 0.01),
398 NetworkPerformanceParams(1000000, 2000000, 30, 5, 0.01),
400 NetworkPerformanceParams(100000, 30000, 130, 5, 0.01),
401 NetworkPerformanceParams(100000, 200000, 130, 5, 0.01)));
403 TEST_P(ProtocolPerfTest
, StreamFrameRate
) {
404 StartHostAndClient(protocol::ChannelConfig::CODEC_VP8
);
405 ASSERT_NO_FATAL_FAILURE(WaitConnected());
407 base::TimeDelta latency
;
409 ReceiveFrame(&latency
);
410 LOG(INFO
) << "First frame latency: " << latency
.InMillisecondsF() << "ms";
411 ReceiveFrames(20, nullptr);
413 base::TimeTicks started
= base::TimeTicks::Now();
414 ReceiveFrames(40, &latency
);
415 base::TimeDelta elapsed
= base::TimeTicks::Now() - started
;
416 LOG(INFO
) << "Frame rate: " << (40.0 / elapsed
.InSecondsF());
417 LOG(INFO
) << "Maximum latency: " << latency
.InMillisecondsF() << "ms";
420 const int kIntermittentFrameSize
= 100 * 1000;
422 // Frame generator that rewrites the whole screen every 60th frame. Should only
423 // be used with the VERBATIM codec as the allocated frame may contain arbitrary
425 class IntermittentChangeFrameGenerator
426 : public base::RefCountedThreadSafe
<IntermittentChangeFrameGenerator
> {
428 IntermittentChangeFrameGenerator()
431 scoped_ptr
<webrtc::DesktopFrame
> GenerateFrame(
432 webrtc::DesktopCapturer::Callback
* callback
) {
433 const int kWidth
= 1000;
434 const int kHeight
= kIntermittentFrameSize
/ kWidth
/ 4;
436 bool fresh_frame
= false;
437 if (frame_index_
% 60 == 0 || !current_frame_
) {
438 current_frame_
.reset(webrtc::SharedDesktopFrame::Wrap(
439 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth
, kHeight
))));
444 scoped_ptr
<webrtc::DesktopFrame
> result(current_frame_
->Share());
445 result
->mutable_updated_region()->Clear();
447 result
->mutable_updated_region()->AddRect(
448 webrtc::DesktopRect::MakeXYWH(0, 0, kWidth
, kHeight
));
450 return result
.Pass();
454 ~IntermittentChangeFrameGenerator() {}
455 friend class base::RefCountedThreadSafe
<IntermittentChangeFrameGenerator
>;
458 scoped_ptr
<webrtc::SharedDesktopFrame
> current_frame_
;
460 DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator
);
463 TEST_P(ProtocolPerfTest
, IntermittentChanges
) {
464 desktop_environment_factory_
.set_frame_generator(
465 base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame
,
466 new IntermittentChangeFrameGenerator()));
468 StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM
);
469 ASSERT_NO_FATAL_FAILURE(WaitConnected());
471 ReceiveFrame(nullptr);
473 base::TimeDelta expected
= GetParam().latency_average
;
474 if (GetParam().bandwidth
> 0) {
475 expected
+= base::TimeDelta::FromSecondsD(kIntermittentFrameSize
/
476 GetParam().bandwidth
);
478 LOG(INFO
) << "Expected: " << expected
.InMillisecondsF() << "ms";
482 const int kFrames
= 5;
483 for (int i
= 0; i
< kFrames
; ++i
) {
484 base::TimeDelta latency
;
485 ReceiveFrame(&latency
);
486 LOG(INFO
) << "Latency: " << latency
.InMillisecondsF()
487 << "ms Encode: " << last_video_packet_
->encode_time_ms()
488 << "ms Capture: " << last_video_packet_
->capture_time_ms()
493 LOG(INFO
) << "Average: " << (sum
/ kFrames
).InMillisecondsF();
496 } // namespace remoting