Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(580)

Side by Side Diff: remoting/test/protocol_perf_test.cc

Issue 394883008: Add basic perf tests for chromoting protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« remoting/remoting_host.gypi ('K') | « remoting/remoting_test.gypi ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« remoting/remoting_host.gypi ('K') | « remoting/remoting_test.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698