Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / remoting / test / protocol_perftest.cc
blob2387ddb5d9c5a4f8cdae2435226849b2e6d35a52
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/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_scheduler.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 ProtocolPerfTest
65 : public testing::Test,
66 public testing::WithParamInterface<NetworkPerformanceParams>,
67 public ClientUserInterface,
68 public VideoRenderer,
69 public HostStatusObserver {
70 public:
71 ProtocolPerfTest()
72 : host_thread_("host"),
73 capture_thread_("capture"),
74 encode_thread_("encode") {
75 VideoScheduler::EnableTimestampsForTests();
76 host_thread_.StartWithOptions(
77 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
78 capture_thread_.Start();
79 encode_thread_.Start();
82 virtual ~ProtocolPerfTest() {
83 host_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, host_.release());
84 host_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE,
85 host_signaling_.release());
86 message_loop_.RunUntilIdle();
89 // ClientUserInterface interface.
90 virtual void OnConnectionState(protocol::ConnectionToHost::State state,
91 protocol::ErrorCode error) OVERRIDE {
92 if (state == protocol::ConnectionToHost::CONNECTED) {
93 client_connected_ = true;
94 if (host_connected_)
95 connecting_loop_->Quit();
98 virtual void OnConnectionReady(bool ready) OVERRIDE {}
99 virtual void OnRouteChanged(const std::string& channel_name,
100 const protocol::TransportRoute& route) OVERRIDE {
102 virtual void SetCapabilities(const std::string& capabilities) OVERRIDE {}
103 virtual void SetPairingResponse(
104 const protocol::PairingResponse& pairing_response) OVERRIDE {}
105 virtual void DeliverHostMessage(
106 const protocol::ExtensionMessage& message) OVERRIDE {}
107 virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE {
108 return NULL;
110 virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE {
111 return NULL;
114 // VideoRenderer interface.
115 virtual void Initialize(const protocol::SessionConfig& config) OVERRIDE {}
116 virtual ChromotingStats* GetStats() OVERRIDE { return NULL; }
117 virtual void ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet,
118 const base::Closure& done) OVERRIDE {
119 if (video_packet->data().empty()) {
120 // Ignore keep-alive packets
121 done.Run();
122 return;
125 last_video_packet_ = video_packet.Pass();
127 if (!on_frame_task_.is_null())
128 on_frame_task_.Run();
130 done.Run();
133 // HostStatusObserver interface.
134 virtual void OnClientConnected(const std::string& jid) OVERRIDE {
135 message_loop_.PostTask(
136 FROM_HERE,
137 base::Bind(&ProtocolPerfTest::OnHostConnectedMainThread,
138 base::Unretained(this)));
141 protected:
142 void WaitConnected() {
143 client_connected_ = false;
144 host_connected_ = false;
146 connecting_loop_.reset(new base::RunLoop());
147 connecting_loop_->Run();
149 ASSERT_TRUE(client_connected_ && host_connected_);
152 void OnHostConnectedMainThread() {
153 host_connected_ = true;
154 if (client_connected_)
155 connecting_loop_->Quit();
158 void ReceiveFrame(base::TimeDelta* latency) {
159 waiting_frames_loop_.reset(new base::RunLoop());
160 on_frame_task_ = waiting_frames_loop_->QuitClosure();
161 waiting_frames_loop_->Run();
163 if (latency) {
164 base::TimeTicks timestamp =
165 base::TimeTicks::FromInternalValue(last_video_packet_->timestamp());
166 *latency = base::TimeTicks::Now() - timestamp;
170 void ReceiveFrames(int frames, base::TimeDelta* max_latency) {
171 if (max_latency)
172 *max_latency = base::TimeDelta();
174 for (int i = 0; i < frames; ++i) {
175 base::TimeDelta latency;
177 ReceiveFrame(&latency);
179 if (max_latency && latency > *max_latency) {
180 *max_latency = latency;
185 // Creates test host and client and starts connection between them. Caller
186 // should call WaitConnected() to wait until connection is established. The
187 // host is started on |host_thread_| while the client works on the main
188 // thread.
189 void StartHostAndClient(protocol::ChannelConfig::Codec video_codec) {
190 fake_network_dispatcher_ = new FakeNetworkDispatcher();
192 client_signaling_.reset(new FakeSignalStrategy(kClientJid));
194 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
196 protocol_config_ = protocol::CandidateSessionConfig::CreateDefault();
197 protocol_config_->DisableAudioChannel();
198 protocol_config_->mutable_video_configs()->clear();
199 protocol_config_->mutable_video_configs()->push_back(
200 protocol::ChannelConfig(
201 protocol::ChannelConfig::TRANSPORT_STREAM, 2, video_codec));
203 host_thread_.message_loop_proxy()->PostTask(
204 FROM_HERE,
205 base::Bind(&ProtocolPerfTest::StartHost, base::Unretained(this)));
208 void StartHost() {
209 DCHECK(host_thread_.message_loop_proxy()->BelongsToCurrentThread());
211 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
213 host_signaling_.reset(new FakeSignalStrategy(kHostJid));
214 host_signaling_->ConnectTo(client_signaling_.get());
216 protocol::NetworkSettings network_settings(
217 protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
219 scoped_ptr<FakePortAllocator> port_allocator(
220 FakePortAllocator::Create(fake_network_dispatcher_));
221 port_allocator->socket_factory()->SetBandwidth(GetParam().bandwidth,
222 GetParam().max_buffers);
223 port_allocator->socket_factory()->SetLatency(GetParam().latency_average,
224 GetParam().latency_stddev);
225 port_allocator->socket_factory()->set_out_of_order_rate(
226 GetParam().out_of_order_rate);
227 scoped_ptr<protocol::TransportFactory> host_transport_factory(
228 new protocol::LibjingleTransportFactory(
229 host_signaling_.get(),
230 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
231 network_settings));
233 scoped_ptr<protocol::SessionManager> session_manager(
234 new protocol::JingleSessionManager(host_transport_factory.Pass()));
236 // Encoder runs on a separate thread, main thread is used for everything
237 // else.
238 host_.reset(new ChromotingHost(host_signaling_.get(),
239 &desktop_environment_factory_,
240 session_manager.Pass(),
241 host_thread_.message_loop_proxy(),
242 host_thread_.message_loop_proxy(),
243 capture_thread_.message_loop_proxy(),
244 encode_thread_.message_loop_proxy(),
245 host_thread_.message_loop_proxy(),
246 host_thread_.message_loop_proxy()));
248 base::FilePath certs_dir(net::GetTestCertsDirectory());
250 std::string host_cert;
251 ASSERT_TRUE(base::ReadFileToString(
252 certs_dir.AppendASCII("unittest.selfsigned.der"), &host_cert));
254 base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
255 std::string key_string;
256 ASSERT_TRUE(base::ReadFileToString(key_path, &key_string));
257 std::string key_base64;
258 base::Base64Encode(key_string, &key_base64);
259 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(key_base64);
260 ASSERT_TRUE(key_pair.get());
263 protocol::SharedSecretHash host_secret;
264 host_secret.hash_function = protocol::AuthenticationMethod::NONE;
265 host_secret.value = "123456";
266 scoped_ptr<protocol::AuthenticatorFactory> auth_factory =
267 protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
268 true, kHostOwner, host_cert, key_pair, host_secret, NULL);
269 host_->SetAuthenticatorFactory(auth_factory.Pass());
271 host_->AddStatusObserver(this);
272 host_->set_protocol_config(protocol_config_->Clone());
273 host_->Start(kHostOwner);
275 message_loop_.PostTask(FROM_HERE,
276 base::Bind(&ProtocolPerfTest::StartClientAfterHost,
277 base::Unretained(this)));
280 void StartClientAfterHost() {
281 client_signaling_->ConnectTo(host_signaling_.get());
283 protocol::NetworkSettings network_settings(
284 protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
286 // Initialize client.
287 client_context_.reset(
288 new ClientContext(base::ThreadTaskRunnerHandle::Get()));
290 scoped_ptr<FakePortAllocator> port_allocator(
291 FakePortAllocator::Create(fake_network_dispatcher_));
292 port_allocator->socket_factory()->SetBandwidth(GetParam().bandwidth,
293 GetParam().max_buffers);
294 port_allocator->socket_factory()->SetLatency(GetParam().latency_average,
295 GetParam().latency_stddev);
296 port_allocator->socket_factory()->set_out_of_order_rate(
297 GetParam().out_of_order_rate);
298 scoped_ptr<protocol::TransportFactory> client_transport_factory(
299 new protocol::LibjingleTransportFactory(
300 client_signaling_.get(),
301 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
302 network_settings));
304 std::vector<protocol::AuthenticationMethod> auth_methods;
305 auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
306 protocol::AuthenticationMethod::NONE));
307 scoped_ptr<protocol::Authenticator> client_authenticator(
308 new protocol::NegotiatingClientAuthenticator(
309 std::string(), // client_pairing_id
310 std::string(), // client_pairing_secret
311 std::string(), // authentication_tag
312 base::Bind(&ProtocolPerfTest::FetchPin, base::Unretained(this)),
313 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(),
314 auth_methods));
315 client_.reset(new ChromotingClient(
316 client_context_.get(), this, this, scoped_ptr<AudioPlayer>()));
317 client_->SetProtocolConfigForTests(protocol_config_->Clone());
318 client_->Start(
319 client_signaling_.get(), client_authenticator.Pass(),
320 client_transport_factory.Pass(), kHostJid, std::string());
323 void FetchPin(
324 bool pairing_supported,
325 const protocol::SecretFetchedCallback& secret_fetched_callback) {
326 secret_fetched_callback.Run("123456");
329 base::MessageLoopForIO message_loop_;
331 scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher_;
333 base::Thread host_thread_;
334 base::Thread capture_thread_;
335 base::Thread encode_thread_;
336 FakeDesktopEnvironmentFactory desktop_environment_factory_;
338 scoped_ptr<protocol::CandidateSessionConfig> protocol_config_;
340 scoped_ptr<FakeSignalStrategy> host_signaling_;
341 scoped_ptr<FakeSignalStrategy> client_signaling_;
343 scoped_ptr<ChromotingHost> host_;
344 scoped_ptr<ClientContext> client_context_;
345 scoped_ptr<ChromotingClient> client_;
347 scoped_ptr<base::RunLoop> connecting_loop_;
348 scoped_ptr<base::RunLoop> waiting_frames_loop_;
350 bool client_connected_;
351 bool host_connected_;
353 base::Closure on_frame_task_;
355 scoped_ptr<VideoPacket> last_video_packet_;
357 DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
360 INSTANTIATE_TEST_CASE_P(
361 NoDelay,
362 ProtocolPerfTest,
363 ::testing::Values(NetworkPerformanceParams(0, 0, 0, 0, 0.0)));
365 INSTANTIATE_TEST_CASE_P(
366 HighLatency,
367 ProtocolPerfTest,
368 ::testing::Values(NetworkPerformanceParams(0, 0, 300, 30, 0.0),
369 NetworkPerformanceParams(0, 0, 30, 10, 0.0)));
371 INSTANTIATE_TEST_CASE_P(
372 OutOfOrder,
373 ProtocolPerfTest,
374 ::testing::Values(NetworkPerformanceParams(0, 0, 2, 0, 0.01),
375 NetworkPerformanceParams(0, 0, 30, 1, 0.01),
376 NetworkPerformanceParams(0, 0, 30, 1, 0.1),
377 NetworkPerformanceParams(0, 0, 300, 20, 0.01),
378 NetworkPerformanceParams(0, 0, 300, 20, 0.1)));
380 INSTANTIATE_TEST_CASE_P(
381 LimitedBandwidth,
382 ProtocolPerfTest,
383 ::testing::Values(
384 // 100 MBps
385 NetworkPerformanceParams(800000000, 800000000, 2, 1, 0.0),
386 // 8 MBps
387 NetworkPerformanceParams(1000000, 300000, 30, 5, 0.01),
388 NetworkPerformanceParams(1000000, 2000000, 30, 5, 0.01),
389 // 800 kBps
390 NetworkPerformanceParams(100000, 30000, 130, 5, 0.01),
391 NetworkPerformanceParams(100000, 200000, 130, 5, 0.01)));
393 TEST_P(ProtocolPerfTest, StreamFrameRate) {
394 StartHostAndClient(protocol::ChannelConfig::CODEC_VP8);
395 ASSERT_NO_FATAL_FAILURE(WaitConnected());
397 base::TimeDelta latency;
399 ReceiveFrame(&latency);
400 LOG(INFO) << "First frame latency: " << latency.InMillisecondsF() << "ms";
401 ReceiveFrames(20, NULL);
403 base::TimeTicks started = base::TimeTicks::Now();
404 ReceiveFrames(40, &latency);
405 base::TimeDelta elapsed = base::TimeTicks::Now() - started;
406 LOG(INFO) << "Frame rate: " << (40.0 / elapsed.InSecondsF());
407 LOG(INFO) << "Maximum latency: " << latency.InMillisecondsF() << "ms";
410 const int kIntermittentFrameSize = 100 * 1000;
412 // Frame generator that rewrites the whole screen every 60th frame. Should only
413 // be used with the VERBATIM codec as the allocated frame may contain arbitrary
414 // data.
415 class IntermittentChangeFrameGenerator
416 : public base::RefCountedThreadSafe<IntermittentChangeFrameGenerator> {
417 public:
418 IntermittentChangeFrameGenerator()
419 : frame_index_(0) {}
421 scoped_ptr<webrtc::DesktopFrame> GenerateFrame(
422 webrtc::DesktopCapturer::Callback* callback) {
423 const int kWidth = 1000;
424 const int kHeight = kIntermittentFrameSize / kWidth / 4;
426 bool fresh_frame = false;
427 if (frame_index_ % 60 == 0 || !current_frame_) {
428 current_frame_.reset(webrtc::SharedDesktopFrame::Wrap(
429 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))));
430 fresh_frame = true;
432 ++frame_index_;
434 scoped_ptr<webrtc::DesktopFrame> result(current_frame_->Share());
435 result->mutable_updated_region()->Clear();
436 if (fresh_frame) {
437 result->mutable_updated_region()->AddRect(
438 webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight));
440 return result.Pass();
443 private:
444 ~IntermittentChangeFrameGenerator() {}
445 friend class base::RefCountedThreadSafe<IntermittentChangeFrameGenerator>;
447 int frame_index_;
448 scoped_ptr<webrtc::SharedDesktopFrame> current_frame_;
450 DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator);
453 TEST_P(ProtocolPerfTest, IntermittentChanges) {
454 desktop_environment_factory_.set_frame_generator(
455 base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame,
456 new IntermittentChangeFrameGenerator()));
458 StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM);
459 ASSERT_NO_FATAL_FAILURE(WaitConnected());
461 ReceiveFrame(NULL);
463 base::TimeDelta expected = GetParam().latency_average;
464 if (GetParam().bandwidth > 0) {
465 expected += base::TimeDelta::FromSecondsD(kIntermittentFrameSize /
466 GetParam().bandwidth);
468 LOG(INFO) << "Expected: " << expected.InMillisecondsF() << "ms";
470 base::TimeDelta sum;
472 const int kFrames = 5;
473 for (int i = 0; i < kFrames; ++i) {
474 base::TimeDelta latency;
475 ReceiveFrame(&latency);
476 LOG(INFO) << "Latency: " << latency.InMillisecondsF()
477 << "ms Encode: " << last_video_packet_->encode_time_ms()
478 << "ms Capture: " << last_video_packet_->capture_time_ms()
479 << "ms";
480 sum += latency;
483 LOG(INFO) << "Average: " << (sum / kFrames).InMillisecondsF();
486 } // namespace remoting