Index: remoting/host/chromoting_host.cc |
=================================================================== |
--- remoting/host/chromoting_host.cc (revision 156297) |
+++ remoting/host/chromoting_host.cc (working copy) |
@@ -10,11 +10,19 @@ |
#include "base/message_loop_proxy.h" |
#include "build/build_config.h" |
#include "remoting/base/constants.h" |
+#include "remoting/codec/audio_encoder.h" |
+#include "remoting/codec/audio_encoder_speex.h" |
+#include "remoting/codec/audio_encoder_verbatim.h" |
+#include "remoting/codec/video_encoder.h" |
+#include "remoting/codec/video_encoder_row_based.h" |
+#include "remoting/codec/video_encoder_vp8.h" |
+#include "remoting/host/audio_capturer.h" |
+#include "remoting/host/audio_scheduler.h" |
#include "remoting/host/chromoting_host_context.h" |
#include "remoting/host/desktop_environment.h" |
-#include "remoting/host/desktop_environment_factory.h" |
#include "remoting/host/event_executor.h" |
#include "remoting/host/host_config.h" |
+#include "remoting/host/screen_recorder.h" |
#include "remoting/protocol/connection_to_client.h" |
#include "remoting/protocol/client_stub.h" |
#include "remoting/protocol/host_stub.h" |
@@ -59,13 +67,13 @@ |
ChromotingHost::ChromotingHost( |
ChromotingHostContext* context, |
SignalStrategy* signal_strategy, |
- DesktopEnvironmentFactory* desktop_environment_factory, |
+ DesktopEnvironment* environment, |
scoped_ptr<protocol::SessionManager> session_manager) |
: context_(context), |
- desktop_environment_factory_(desktop_environment_factory), |
+ desktop_environment_(environment), |
session_manager_(session_manager.Pass()), |
signal_strategy_(signal_strategy), |
- clients_count_(0), |
+ stopping_recorders_(0), |
state_(kInitial), |
protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), |
login_backoff_(&kDefaultBackoffPolicy), |
@@ -73,9 +81,10 @@ |
reject_authenticating_client_(false) { |
DCHECK(context_); |
DCHECK(signal_strategy); |
+ DCHECK(desktop_environment_); |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
- if (!desktop_environment_factory_->SupportsAudioCapture()) { |
+ if (!AudioCapturer::IsSupported()) { |
// Disable audio by replacing our list of supported audio configurations |
// with the NONE config. |
protocol_config_->mutable_audio_configs()->clear(); |
@@ -133,15 +142,18 @@ |
shutdown_tasks_.push_back(shutdown_task); |
state_ = kStopping; |
- // Disconnect all of the clients. |
+ // Disconnect all of the clients, implicitly stopping the ScreenRecorder. |
while (!clients_.empty()) { |
clients_.front()->Disconnect(); |
} |
+ DCHECK(!recorder_.get()); |
+ DCHECK(!audio_scheduler_.get()); |
- // Run the remaining shutdown tasks. |
- if (state_ == kStopping && !clients_count_) |
+ // Destroy session manager. |
+ session_manager_.reset(); |
+ |
+ if (!stopping_recorders_) |
ShutdownFinish(); |
- |
break; |
} |
} |
@@ -185,13 +197,15 @@ |
ClientList clients_copy(clients_); |
for (ClientList::const_iterator other_client = clients_copy.begin(); |
other_client != clients_copy.end(); ++other_client) { |
- if (other_client->get() != client) { |
+ if ((*other_client) != client) { |
(*other_client)->Disconnect(); |
} |
} |
// Disconnects above must have destroyed all other clients and |recorder_|. |
DCHECK_EQ(clients_.size(), 1U); |
+ DCHECK(!recorder_.get()); |
+ DCHECK(!audio_scheduler_.get()); |
// Notify observers that there is at least one authenticated client. |
const std::string& jid = client->client_jid(); |
@@ -211,7 +225,32 @@ |
void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
- // Notify observers. |
+ // Then we create a ScreenRecorder passing the message loops that |
+ // it should run on. |
+ VideoEncoder* video_encoder = |
+ CreateVideoEncoder(client->connection()->session()->config()); |
+ |
+ recorder_ = new ScreenRecorder(context_->capture_task_runner(), |
+ context_->encode_task_runner(), |
+ context_->network_task_runner(), |
+ desktop_environment_->capturer(), |
+ video_encoder); |
+ if (client->connection()->session()->config().is_audio_enabled()) { |
+ scoped_ptr<AudioEncoder> audio_encoder = |
+ CreateAudioEncoder(client->connection()->session()->config()); |
+ audio_scheduler_ = new AudioScheduler( |
+ context_->audio_task_runner(), |
+ context_->network_task_runner(), |
+ desktop_environment_->audio_capturer(), |
+ audio_encoder.Pass(), |
+ client->connection()->audio_stub()); |
+ } |
+ |
+ // Immediately add the connection and start the session. |
+ recorder_->AddConnection(client->connection()); |
+ recorder_->Start(); |
+ desktop_environment_->OnSessionStarted(client->CreateClipboardProxy()); |
+ |
FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
OnClientConnected(client->client_jid())); |
} |
@@ -227,26 +266,42 @@ |
void ChromotingHost::OnSessionClosed(ClientSession* client) { |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
- ClientList::iterator it = clients_.begin(); |
- for (; it != clients_.end(); ++it) { |
- if (it->get() == client) { |
- break; |
- } |
- } |
+ scoped_ptr<ClientSession> client_destroyer(client); |
+ |
+ ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); |
CHECK(it != clients_.end()); |
+ clients_.erase(it); |
+ if (recorder_.get()) { |
+ recorder_->RemoveConnection(client->connection()); |
+ } |
+ |
+ if (audio_scheduler_.get()) { |
+ audio_scheduler_->OnClientDisconnected(); |
+ StopAudioScheduler(); |
+ } |
+ |
if (client->is_authenticated()) { |
FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
OnClientDisconnected(client->client_jid())); |
+ |
+ // TODO(sergeyu): This teardown logic belongs to ClientSession |
+ // class. It should start/stop screen recorder or tell the host |
+ // when to do it. |
+ if (recorder_.get()) { |
+ // Currently we don't allow more than one simultaneous connection, |
+ // so we need to shutdown recorder when a client disconnects. |
+ StopScreenRecorder(); |
+ } |
+ desktop_environment_->OnSessionFinished(); |
} |
- |
- client->Stop(base::Bind(&ChromotingHost::OnClientStopped, this)); |
- clients_.erase(it); |
} |
void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, |
int64 sequence_number) { |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
+ if (recorder_.get()) |
+ recorder_->UpdateSequenceNumber(sequence_number); |
} |
void ChromotingHost::OnSessionRouteChange( |
@@ -300,23 +355,17 @@ |
LOG(INFO) << "Client connected: " << session->jid(); |
- // Create the desktop integration implementation for the client to use. |
- scoped_ptr<DesktopEnvironment> desktop_environment = |
- desktop_environment_factory_->Create(context_); |
- |
// Create a client object. |
scoped_ptr<protocol::ConnectionToClient> connection( |
new protocol::ConnectionToClient(session)); |
- scoped_refptr<ClientSession> client = new ClientSession( |
+ ClientSession* client = new ClientSession( |
this, |
- context_->capture_task_runner(), |
- context_->encode_task_runner(), |
- context_->network_task_runner(), |
connection.Pass(), |
- desktop_environment.Pass(), |
+ desktop_environment_->event_executor(), |
+ desktop_environment_->event_executor(), |
+ desktop_environment_->capturer(), |
max_session_duration_); |
clients_.push_back(client); |
- clients_count_++; |
} |
void ChromotingHost::set_protocol_config( |
@@ -375,21 +424,76 @@ |
ui_strings_ = ui_strings; |
} |
-void ChromotingHost::OnClientStopped() { |
+// TODO(sergeyu): Move this to SessionManager? |
+// static |
+VideoEncoder* ChromotingHost::CreateVideoEncoder( |
+ const protocol::SessionConfig& config) { |
+ const protocol::ChannelConfig& video_config = config.video_config(); |
+ |
+ if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { |
+ return VideoEncoderRowBased::CreateVerbatimEncoder(); |
+ } else if (video_config.codec == protocol::ChannelConfig::CODEC_ZIP) { |
+ return VideoEncoderRowBased::CreateZlibEncoder(); |
+ } else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { |
+ return new remoting::VideoEncoderVp8(); |
+ } |
+ |
+ return NULL; |
+} |
+ |
+// static |
+scoped_ptr<AudioEncoder> ChromotingHost::CreateAudioEncoder( |
+ const protocol::SessionConfig& config) { |
+ const protocol::ChannelConfig& audio_config = config.audio_config(); |
+ |
+ if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { |
+ return scoped_ptr<AudioEncoder>(new AudioEncoderVerbatim()); |
+ } else if (audio_config.codec == protocol::ChannelConfig::CODEC_SPEEX) { |
+ return scoped_ptr<AudioEncoder>(new AudioEncoderSpeex()); |
+ } |
+ |
+ NOTIMPLEMENTED(); |
+ return scoped_ptr<AudioEncoder>(NULL); |
+} |
+ |
+void ChromotingHost::StopScreenRecorder() { |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
+ DCHECK(recorder_.get()); |
- --clients_count_; |
- if (state_ == kStopping && !clients_count_) |
+ ++stopping_recorders_; |
+ scoped_refptr<ScreenRecorder> recorder = recorder_; |
+ recorder_ = NULL; |
+ recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); |
+} |
+ |
+void ChromotingHost::StopAudioScheduler() { |
+ DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
+ DCHECK(audio_scheduler_.get()); |
+ |
+ ++stopping_recorders_; |
+ scoped_refptr<AudioScheduler> recorder = audio_scheduler_; |
+ audio_scheduler_ = NULL; |
+ recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); |
+} |
+ |
+void ChromotingHost::OnRecorderStopped() { |
+ if (!context_->network_task_runner()->BelongsToCurrentThread()) { |
+ context_->network_task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&ChromotingHost::OnRecorderStopped, this)); |
+ return; |
+ } |
+ |
+ --stopping_recorders_; |
+ DCHECK_GE(stopping_recorders_, 0); |
+ |
+ if (!stopping_recorders_ && state_ == kStopping) |
ShutdownFinish(); |
} |
void ChromotingHost::ShutdownFinish() { |
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
- DCHECK_EQ(state_, kStopping); |
+ DCHECK(!stopping_recorders_); |
- // Destroy session manager. |
- session_manager_.reset(); |
- |
state_ = kStopped; |
// Keep reference to |this|, so that we don't get destroyed while |