| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/protocol/ice_connection_to_host.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "remoting/base/constants.h" |
| #include "remoting/protocol/audio_decode_scheduler.h" |
| #include "remoting/protocol/audio_reader.h" |
| #include "remoting/protocol/audio_stub.h" |
| #include "remoting/protocol/auth_util.h" |
| #include "remoting/protocol/client_control_dispatcher.h" |
| #include "remoting/protocol/client_event_dispatcher.h" |
| #include "remoting/protocol/client_stub.h" |
| #include "remoting/protocol/client_video_dispatcher.h" |
| #include "remoting/protocol/clipboard_stub.h" |
| #include "remoting/protocol/errors.h" |
| #include "remoting/protocol/ice_transport.h" |
| #include "remoting/protocol/transport_context.h" |
| #include "remoting/protocol/video_renderer.h" |
| |
| namespace remoting { |
| namespace protocol { |
| |
| IceConnectionToHost::IceConnectionToHost() = default; |
| IceConnectionToHost::~IceConnectionToHost() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void IceConnectionToHost::Connect( |
| std::unique_ptr<Session> session, |
| scoped_refptr<TransportContext> transport_context, |
| HostEventCallback* event_callback) { |
| DCHECK(client_stub_); |
| DCHECK(clipboard_stub_); |
| DCHECK(video_renderer_); |
| |
| transport_.reset(new IceTransport(transport_context, this)); |
| |
| session_ = std::move(session); |
| session_->SetEventHandler(this); |
| session_->SetTransport(transport_.get()); |
| |
| event_callback_ = event_callback; |
| |
| SetState(CONNECTING, OK); |
| } |
| |
| const SessionConfig& IceConnectionToHost::config() { |
| return session_->config(); |
| } |
| |
| ClipboardStub* IceConnectionToHost::clipboard_forwarder() { |
| return &clipboard_forwarder_; |
| } |
| |
| HostStub* IceConnectionToHost::host_stub() { |
| // TODO(wez): Add a HostFilter class, equivalent to input filter. |
| return control_dispatcher_.get(); |
| } |
| |
| InputStub* IceConnectionToHost::input_stub() { |
| return &event_forwarder_; |
| } |
| |
| void IceConnectionToHost::set_client_stub(ClientStub* client_stub) { |
| client_stub_ = client_stub; |
| } |
| |
| void IceConnectionToHost::set_clipboard_stub(ClipboardStub* clipboard_stub) { |
| clipboard_stub_ = clipboard_stub; |
| } |
| |
| void IceConnectionToHost::set_video_renderer(VideoRenderer* video_renderer) { |
| DCHECK(video_renderer); |
| DCHECK(!monitored_video_stub_); |
| video_renderer_ = video_renderer; |
| } |
| |
| void IceConnectionToHost::InitializeAudio( |
| scoped_refptr<base::SingleThreadTaskRunner> audio_decode_task_runner, |
| base::WeakPtr<AudioStub> audio_stub) { |
| audio_decode_scheduler_.reset( |
| new AudioDecodeScheduler(audio_decode_task_runner, audio_stub)); |
| } |
| |
| void IceConnectionToHost::OnSessionStateChange(Session::State state) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(event_callback_); |
| |
| switch (state) { |
| case Session::INITIALIZING: |
| case Session::CONNECTING: |
| case Session::ACCEPTING: |
| case Session::ACCEPTED: |
| case Session::AUTHENTICATING: |
| // Don't care about these events. |
| break; |
| |
| case Session::AUTHENTICATED: |
| SetState(AUTHENTICATED, OK); |
| |
| // Setup control channel. |
| control_dispatcher_.reset(new ClientControlDispatcher()); |
| control_dispatcher_->Init(transport_->GetMultiplexedChannelFactory(), |
| this); |
| control_dispatcher_->set_client_stub(client_stub_); |
| control_dispatcher_->set_clipboard_stub(clipboard_stub_); |
| |
| // Setup event channel. |
| event_dispatcher_.reset(new ClientEventDispatcher()); |
| event_dispatcher_->Init(transport_->GetMultiplexedChannelFactory(), this); |
| |
| // Configure video pipeline. |
| video_renderer_->OnSessionConfig(session_->config()); |
| monitored_video_stub_.reset(new MonitoredVideoStub( |
| video_renderer_->GetVideoStub(), |
| base::TimeDelta::FromSeconds( |
| MonitoredVideoStub::kConnectivityCheckDelaySeconds), |
| base::Bind(&IceConnectionToHost::OnVideoChannelStatus, |
| base::Unretained(this)))); |
| video_dispatcher_.reset( |
| new ClientVideoDispatcher(monitored_video_stub_.get(), client_stub_)); |
| video_dispatcher_->Init(transport_->GetChannelFactory(), this); |
| |
| // Configure audio pipeline if necessary. |
| if (session_->config().is_audio_enabled()) { |
| audio_reader_.reset(new AudioReader(audio_decode_scheduler_.get())); |
| audio_reader_->Init(transport_->GetMultiplexedChannelFactory(), this); |
| audio_decode_scheduler_->Initialize(session_->config()); |
| } |
| break; |
| |
| case Session::CLOSED: |
| CloseChannels(); |
| SetState(CLOSED, OK); |
| break; |
| |
| case Session::FAILED: |
| // If we were connected then treat signaling timeout error as if |
| // the connection was closed by the peer. |
| // |
| // TODO(sergeyu): This logic belongs to the webapp, but we |
| // currently don't expose this error code to the webapp, and it |
| // would be hard to add it because client plugin and webapp |
| // versions may not be in sync. It should be easy to do after we |
| // are finished moving the client plugin to NaCl. |
| CloseChannels(); |
| if (state_ == CONNECTED && session_->error() == SIGNALING_TIMEOUT) { |
| SetState(CLOSED, OK); |
| } else { |
| SetState(FAILED, session_->error()); |
| } |
| break; |
| } |
| } |
| |
| void IceConnectionToHost::OnIceTransportRouteChange( |
| const std::string& channel_name, |
| const TransportRoute& route) { |
| event_callback_->OnRouteChanged(channel_name, route); |
| } |
| |
| void IceConnectionToHost::OnIceTransportError(ErrorCode error) { |
| session_->Close(error); |
| } |
| |
| void IceConnectionToHost::OnChannelInitialized( |
| ChannelDispatcherBase* channel_dispatcher) { |
| NotifyIfChannelsReady(); |
| } |
| |
| void IceConnectionToHost::OnChannelClosed( |
| ChannelDispatcherBase* channel_dispatcher) { |
| session_->Close(OK); |
| } |
| |
| void IceConnectionToHost::OnVideoChannelStatus(bool active) { |
| event_callback_->OnConnectionReady(active); |
| } |
| |
| ConnectionToHost::State IceConnectionToHost::state() const { |
| return state_; |
| } |
| |
| void IceConnectionToHost::NotifyIfChannelsReady() { |
| if (!control_dispatcher_.get() || !control_dispatcher_->is_connected()) |
| return; |
| if (!event_dispatcher_.get() || !event_dispatcher_->is_connected()) |
| return; |
| if (!video_dispatcher_.get() || !video_dispatcher_->is_connected()) |
| return; |
| if ((!audio_reader_.get() || !audio_reader_->is_connected()) && |
| session_->config().is_audio_enabled()) { |
| return; |
| } |
| if (state_ != AUTHENTICATED) |
| return; |
| |
| // Start forwarding clipboard and input events. |
| clipboard_forwarder_.set_clipboard_stub(control_dispatcher_.get()); |
| event_forwarder_.set_input_stub(event_dispatcher_.get()); |
| SetState(CONNECTED, OK); |
| } |
| |
| void IceConnectionToHost::CloseChannels() { |
| control_dispatcher_.reset(); |
| event_dispatcher_.reset(); |
| clipboard_forwarder_.set_clipboard_stub(nullptr); |
| event_forwarder_.set_input_stub(nullptr); |
| video_dispatcher_.reset(); |
| audio_reader_.reset(); |
| } |
| |
| void IceConnectionToHost::SetState(State state, ErrorCode error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // |error| should be specified only when |state| is set to FAILED. |
| DCHECK(state == FAILED || error == OK); |
| |
| if (state != state_) { |
| state_ = state; |
| error_ = error; |
| event_callback_->OnConnectionState(state_, error_); |
| } |
| } |
| |
| } // namespace protocol |
| } // namespace remoting |