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