OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/chromoting_host.h" | 5 #include "remoting/host/chromoting_host.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
11 #include "build/build_config.h" | 11 #include "build/build_config.h" |
12 #include "remoting/base/constants.h" | 12 #include "remoting/base/constants.h" |
13 #include "remoting/base/encoder.h" | |
14 #include "remoting/base/encoder_row_based.h" | |
15 #include "remoting/base/encoder_vp8.h" | |
16 #include "remoting/codec/audio_encoder.h" | |
17 #include "remoting/codec/audio_encoder_verbatim.h" | |
18 #include "remoting/host/audio_scheduler.h" | |
19 #include "remoting/host/chromoting_host_context.h" | 13 #include "remoting/host/chromoting_host_context.h" |
20 #include "remoting/host/desktop_environment.h" | 14 #include "remoting/host/desktop_environment.h" |
15 #include "remoting/host/desktop_environment_factory.h" | |
21 #include "remoting/host/event_executor.h" | 16 #include "remoting/host/event_executor.h" |
22 #include "remoting/host/host_config.h" | 17 #include "remoting/host/host_config.h" |
23 #include "remoting/host/screen_recorder.h" | |
24 #include "remoting/protocol/connection_to_client.h" | 18 #include "remoting/protocol/connection_to_client.h" |
25 #include "remoting/protocol/client_stub.h" | 19 #include "remoting/protocol/client_stub.h" |
26 #include "remoting/protocol/host_stub.h" | 20 #include "remoting/protocol/host_stub.h" |
27 #include "remoting/protocol/input_stub.h" | 21 #include "remoting/protocol/input_stub.h" |
28 #include "remoting/protocol/session_config.h" | 22 #include "remoting/protocol/session_config.h" |
29 | 23 |
30 using remoting::protocol::ConnectionToClient; | 24 using remoting::protocol::ConnectionToClient; |
31 using remoting::protocol::InputStub; | 25 using remoting::protocol::InputStub; |
32 | 26 |
33 namespace remoting { | 27 namespace remoting { |
(...skipping 24 matching lines...) Expand all Loading... | |
58 | 52 |
59 // Don't use initial delay unless the last request was an error. | 53 // Don't use initial delay unless the last request was an error. |
60 false, | 54 false, |
61 }; | 55 }; |
62 | 56 |
63 } // namespace | 57 } // namespace |
64 | 58 |
65 ChromotingHost::ChromotingHost( | 59 ChromotingHost::ChromotingHost( |
66 ChromotingHostContext* context, | 60 ChromotingHostContext* context, |
67 SignalStrategy* signal_strategy, | 61 SignalStrategy* signal_strategy, |
68 DesktopEnvironment* environment, | 62 DesktopEnvironmentFactory* desktop_environment_factory, |
69 scoped_ptr<protocol::SessionManager> session_manager) | 63 scoped_ptr<protocol::SessionManager> session_manager) |
70 : context_(context), | 64 : context_(context), |
71 desktop_environment_(environment), | 65 desktop_environment_factory_(desktop_environment_factory), |
72 session_manager_(session_manager.Pass()), | 66 session_manager_(session_manager.Pass()), |
73 signal_strategy_(signal_strategy), | 67 signal_strategy_(signal_strategy), |
74 stopping_recorders_(0), | |
75 state_(kInitial), | 68 state_(kInitial), |
76 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), | 69 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), |
77 login_backoff_(&kDefaultBackoffPolicy), | 70 login_backoff_(&kDefaultBackoffPolicy), |
78 authenticating_client_(false), | 71 authenticating_client_(false), |
79 reject_authenticating_client_(false) { | 72 reject_authenticating_client_(false) { |
80 DCHECK(context_); | 73 DCHECK(context_); |
81 DCHECK(signal_strategy); | 74 DCHECK(signal_strategy); |
82 DCHECK(desktop_environment_); | |
83 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 75 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
84 | 76 |
85 if (!desktop_environment_->audio_capturer()) { | 77 #if !defined(OS_WIN) |
simonmorris
2012/09/04 18:47:21
Add a HasAudioCapturer() method to DesktopEnvironm
Wez
2012/09/04 20:29:30
Good idea, although I'd suggest SupportsAudioCaptu
alexeypa (please no reviews)
2012/09/05 22:53:29
Done.
alexeypa (please no reviews)
2012/09/05 22:53:29
Done.
| |
86 // Disable audio by replacing our list of supported audio configurations | 78 // Disable audio by replacing our list of supported audio configurations |
87 // with the NONE config. | 79 // with the NONE config. |
88 protocol_config_->mutable_audio_configs()->clear(); | 80 // AudioCapturer::Create() is currently only implemented for Windows. |
89 protocol_config_->mutable_audio_configs()->push_back( | 81 protocol_config_->mutable_audio_configs()->clear(); |
90 protocol::ChannelConfig(protocol::ChannelConfig::TRANSPORT_NONE, | 82 protocol_config_->mutable_audio_configs()->push_back( |
91 protocol::kDefaultStreamVersion, | 83 protocol::ChannelConfig(protocol::ChannelConfig::TRANSPORT_NONE, |
92 protocol::ChannelConfig::CODEC_VERBATIM)); | 84 protocol::kDefaultStreamVersion, |
93 } | 85 protocol::ChannelConfig::CODEC_VERBATIM)); |
86 #endif // !defined(OS_WIN) | |
94 } | 87 } |
95 | 88 |
96 ChromotingHost::~ChromotingHost() { | 89 ChromotingHost::~ChromotingHost() { |
97 DCHECK(clients_.empty()); | 90 DCHECK(clients_.empty()); |
98 } | 91 } |
99 | 92 |
100 void ChromotingHost::Start() { | 93 void ChromotingHost::Start() { |
101 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 94 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
102 | 95 |
103 LOG(INFO) << "Starting host"; | 96 LOG(INFO) << "Starting host"; |
(...skipping 28 matching lines...) Expand all Loading... | |
132 // We are already stopping. Just save the task. | 125 // We are already stopping. Just save the task. |
133 if (!shutdown_task.is_null()) | 126 if (!shutdown_task.is_null()) |
134 shutdown_tasks_.push_back(shutdown_task); | 127 shutdown_tasks_.push_back(shutdown_task); |
135 break; | 128 break; |
136 | 129 |
137 case kStarted: | 130 case kStarted: |
138 if (!shutdown_task.is_null()) | 131 if (!shutdown_task.is_null()) |
139 shutdown_tasks_.push_back(shutdown_task); | 132 shutdown_tasks_.push_back(shutdown_task); |
140 state_ = kStopping; | 133 state_ = kStopping; |
141 | 134 |
142 // Disconnect all of the clients, implicitly stopping the ScreenRecorder. | 135 // Disconnect all of the clients. |
143 while (!clients_.empty()) { | 136 while (!clients_.empty()) { |
144 clients_.front()->Disconnect(); | 137 clients_.front()->Disconnect(); |
145 } | 138 } |
146 DCHECK(!recorder_.get()); | |
147 DCHECK(!audio_scheduler_.get()); | |
148 | 139 |
149 // Destroy session manager. | 140 // Destroy session manager. |
150 session_manager_.reset(); | 141 session_manager_.reset(); |
151 | 142 |
152 if (!stopping_recorders_) | 143 // Run the remaining shutdown tasks. |
153 ShutdownFinish(); | 144 ShutdownFinish(); |
154 break; | 145 break; |
155 } | 146 } |
156 } | 147 } |
157 | 148 |
158 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { | 149 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { |
159 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 150 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
160 status_observers_.AddObserver(observer); | 151 status_observers_.AddObserver(observer); |
161 } | 152 } |
162 | 153 |
163 void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { | 154 void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { |
(...skipping 30 matching lines...) Expand all Loading... | |
194 ClientList clients_copy(clients_); | 185 ClientList clients_copy(clients_); |
195 for (ClientList::const_iterator other_client = clients_copy.begin(); | 186 for (ClientList::const_iterator other_client = clients_copy.begin(); |
196 other_client != clients_copy.end(); ++other_client) { | 187 other_client != clients_copy.end(); ++other_client) { |
197 if ((*other_client) != client) { | 188 if ((*other_client) != client) { |
198 (*other_client)->Disconnect(); | 189 (*other_client)->Disconnect(); |
199 } | 190 } |
200 } | 191 } |
201 | 192 |
202 // Disconnects above must have destroyed all other clients and |recorder_|. | 193 // Disconnects above must have destroyed all other clients and |recorder_|. |
203 DCHECK_EQ(clients_.size(), 1U); | 194 DCHECK_EQ(clients_.size(), 1U); |
204 DCHECK(!recorder_.get()); | |
205 DCHECK(!audio_scheduler_.get()); | |
206 | 195 |
207 // Notify observers that there is at least one authenticated client. | 196 // Notify observers that there is at least one authenticated client. |
208 const std::string& jid = client->client_jid(); | 197 const std::string& jid = client->client_jid(); |
209 | 198 |
210 reject_authenticating_client_ = false; | 199 reject_authenticating_client_ = false; |
211 | 200 |
212 authenticating_client_ = true; | 201 authenticating_client_ = true; |
213 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 202 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
214 OnClientAuthenticated(jid)); | 203 OnClientAuthenticated(jid)); |
215 authenticating_client_ = false; | 204 authenticating_client_ = false; |
216 | 205 |
217 if (reject_authenticating_client_) { | 206 if (reject_authenticating_client_) { |
218 client->Disconnect(); | 207 client->Disconnect(); |
219 } | 208 } |
220 } | 209 } |
221 | 210 |
222 void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { | 211 void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { |
223 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 212 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
224 | 213 |
225 // Then we create a ScreenRecorder passing the message loops that | 214 // Notify observers. |
226 // it should run on. | |
227 Encoder* encoder = CreateEncoder(client->connection()->session()->config()); | |
228 | |
229 recorder_ = new ScreenRecorder(context_->capture_task_runner(), | |
230 context_->encode_task_runner(), | |
231 context_->network_task_runner(), | |
232 desktop_environment_->capturer(), | |
233 encoder); | |
234 if (client->connection()->session()->config().is_audio_enabled()) { | |
235 scoped_ptr<AudioEncoder> audio_encoder = | |
236 CreateAudioEncoder(client->connection()->session()->config()); | |
237 audio_scheduler_ = new AudioScheduler( | |
238 context_->capture_task_runner(), | |
239 context_->network_task_runner(), | |
240 desktop_environment_->audio_capturer(), | |
241 audio_encoder.Pass(), | |
242 client->connection()->audio_stub()); | |
243 } | |
244 | |
245 // Immediately add the connection and start the session. | |
246 recorder_->AddConnection(client->connection()); | |
247 recorder_->Start(); | |
248 desktop_environment_->OnSessionStarted(client->CreateClipboardProxy()); | |
249 | |
250 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 215 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
251 OnClientConnected(client->client_jid())); | 216 OnClientConnected(client->client_jid())); |
252 } | 217 } |
253 | 218 |
254 void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { | 219 void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { |
255 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 220 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
256 | 221 |
257 // Notify observers. | 222 // Notify observers. |
258 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 223 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
259 OnAccessDenied(client->client_jid())); | 224 OnAccessDenied(client->client_jid())); |
260 } | 225 } |
261 | 226 |
262 void ChromotingHost::OnSessionClosed(ClientSession* client) { | 227 void ChromotingHost::OnSessionClosed(ClientSession* client) { |
263 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 228 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
264 | 229 |
265 scoped_ptr<ClientSession> client_destroyer(client); | |
266 | |
267 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); | 230 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); |
268 CHECK(it != clients_.end()); | 231 CHECK(it != clients_.end()); |
269 clients_.erase(it); | 232 clients_.erase(it); |
270 | 233 |
271 if (recorder_.get()) { | |
272 recorder_->RemoveConnection(client->connection()); | |
273 } | |
274 | |
275 if (audio_scheduler_.get()) { | |
276 audio_scheduler_->OnClientDisconnected(); | |
277 StopAudioScheduler(); | |
278 } | |
279 | |
280 if (client->is_authenticated()) { | 234 if (client->is_authenticated()) { |
281 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 235 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
282 OnClientDisconnected(client->client_jid())); | 236 OnClientDisconnected(client->client_jid())); |
237 } | |
283 | 238 |
284 // TODO(sergeyu): This teardown logic belongs to ClientSession | 239 client->StopAndDelete(); |
285 // class. It should start/stop screen recorder or tell the host | |
286 // when to do it. | |
287 if (recorder_.get()) { | |
288 // Currently we don't allow more than one simultaneous connection, | |
289 // so we need to shutdown recorder when a client disconnects. | |
290 StopScreenRecorder(); | |
291 } | |
292 desktop_environment_->OnSessionFinished(); | |
293 } | |
294 } | 240 } |
295 | 241 |
296 void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, | 242 void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, |
297 int64 sequence_number) { | 243 int64 sequence_number) { |
298 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 244 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
299 if (recorder_.get()) | |
300 recorder_->UpdateSequenceNumber(sequence_number); | |
301 } | 245 } |
302 | 246 |
303 void ChromotingHost::OnSessionRouteChange( | 247 void ChromotingHost::OnSessionRouteChange( |
304 ClientSession* session, | 248 ClientSession* session, |
305 const std::string& channel_name, | 249 const std::string& channel_name, |
306 const protocol::TransportRoute& route) { | 250 const protocol::TransportRoute& route) { |
307 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 251 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
308 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 252 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
309 OnClientRouteChange(session->client_jid(), channel_name, | 253 OnClientRouteChange(session->client_jid(), channel_name, |
310 route)); | 254 route)); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
344 *response = protocol::SessionManager::INCOMPATIBLE; | 288 *response = protocol::SessionManager::INCOMPATIBLE; |
345 return; | 289 return; |
346 } | 290 } |
347 | 291 |
348 session->set_config(config); | 292 session->set_config(config); |
349 | 293 |
350 *response = protocol::SessionManager::ACCEPT; | 294 *response = protocol::SessionManager::ACCEPT; |
351 | 295 |
352 LOG(INFO) << "Client connected: " << session->jid(); | 296 LOG(INFO) << "Client connected: " << session->jid(); |
353 | 297 |
298 // Create the desktop integration implementation for the client to use. | |
299 scoped_ptr<DesktopEnvironment> desktop_environment = | |
300 desktop_environment_factory_->Create(context_); | |
301 | |
354 // Create a client object. | 302 // Create a client object. |
355 scoped_ptr<protocol::ConnectionToClient> connection( | 303 scoped_ptr<protocol::ConnectionToClient> connection( |
356 new protocol::ConnectionToClient(session)); | 304 new protocol::ConnectionToClient(session)); |
357 ClientSession* client = new ClientSession( | 305 ClientSession* client = new ClientSession( |
358 this, | 306 this, |
307 context_->capture_task_runner(), | |
308 context_->encode_task_runner(), | |
309 context_->network_task_runner(), | |
359 connection.Pass(), | 310 connection.Pass(), |
360 desktop_environment_->event_executor(), | 311 desktop_environment.Pass(), |
361 desktop_environment_->event_executor(), | |
362 desktop_environment_->capturer(), | |
363 max_session_duration_); | 312 max_session_duration_); |
364 clients_.push_back(client); | 313 clients_.push_back(client); |
365 } | 314 } |
366 | 315 |
367 void ChromotingHost::set_protocol_config( | 316 void ChromotingHost::set_protocol_config( |
368 protocol::CandidateSessionConfig* config) { | 317 protocol::CandidateSessionConfig* config) { |
369 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 318 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
370 DCHECK(config); | 319 DCHECK(config); |
371 DCHECK_EQ(state_, kInitial); | 320 DCHECK_EQ(state_, kInitial); |
372 protocol_config_.reset(config); | 321 protocol_config_.reset(config); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
413 } | 362 } |
414 } | 363 } |
415 | 364 |
416 void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) { | 365 void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) { |
417 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 366 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
418 DCHECK_EQ(state_, kInitial); | 367 DCHECK_EQ(state_, kInitial); |
419 | 368 |
420 ui_strings_ = ui_strings; | 369 ui_strings_ = ui_strings; |
421 } | 370 } |
422 | 371 |
423 // TODO(sergeyu): Move this to SessionManager? | |
424 // static | |
425 Encoder* ChromotingHost::CreateEncoder(const protocol::SessionConfig& config) { | |
426 const protocol::ChannelConfig& video_config = config.video_config(); | |
427 | |
428 if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { | |
429 return EncoderRowBased::CreateVerbatimEncoder(); | |
430 } else if (video_config.codec == protocol::ChannelConfig::CODEC_ZIP) { | |
431 return EncoderRowBased::CreateZlibEncoder(); | |
432 } else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { | |
433 return new remoting::EncoderVp8(); | |
434 } | |
435 | |
436 return NULL; | |
437 } | |
438 | |
439 // static | |
440 scoped_ptr<AudioEncoder> ChromotingHost::CreateAudioEncoder( | |
441 const protocol::SessionConfig& config) { | |
442 const protocol::ChannelConfig& audio_config = config.audio_config(); | |
443 | |
444 if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { | |
445 return scoped_ptr<AudioEncoder>(new AudioEncoderVerbatim()); | |
446 } | |
447 | |
448 NOTIMPLEMENTED(); | |
449 return scoped_ptr<AudioEncoder>(NULL); | |
450 } | |
451 | |
452 void ChromotingHost::StopScreenRecorder() { | |
453 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | |
454 DCHECK(recorder_.get()); | |
455 | |
456 ++stopping_recorders_; | |
457 scoped_refptr<ScreenRecorder> recorder = recorder_; | |
458 recorder_ = NULL; | |
459 recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
460 } | |
461 | |
462 void ChromotingHost::StopAudioScheduler() { | |
463 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | |
464 DCHECK(audio_scheduler_.get()); | |
465 | |
466 ++stopping_recorders_; | |
467 scoped_refptr<AudioScheduler> recorder = audio_scheduler_; | |
468 audio_scheduler_ = NULL; | |
469 recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
470 } | |
471 | |
472 void ChromotingHost::OnRecorderStopped() { | |
473 if (!context_->network_task_runner()->BelongsToCurrentThread()) { | |
474 context_->network_task_runner()->PostTask( | |
475 FROM_HERE, base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
476 return; | |
477 } | |
478 | |
479 --stopping_recorders_; | |
480 DCHECK_GE(stopping_recorders_, 0); | |
481 | |
482 if (!stopping_recorders_ && state_ == kStopping) | |
483 ShutdownFinish(); | |
484 } | |
485 | |
486 void ChromotingHost::ShutdownFinish() { | 372 void ChromotingHost::ShutdownFinish() { |
487 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 373 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
488 DCHECK(!stopping_recorders_); | |
489 | 374 |
490 state_ = kStopped; | 375 state_ = kStopped; |
491 | 376 |
492 // Keep reference to |this|, so that we don't get destroyed while | 377 // Keep reference to |this|, so that we don't get destroyed while |
493 // sending notifications. | 378 // sending notifications. |
494 scoped_refptr<ChromotingHost> self(this); | 379 scoped_refptr<ChromotingHost> self(this); |
495 | 380 |
496 // Notify observers. | 381 // Notify observers. |
497 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 382 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
498 OnShutdown()); | 383 OnShutdown()); |
499 | 384 |
500 for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin(); | 385 for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin(); |
501 it != shutdown_tasks_.end(); ++it) { | 386 it != shutdown_tasks_.end(); ++it) { |
502 it->Run(); | 387 it->Run(); |
503 } | 388 } |
504 shutdown_tasks_.clear(); | 389 shutdown_tasks_.clear(); |
505 } | 390 } |
506 | 391 |
507 } // namespace remoting | 392 } // namespace remoting |
OLD | NEW |