OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "base/base64.h" | |
6 #include "base/file_util.h" | |
7 #include "base/message_loop/message_loop.h" | |
8 #include "base/run_loop.h" | |
9 #include "base/single_thread_task_runner.h" | |
10 #include "base/synchronization/waitable_event.h" | |
11 #include "base/thread_task_runner_handle.h" | |
12 #include "net/base/test_data_directory.h" | |
13 #include "net/url_request/url_request_context_getter.h" | |
14 #include "remoting/base/rsa_key_pair.h" | |
15 #include "remoting/client/audio_player.h" | |
16 #include "remoting/client/chromoting_client.h" | |
17 #include "remoting/client/client_context.h" | |
18 #include "remoting/client/client_user_interface.h" | |
19 #include "remoting/client/video_renderer.h" | |
20 #include "remoting/host/chromoting_host.h" | |
21 #include "remoting/host/chromoting_host_context.h" | |
22 #include "remoting/host/fake_desktop_environment.h" | |
23 #include "remoting/host/video_scheduler.h" | |
24 #include "remoting/protocol/chromium_port_allocator.h" | |
25 #include "remoting/protocol/jingle_session_manager.h" | |
26 #include "remoting/protocol/libjingle_transport_factory.h" | |
27 #include "remoting/protocol/me2me_host_authenticator_factory.h" | |
28 #include "remoting/protocol/negotiating_client_authenticator.h" | |
29 #include "remoting/protocol/session_config.h" | |
30 #include "remoting/signaling/fake_signal_strategy.h" | |
31 #include "testing/gtest/include/gtest/gtest.h" | |
32 | |
33 namespace remoting { | |
34 | |
35 using protocol::ChannelConfig; | |
36 | |
37 const char kHostJid[] = "host_jid@example.com/host"; | |
38 const char kHostOwner[] = "jane.doe@example.com"; | |
39 const char kClientJid[] = "jane.doe@example.com/client"; | |
40 | |
41 class ProtocolPerfTest : public testing::Test, | |
42 public ClientUserInterface, | |
43 public VideoRenderer, | |
44 public HostStatusObserver { | |
45 public: | |
46 ProtocolPerfTest() | |
47 : capture_thread_("capture"), | |
48 encode_thread_("encode") { | |
49 VideoScheduler::EnableTimestampsForTests(); | |
50 capture_thread_.Start(); | |
51 encode_thread_.Start(); | |
52 } | |
53 ~ProtocolPerfTest() { | |
54 message_loop_.RunUntilIdle(); | |
55 } | |
56 | |
57 // ClientUserInterface interface. | |
58 virtual void OnConnectionState(protocol::ConnectionToHost::State state, | |
59 protocol::ErrorCode error) OVERRIDE { | |
60 if (state == protocol::ConnectionToHost::CONNECTED) { | |
61 message_loop_.PostTask( | |
62 FROM_HERE, | |
63 base::Bind(&ProtocolPerfTest::OnClientConnectedMainThread, | |
64 base::Unretained(this))); | |
65 } | |
66 } | |
67 virtual void OnConnectionReady(bool ready) OVERRIDE {} | |
68 virtual void OnRouteChanged(const std::string& channel_name, | |
69 const protocol::TransportRoute& route) OVERRIDE { | |
70 } | |
71 virtual void SetCapabilities(const std::string& capabilities) OVERRIDE {} | |
72 virtual void SetPairingResponse( | |
73 const protocol::PairingResponse& pairing_response) OVERRIDE {} | |
74 virtual void DeliverHostMessage( | |
75 const protocol::ExtensionMessage& message) OVERRIDE {} | |
76 virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE { | |
77 return NULL; | |
78 } | |
79 virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE { | |
80 return NULL; | |
81 } | |
82 | |
83 // VideoRenderer interface. | |
84 virtual void Initialize(const protocol::SessionConfig& config) OVERRIDE {} | |
85 virtual ChromotingStats* GetStats() OVERRIDE { return NULL; } | |
86 virtual void ProcessVideoPacket(scoped_ptr<VideoPacket> video_packet, | |
87 const base::Closure& done) OVERRIDE { | |
88 if (video_packet->data().empty()) { | |
89 // Ignore keep-alive packets | |
90 done.Run(); | |
91 return; | |
92 } | |
93 | |
94 last_video_packet_ = video_packet.Pass(); | |
95 | |
96 if (!on_frame_task_.is_null()) | |
97 on_frame_task_.Run(); | |
98 | |
99 done.Run(); | |
100 } | |
101 | |
102 // HostStatusObserver interface. | |
103 virtual void OnClientConnected(const std::string& jid) { | |
104 message_loop_.PostTask( | |
105 FROM_HERE, | |
106 base::Bind(&ProtocolPerfTest::OnHostConnectedMainThread, | |
107 base::Unretained(this))); | |
108 } | |
109 | |
110 protected: | |
111 void WaitConnected() { | |
112 client_connected_ = false; | |
113 host_connected_ = false; | |
114 | |
115 connecting_loop_.reset(new base::RunLoop()); | |
116 connecting_loop_->Run(); | |
117 | |
118 ASSERT_TRUE(client_connected_ && host_connected_); | |
119 } | |
120 | |
121 void ReceiveFrame(base::TimeDelta* latency) { | |
122 waiting_frames_loop_.reset(new base::RunLoop()); | |
123 on_frame_task_ = waiting_frames_loop_->QuitClosure(); | |
124 waiting_frames_loop_->Run(); | |
125 | |
126 if (latency) { | |
127 base::TimeTicks timestamp = | |
128 base::TimeTicks::FromInternalValue(last_video_packet_->timestamp()); | |
129 *latency = base::TimeTicks::Now() - timestamp; | |
130 } | |
131 } | |
132 | |
133 void ReceiveFrames(int frames, base::TimeDelta* max_latency) { | |
134 if (max_latency) | |
135 *max_latency = base::TimeDelta(); | |
136 | |
137 for (int i = 0; i < frames; ++i) { | |
138 base::TimeDelta latency; | |
139 | |
140 ReceiveFrame(&latency); | |
141 | |
142 if (max_latency && latency > *max_latency) { | |
143 *max_latency = latency; | |
144 } | |
145 } | |
146 } | |
147 | |
148 void OnHostConnectedMainThread() { | |
149 host_connected_ = true; | |
150 if (client_connected_) | |
151 connecting_loop_->Quit(); | |
152 } | |
153 | |
154 void OnClientConnectedMainThread() { | |
155 client_connected_ = true; | |
156 if (host_connected_) | |
157 connecting_loop_->Quit(); | |
158 } | |
159 | |
160 void StartHostAndClient(protocol::ChannelConfig::Codec video_codec) { | |
161 host_signaling_.reset(new FakeSignalStrategy(kHostJid)); | |
162 client_signaling_.reset(new FakeSignalStrategy(kClientJid)); | |
163 FakeSignalStrategy::Connect(host_signaling_.get(), client_signaling_.get()); | |
164 | |
165 protocol::NetworkSettings network_settings( | |
166 protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING); | |
167 | |
168 scoped_ptr<protocol::CandidateSessionConfig> protocol_config = | |
169 protocol::CandidateSessionConfig::CreateDefault(); | |
170 protocol_config->DisableAudioChannel(); | |
171 protocol_config->mutable_video_configs()->clear(); | |
172 protocol_config->mutable_video_configs()->push_back(protocol::ChannelConfig( | |
173 protocol::ChannelConfig::TRANSPORT_STREAM, 2, video_codec)); | |
174 | |
175 // TODO(sergeyu): Replace with a fake port allocator. | |
176 scoped_ptr<cricket::HttpPortAllocatorBase> host_port_allocator = | |
177 protocol::ChromiumPortAllocator::Create(NULL, network_settings) | |
178 .PassAs<cricket::HttpPortAllocatorBase>(); | |
179 | |
180 scoped_ptr<protocol::TransportFactory> host_transport_factory( | |
181 new protocol::LibjingleTransportFactory( | |
182 host_signaling_.get(), | |
183 host_port_allocator.Pass(), | |
184 network_settings)); | |
185 | |
186 scoped_ptr<protocol::SessionManager> session_manager( | |
187 new protocol::JingleSessionManager(host_transport_factory.Pass())); | |
188 | |
189 // Encoder runs on a separate thread, main thread is used for everything | |
190 // else. | |
191 host_.reset(new ChromotingHost(host_signaling_.get(), | |
192 &desktop_environment_factory_, | |
193 session_manager.Pass(), | |
194 message_loop_.message_loop_proxy(), | |
195 message_loop_.message_loop_proxy(), | |
196 capture_thread_.message_loop_proxy(), | |
197 encode_thread_.message_loop_proxy(), | |
198 message_loop_.message_loop_proxy(), | |
199 message_loop_.message_loop_proxy())); | |
200 | |
201 base::FilePath certs_dir(net::GetTestCertsDirectory()); | |
202 | |
203 std::string host_cert; | |
204 ASSERT_TRUE(base::ReadFileToString( | |
205 certs_dir.AppendASCII("unittest.selfsigned.der"), &host_cert)); | |
206 | |
207 base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin"); | |
208 std::string key_string; | |
209 ASSERT_TRUE(base::ReadFileToString(key_path, &key_string)); | |
210 std::string key_base64; | |
211 base::Base64Encode(key_string, &key_base64); | |
212 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(key_base64); | |
213 ASSERT_TRUE(key_pair.get()); | |
214 | |
215 | |
216 protocol::SharedSecretHash host_secret; | |
217 host_secret.hash_function = protocol::AuthenticationMethod::NONE; | |
218 host_secret.value = "123456"; | |
219 scoped_ptr<protocol::AuthenticatorFactory> auth_factory = | |
220 protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret( | |
221 true, kHostOwner, host_cert, key_pair, host_secret, NULL); | |
222 host_->SetAuthenticatorFactory(auth_factory.Pass()); | |
223 | |
224 host_->AddStatusObserver(this); | |
225 host_->set_protocol_config(protocol_config->Clone()); | |
226 host_->Start(kHostOwner); | |
227 | |
228 // Initialize client. | |
229 client_context_.reset( | |
230 new ClientContext(base::ThreadTaskRunnerHandle::Get())); | |
231 | |
232 // TODO(sergeyu): Replace with a fake port allocator | |
233 scoped_ptr<cricket::HttpPortAllocatorBase> client_port_allocator = | |
234 protocol::ChromiumPortAllocator::Create(NULL, network_settings) | |
235 .PassAs<cricket::HttpPortAllocatorBase>(); | |
236 | |
237 scoped_ptr<protocol::TransportFactory> client_transport_factory( | |
238 new protocol::LibjingleTransportFactory(client_signaling_.get(), | |
239 client_port_allocator.Pass(), | |
240 network_settings)); | |
241 | |
242 std::vector<protocol::AuthenticationMethod> auth_methods; | |
243 auth_methods.push_back(protocol::AuthenticationMethod::Spake2( | |
244 protocol::AuthenticationMethod::NONE)); | |
245 scoped_ptr<protocol::Authenticator> client_authenticator( | |
246 new protocol::NegotiatingClientAuthenticator( | |
247 std::string(), // client_pairing_id | |
248 std::string(), // client_pairing_secret | |
249 std::string(), // authentication_tag | |
250 base::Bind(&ProtocolPerfTest::FetchPin, base::Unretained(this)), | |
251 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(), | |
252 auth_methods)); | |
253 client_.reset(new ChromotingClient( | |
254 client_context_.get(), this, this, scoped_ptr<AudioPlayer>())); | |
255 client_->set_candidate_config(protocol_config->Clone()); | |
256 client_->Start( | |
257 client_signaling_.get(), client_authenticator.Pass(), | |
258 client_transport_factory.Pass(), kHostJid, std::string()); | |
259 } | |
260 | |
261 void FetchPin( | |
262 bool pairing_supported, | |
263 const protocol::SecretFetchedCallback& secret_fetched_callback) { | |
264 secret_fetched_callback.Run("123456"); | |
265 } | |
266 | |
267 base::MessageLoopForIO message_loop_; | |
268 | |
269 FakeDesktopEnvironmentFactory desktop_environment_factory_; | |
270 base::Thread capture_thread_; | |
271 base::Thread encode_thread_; | |
272 | |
273 scoped_ptr<FakeSignalStrategy> host_signaling_; | |
274 scoped_ptr<FakeSignalStrategy> client_signaling_; | |
275 | |
276 scoped_ptr<ChromotingHost> host_; | |
277 scoped_ptr<ClientContext> client_context_; | |
278 scoped_ptr<ChromotingClient> client_; | |
279 | |
280 scoped_ptr<base::RunLoop> connecting_loop_; | |
281 scoped_ptr<base::RunLoop> waiting_frames_loop_; | |
282 | |
283 bool client_connected_; | |
284 bool host_connected_; | |
285 | |
286 base::Closure on_frame_task_; | |
287 | |
288 scoped_ptr<VideoPacket> last_video_packet_; | |
289 | |
290 DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest); | |
291 }; | |
292 | |
293 TEST_F(ProtocolPerfTest, StreamFrameRate) { | |
294 StartHostAndClient(protocol::ChannelConfig::CODEC_VP8); | |
295 ASSERT_NO_FATAL_FAILURE(WaitConnected()); | |
296 | |
297 base::TimeDelta latency; | |
298 | |
299 ReceiveFrame(&latency); | |
300 LOG(ERROR) << "First frame latency: " << latency.InMillisecondsF() << "ms"; | |
301 ReceiveFrames(20, NULL); | |
302 | |
303 base::TimeTicks started = base::TimeTicks::Now(); | |
304 ReceiveFrames(40, &latency); | |
305 base::TimeDelta elapsed = base::TimeTicks::Now() - started; | |
306 LOG(ERROR) << "Frame rate: " << (40.0 / elapsed.InSecondsF()); | |
rmsousa
2014/07/16 23:06:00
Nit: LOG(INFO)?
Sergey Ulanov
2014/07/17 01:47:58
Done.
| |
307 LOG(ERROR) << "Maximum latency: " << latency.InMillisecondsF() << "ms"; | |
308 } | |
309 | |
310 // Frame generator that rewrites the whole screen every 60th frame. | |
311 class IntermittentChangeFrameGenerator | |
312 : public base::RefCountedThreadSafe<IntermittentChangeFrameGenerator> { | |
313 public: | |
314 IntermittentChangeFrameGenerator() | |
315 : frame_index_(0) {} | |
316 | |
317 scoped_ptr<webrtc::DesktopFrame> GenerateFrame( | |
318 webrtc::ScreenCapturer::Callback* callback) { | |
319 const int kWidth = 800; | |
320 const int kHeight = 600; | |
321 | |
322 bool fresh_frame = false; | |
323 if (frame_index_ % 60 == 0 || !current_frame_) { | |
324 current_frame_.reset(webrtc::SharedDesktopFrame::Wrap( | |
rmsousa
2014/07/16 23:06:00
If you don't write anything to the frame, will it
Sergey Ulanov
2014/07/17 01:47:58
It will contain garbage. BasicDesktopFrame() just
| |
325 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight)))); | |
326 fresh_frame = true; | |
327 } | |
328 ++frame_index_; | |
329 | |
330 scoped_ptr<webrtc::DesktopFrame> result(current_frame_->Share()); | |
331 result->mutable_updated_region()->Clear(); | |
332 if (fresh_frame) { | |
333 result->mutable_updated_region()->AddRect( | |
334 webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight)); | |
335 } | |
336 return result.Pass(); | |
337 } | |
338 | |
339 private: | |
340 ~IntermittentChangeFrameGenerator() {} | |
341 friend class RefCountedThreadSafe<IntermittentChangeFrameGenerator>; | |
342 | |
343 int frame_index_; | |
344 scoped_ptr<webrtc::SharedDesktopFrame> current_frame_; | |
345 | |
346 DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator); | |
347 }; | |
348 | |
349 TEST_F(ProtocolPerfTest, IntermittentChanges) { | |
350 desktop_environment_factory_.set_frame_generator( | |
351 base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame, | |
352 new IntermittentChangeFrameGenerator())); | |
353 | |
354 StartHostAndClient(protocol::ChannelConfig::CODEC_VERBATIM); | |
355 ASSERT_NO_FATAL_FAILURE(WaitConnected()); | |
356 | |
357 ReceiveFrame(NULL); | |
358 | |
359 for (int i = 0; i < 5; ++i) { | |
360 base::TimeDelta latency; | |
361 ReceiveFrame(&latency); | |
362 LOG(ERROR) << "Latency: " << latency.InMillisecondsF() | |
363 << "ms Encode: " << last_video_packet_->encode_time_ms() | |
364 << "ms Capture: " << last_video_packet_->capture_time_ms() | |
365 << "ms"; | |
366 } | |
367 } | |
368 | |
369 } // namespace remoting | |
OLD | NEW |