Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / remoting / test / protocol_perftest.cc
blobc9356c54a0e66a7f417002d6713d32bd65d814d2
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"
37 namespace remoting {
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,
47 int max_buffers,
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) {}
57 int bandwidth;
58 int max_buffers;
59 base::TimeDelta latency_average;
60 base::TimeDelta latency_stddev;
61 double out_of_order_rate;
64 class FakeCursorShapeStub : public protocol::CursorShapeStub {
65 public:
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,
77 public VideoRenderer,
78 public protocol::VideoStub,
79 public HostStatusObserver {
80 public:
81 ProtocolPerfTest()
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;
104 if (host_connected_)
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
130 done.Run();
131 return;
134 last_video_packet_ = video_packet.Pass();
136 if (!on_frame_task_.is_null())
137 on_frame_task_.Run();
139 done.Run();
142 // HostStatusObserver interface.
143 void OnClientConnected(const std::string& jid) override {
144 message_loop_.PostTask(
145 FROM_HERE,
146 base::Bind(&ProtocolPerfTest::OnHostConnectedMainThread,
147 base::Unretained(this)));
150 protected:
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();
172 if (latency) {
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) {
180 if (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
197 // thread.
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(
213 FROM_HERE,
214 base::Bind(&ProtocolPerfTest::StartHost, base::Unretained(this)));
217 void StartHost() {
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()));
243 session_manager->set_protocol_config(protocol_config_->Clone());
245 // Encoder runs on a separate thread, main thread is used for everything
246 // else.
247 host_.reset(new ChromotingHost(host_signaling_.get(),
248 &desktop_environment_factory_,
249 session_manager.Pass(),
250 host_thread_.task_runner(),
251 host_thread_.task_runner(),
252 capture_thread_.task_runner(),
253 encode_thread_.task_runner(),
254 host_thread_.task_runner(),
255 host_thread_.task_runner()));
257 base::FilePath certs_dir(net::GetTestCertsDirectory());
259 std::string host_cert;
260 ASSERT_TRUE(base::ReadFileToString(
261 certs_dir.AppendASCII("unittest.selfsigned.der"), &host_cert));
263 base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
264 std::string key_string;
265 ASSERT_TRUE(base::ReadFileToString(key_path, &key_string));
266 std::string key_base64;
267 base::Base64Encode(key_string, &key_base64);
268 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(key_base64);
269 ASSERT_TRUE(key_pair.get());
272 protocol::SharedSecretHash host_secret;
273 host_secret.hash_function = protocol::AuthenticationMethod::NONE;
274 host_secret.value = "123456";
275 scoped_ptr<protocol::AuthenticatorFactory> auth_factory =
276 protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
277 true, kHostOwner, host_cert, key_pair, host_secret, nullptr);
278 host_->SetAuthenticatorFactory(auth_factory.Pass());
280 host_->AddStatusObserver(this);
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)),
320 nullptr,
321 auth_methods));
322 client_.reset(
323 new ChromotingClient(client_context_.get(), this, this, nullptr));
324 client_->set_protocol_config(protocol_config_->Clone());
325 client_->Start(client_signaling_.get(), client_authenticator.Pass(),
326 client_transport_factory.Pass(), kHostJid, std::string());
329 void FetchPin(
330 bool pairing_supported,
331 const protocol::SecretFetchedCallback& secret_fetched_callback) {
332 secret_fetched_callback.Run("123456");
335 base::MessageLoopForIO message_loop_;
337 scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher_;
339 base::Thread host_thread_;
340 base::Thread capture_thread_;
341 base::Thread encode_thread_;
342 FakeDesktopEnvironmentFactory desktop_environment_factory_;
344 FakeCursorShapeStub cursor_shape_stub_;
346 scoped_ptr<protocol::CandidateSessionConfig> protocol_config_;
348 scoped_ptr<FakeSignalStrategy> host_signaling_;
349 scoped_ptr<FakeSignalStrategy> client_signaling_;
351 scoped_ptr<ChromotingHost> host_;
352 scoped_ptr<ClientContext> client_context_;
353 scoped_ptr<ChromotingClient> client_;
355 scoped_ptr<base::RunLoop> connecting_loop_;
356 scoped_ptr<base::RunLoop> waiting_frames_loop_;
358 bool client_connected_;
359 bool host_connected_;
361 base::Closure on_frame_task_;
363 scoped_ptr<VideoPacket> last_video_packet_;
365 private:
366 DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
369 INSTANTIATE_TEST_CASE_P(
370 NoDelay,
371 ProtocolPerfTest,
372 ::testing::Values(NetworkPerformanceParams(0, 0, 0, 0, 0.0)));
374 INSTANTIATE_TEST_CASE_P(
375 HighLatency,
376 ProtocolPerfTest,
377 ::testing::Values(NetworkPerformanceParams(0, 0, 300, 30, 0.0),
378 NetworkPerformanceParams(0, 0, 30, 10, 0.0)));
380 INSTANTIATE_TEST_CASE_P(
381 OutOfOrder,
382 ProtocolPerfTest,
383 ::testing::Values(NetworkPerformanceParams(0, 0, 2, 0, 0.01),
384 NetworkPerformanceParams(0, 0, 30, 1, 0.01),
385 NetworkPerformanceParams(0, 0, 30, 1, 0.1),
386 NetworkPerformanceParams(0, 0, 300, 20, 0.01),
387 NetworkPerformanceParams(0, 0, 300, 20, 0.1)));
389 INSTANTIATE_TEST_CASE_P(
390 LimitedBandwidth,
391 ProtocolPerfTest,
392 ::testing::Values(
393 // 100 MBps
394 NetworkPerformanceParams(800000000, 800000000, 2, 1, 0.0),
395 // 8 MBps
396 NetworkPerformanceParams(1000000, 300000, 30, 5, 0.01),
397 NetworkPerformanceParams(1000000, 2000000, 30, 5, 0.01),
398 // 800 kBps
399 NetworkPerformanceParams(100000, 30000, 130, 5, 0.01),
400 NetworkPerformanceParams(100000, 200000, 130, 5, 0.01)));
402 TEST_P(ProtocolPerfTest, StreamFrameRate) {
403 StartHostAndClient(protocol::ChannelConfig::CODEC_VP8);
404 ASSERT_NO_FATAL_FAILURE(WaitConnected());
406 base::TimeDelta latency;
408 ReceiveFrame(&latency);
409 LOG(INFO) << "First frame latency: " << latency.InMillisecondsF() << "ms";
410 ReceiveFrames(20, nullptr);
412 base::TimeTicks started = base::TimeTicks::Now();
413 ReceiveFrames(40, &latency);
414 base::TimeDelta elapsed = base::TimeTicks::Now() - started;
415 LOG(INFO) << "Frame rate: " << (40.0 / elapsed.InSecondsF());
416 LOG(INFO) << "Maximum latency: " << latency.InMillisecondsF() << "ms";
419 const int kIntermittentFrameSize = 100 * 1000;
421 // Frame generator that rewrites the whole screen every 60th frame. Should only
422 // be used with the VERBATIM codec as the allocated frame may contain arbitrary
423 // data.
424 class IntermittentChangeFrameGenerator
425 : public base::RefCountedThreadSafe<IntermittentChangeFrameGenerator> {
426 public:
427 IntermittentChangeFrameGenerator()
428 : frame_index_(0) {}
430 scoped_ptr<webrtc::DesktopFrame> GenerateFrame(
431 webrtc::DesktopCapturer::Callback* callback) {
432 const int kWidth = 1000;
433 const int kHeight = kIntermittentFrameSize / kWidth / 4;
435 bool fresh_frame = false;
436 if (frame_index_ % 60 == 0 || !current_frame_) {
437 current_frame_.reset(webrtc::SharedDesktopFrame::Wrap(
438 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))));
439 fresh_frame = true;
441 ++frame_index_;
443 scoped_ptr<webrtc::DesktopFrame> result(current_frame_->Share());
444 result->mutable_updated_region()->Clear();
445 if (fresh_frame) {
446 result->mutable_updated_region()->AddRect(
447 webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight));
449 return result.Pass();
452 private:
453 ~IntermittentChangeFrameGenerator() {}
454 friend class base::RefCountedThreadSafe<IntermittentChangeFrameGenerator>;
456 int frame_index_;
457 scoped_ptr<webrtc::SharedDesktopFrame> current_frame_;
459 DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator);
462 TEST_P(ProtocolPerfTest, IntermittentChanges) {
463 desktop_environment_factory_.set_frame_generator(
464 base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame,
465 new IntermittentChangeFrameGenerator()));
467 StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM);
468 ASSERT_NO_FATAL_FAILURE(WaitConnected());
470 ReceiveFrame(nullptr);
472 base::TimeDelta expected = GetParam().latency_average;
473 if (GetParam().bandwidth > 0) {
474 expected += base::TimeDelta::FromSecondsD(kIntermittentFrameSize /
475 GetParam().bandwidth);
477 LOG(INFO) << "Expected: " << expected.InMillisecondsF() << "ms";
479 base::TimeDelta sum;
481 const int kFrames = 5;
482 for (int i = 0; i < kFrames; ++i) {
483 base::TimeDelta latency;
484 ReceiveFrame(&latency);
485 LOG(INFO) << "Latency: " << latency.InMillisecondsF()
486 << "ms Encode: " << last_video_packet_->encode_time_ms()
487 << "ms Capture: " << last_video_packet_->capture_time_ms()
488 << "ms";
489 sum += latency;
492 LOG(INFO) << "Average: " << (sum / kFrames).InMillisecondsF();
495 } // namespace remoting