blob: 1d47872aa82c7b4c89d5f8daf82572aad9da7152 [file] [log] [blame]
// 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_client.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "net/base/io_buffer.h"
#include "remoting/codec/audio_encoder.h"
#include "remoting/codec/audio_encoder_opus.h"
#include "remoting/codec/video_encoder.h"
#include "remoting/protocol/audio_pump.h"
#include "remoting/protocol/audio_source.h"
#include "remoting/protocol/audio_writer.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/host_control_dispatcher.h"
#include "remoting/protocol/host_event_dispatcher.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/host_video_dispatcher.h"
#include "remoting/protocol/input_stub.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/protocol/video_frame_pump.h"
namespace remoting {
namespace protocol {
namespace {
std::unique_ptr<AudioEncoder> CreateAudioEncoder(
const protocol::SessionConfig& config) {
#if defined(OS_IOS)
// TODO(nicholss): iOS should not use Opus. This is to prevent us from
// depending on //media. In the future we will use webrtc for conneciton
// and this will be a non-issue.
return nullptr;
#else
const protocol::ChannelConfig& audio_config = config.audio_config();
if (audio_config.codec == protocol::ChannelConfig::CODEC_OPUS) {
return base::WrapUnique(new AudioEncoderOpus());
}
#endif
NOTREACHED();
return nullptr;
}
} // namespace
IceConnectionToClient::IceConnectionToClient(
std::unique_ptr<protocol::Session> session,
scoped_refptr<TransportContext> transport_context,
scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner)
: event_handler_(nullptr),
session_(std::move(session)),
video_encode_task_runner_(std::move(video_encode_task_runner)),
audio_task_runner_(std::move(audio_task_runner)),
transport_(transport_context, this),
control_dispatcher_(new HostControlDispatcher()),
event_dispatcher_(new HostEventDispatcher()),
video_dispatcher_(new HostVideoDispatcher()) {
session_->SetEventHandler(this);
session_->SetTransport(&transport_);
}
IceConnectionToClient::~IceConnectionToClient() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void IceConnectionToClient::SetEventHandler(
ConnectionToClient::EventHandler* event_handler) {
DCHECK(thread_checker_.CalledOnValidThread());
event_handler_ = event_handler;
}
protocol::Session* IceConnectionToClient::session() {
DCHECK(thread_checker_.CalledOnValidThread());
return session_.get();
}
void IceConnectionToClient::Disconnect(ErrorCode error) {
DCHECK(thread_checker_.CalledOnValidThread());
// This should trigger OnConnectionClosed() event and this object
// may be destroyed as the result.
session_->Close(error);
}
std::unique_ptr<VideoStream> IceConnectionToClient::StartVideoStream(
std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer) {
DCHECK(thread_checker_.CalledOnValidThread());
std::unique_ptr<VideoEncoder> video_encoder =
VideoEncoder::Create(session_->config());
std::unique_ptr<VideoFramePump> pump(
new VideoFramePump(video_encode_task_runner_, std::move(desktop_capturer),
std::move(video_encoder), video_dispatcher_.get()));
pump->SetEventTimestampsSource(event_dispatcher_->event_timestamps_source());
video_dispatcher_->set_video_feedback_stub(pump->video_feedback_stub());
return std::move(pump);
}
std::unique_ptr<AudioStream> IceConnectionToClient::StartAudioStream(
std::unique_ptr<AudioSource> audio_source) {
DCHECK(thread_checker_.CalledOnValidThread());
// Audio channel is disabled.
if (!audio_writer_)
return nullptr;
std::unique_ptr<AudioEncoder> audio_encoder =
CreateAudioEncoder(session_->config());
return base::WrapUnique(
new AudioPump(audio_task_runner_, std::move(audio_source),
std::move(audio_encoder), audio_writer_.get()));
}
// Return pointer to ClientStub.
ClientStub* IceConnectionToClient::client_stub() {
DCHECK(thread_checker_.CalledOnValidThread());
return control_dispatcher_.get();
}
void IceConnectionToClient::set_clipboard_stub(
protocol::ClipboardStub* clipboard_stub) {
DCHECK(thread_checker_.CalledOnValidThread());
control_dispatcher_->set_clipboard_stub(clipboard_stub);
}
void IceConnectionToClient::set_host_stub(protocol::HostStub* host_stub) {
DCHECK(thread_checker_.CalledOnValidThread());
control_dispatcher_->set_host_stub(host_stub);
}
void IceConnectionToClient::set_input_stub(protocol::InputStub* input_stub) {
DCHECK(thread_checker_.CalledOnValidThread());
event_dispatcher_->set_input_stub(input_stub);
}
PeerConnectionControls* IceConnectionToClient::peer_connection_controls() {
return nullptr;
}
WebrtcEventLogData* IceConnectionToClient::rtc_event_log() {
return nullptr;
}
void IceConnectionToClient::OnSessionStateChange(Session::State state) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(event_handler_);
switch (state) {
case Session::INITIALIZING:
case Session::CONNECTING:
case Session::ACCEPTING:
case Session::ACCEPTED:
// Don't care about these events.
break;
case Session::AUTHENTICATING:
event_handler_->OnConnectionAuthenticating();
break;
case Session::AUTHENTICATED:
// Initialize channels.
control_dispatcher_->Init(transport_.GetMultiplexedChannelFactory(),
this);
event_dispatcher_->Init(transport_.GetMultiplexedChannelFactory(), this);
video_dispatcher_->Init(transport_.GetChannelFactory(), this);
audio_writer_ = AudioWriter::Create(session_->config());
if (audio_writer_)
audio_writer_->Init(transport_.GetMultiplexedChannelFactory(), this);
// Notify the handler after initializing the channels, so that
// ClientSession can get a client clipboard stub.
event_handler_->OnConnectionAuthenticated();
break;
case Session::CLOSED:
case Session::FAILED:
CloseChannels();
event_handler_->OnConnectionClosed(
state == Session::FAILED ? session_->error() : OK);
break;
}
}
void IceConnectionToClient::OnIceTransportRouteChange(
const std::string& channel_name,
const TransportRoute& route) {
event_handler_->OnRouteChange(channel_name, route);
}
void IceConnectionToClient::OnIceTransportError(ErrorCode error) {
DCHECK(thread_checker_.CalledOnValidThread());
Disconnect(error);
}
void IceConnectionToClient::OnChannelInitialized(
ChannelDispatcherBase* channel_dispatcher) {
DCHECK(thread_checker_.CalledOnValidThread());
NotifyIfChannelsReady();
}
void IceConnectionToClient::OnChannelClosed(
ChannelDispatcherBase* channel_dispatcher) {
DCHECK(thread_checker_.CalledOnValidThread());
Disconnect(OK);
}
void IceConnectionToClient::NotifyIfChannelsReady() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!control_dispatcher_ || !control_dispatcher_->is_connected())
return;
if (!event_dispatcher_ || !event_dispatcher_->is_connected())
return;
if (!video_dispatcher_ || !video_dispatcher_->is_connected())
return;
if ((!audio_writer_ || !audio_writer_->is_connected()) &&
session_->config().is_audio_enabled()) {
return;
}
event_handler_->OnConnectionChannelsConnected();
event_handler_->CreateMediaStreams();
}
void IceConnectionToClient::CloseChannels() {
control_dispatcher_.reset();
event_dispatcher_.reset();
video_dispatcher_.reset();
audio_writer_.reset();
}
} // namespace protocol
} // namespace remoting