diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-24 21:08:35 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-24 21:08:35 +0000 |
commit | 8922bdbce7e19faf46604138b6bf0e5276f1db3c (patch) | |
tree | 74cc718706f2dcee7cd4eff96f507dbcb2db472b | |
parent | 476f1c53ebdd8a5ddcadef5c34123f5492887176 (diff) | |
download | chromium_src-8922bdbce7e19faf46604138b6bf0e5276f1db3c.zip chromium_src-8922bdbce7e19faf46604138b6bf0e5276f1db3c.tar.gz chromium_src-8922bdbce7e19faf46604138b6bf0e5276f1db3c.tar.bz2 |
Implemented chromotocol configuration negotiation.
Renamed "events" to "event".
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/3988002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63693 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 954 insertions, 144 deletions
diff --git a/remoting/client/jingle_host_connection.cc b/remoting/client/jingle_host_connection.cc index c70e2c6..b4a4ecf 100644 --- a/remoting/client/jingle_host_connection.cc +++ b/remoting/client/jingle_host_connection.cc @@ -43,7 +43,7 @@ void JingleHostConnection::Disconnect() { return; } - events_writer_.Close(); + event_writer_.Close(); video_reader_.Close(); if (connection_) { @@ -73,9 +73,14 @@ void JingleHostConnection::InitConnection() { NewCallback(this, &JingleHostConnection::OnNewChromotocolConnection)); chromotocol_server_ = chromotocol_server; + CandidateChromotocolConfig* candidate_config = + CandidateChromotocolConfig::CreateDefault(); + // TODO(sergeyu): Set resolution in the |candidate_config| to the desired + // resolution. + // Initialize |connection_|. connection_ = chromotocol_server_->Connect( - host_jid_, + host_jid_, candidate_config, NewCallback(this, &JingleHostConnection::OnConnectionStateChange)); } @@ -100,7 +105,7 @@ void JingleHostConnection::OnServerClosed() { void JingleHostConnection::SendEvent(const ChromotingClientMessage& msg) { // This drops the message if we are not connected yet. - events_writer_.SendMessage(msg); + event_writer_.SendMessage(msg); } // JingleClient::Callback interface. @@ -120,10 +125,11 @@ void JingleHostConnection::OnStateChange(JingleClient* client, } void JingleHostConnection::OnNewChromotocolConnection( - ChromotingConnection* connection, bool* accept) { + ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response) { DCHECK_EQ(message_loop(), MessageLoop::current()); // Client always rejects incoming connections. - *accept = false; + *response = ChromotingServer::DECLINE; } void JingleHostConnection::OnConnectionStateChange( @@ -142,7 +148,7 @@ void JingleHostConnection::OnConnectionStateChange( case ChromotingConnection::CONNECTED: // Initialize reader and writer. - events_writer_.Init(connection_->GetEventsChannel()); + event_writer_.Init(connection_->GetEventChannel()); video_reader_.Init( connection_->GetVideoChannel(), NewCallback(this, &JingleHostConnection::OnVideoMessage)); diff --git a/remoting/client/jingle_host_connection.h b/remoting/client/jingle_host_connection.h index 1701b5b..a72d8a9 100644 --- a/remoting/client/jingle_host_connection.h +++ b/remoting/client/jingle_host_connection.h @@ -55,7 +55,8 @@ class JingleHostConnection : public HostConnection, // Callback for ChromotingServer. void OnNewChromotocolConnection( - ChromotingConnection* connection, bool* accept); + ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response); // Callback for ChromotingConnection. void OnConnectionStateChange(ChromotingConnection::State state); @@ -83,7 +84,7 @@ class JingleHostConnection : public HostConnection, scoped_refptr<ChromotingServer> chromotocol_server_; scoped_refptr<ChromotingConnection> connection_; - EventsStreamWriter events_writer_; + EventStreamWriter event_writer_; VideoStreamReader video_reader_; HostEventCallback* event_callback_; diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index 7676eda..c9acbe7 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -215,14 +215,14 @@ void ChromotingHost::OnStateChange(JingleClient* jingle_client, VLOG(1) << "Host connected as " << jingle_client->GetFullJid(); // Create and start |chromotocol_server_|. - chromotocol_server_ = + JingleChromotingServer* server = new JingleChromotingServer(context_->jingle_thread()->message_loop()); // TODO(ajwong): Make this a command switch when we're more stable. - chromotocol_server_->set_allow_local_ips(true); - chromotocol_server_->Init( - jingle_client->GetFullJid(), - jingle_client->session_manager(), - NewCallback(this, &ChromotingHost::OnNewClientConnection)); + server->set_allow_local_ips(true); + server->Init(jingle_client->GetFullJid(), + jingle_client->session_manager(), + NewCallback(this, &ChromotingHost::OnNewClientConnection)); + chromotocol_server_ = server; // Start heartbeating. heartbeat_sender_->Start(); @@ -239,21 +239,40 @@ void ChromotingHost::OnStateChange(JingleClient* jingle_client, } void ChromotingHost::OnNewClientConnection( - ChromotingConnection* connection, bool* accept) { + ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response) { AutoLock auto_lock(lock_); // TODO(hclam): Allow multiple clients to connect to the host. if (client_.get() || state_ != kStarted) { - *accept = false; + *response = ChromotingServer::DECLINE; return; } // Check that the user has access to the host. if (!access_verifier_.VerifyPermissions(connection->jid())) { - *accept = false; + *response = ChromotingServer::DECLINE; return; } - *accept = true; + scoped_ptr<CandidateChromotocolConfig> local_config( + CandidateChromotocolConfig::CreateDefault()); + local_config->SetInitialResolution( + ScreenResolution(capturer_->width(), capturer_->height())); + // TODO(sergeyu): Respect resolution requested by the client if supported. + ChromotocolConfig* config = + local_config->Select(connection->candidate_config(), + true /* force_host_resolution */); + + if (!config) { + LOG(WARNING) << "Rejecting connection from " << connection->jid() + << " because no compartible configuration has been found."; + *response = ChromotingServer::INCOMPATIBLE; + return; + } + + connection->set_config(config); + + *response = ChromotingServer::ACCEPT; VLOG(1) << "Client connected: " << connection->jid(); diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h index cf74d23..bc61bc6 100644 --- a/remoting/host/chromoting_host.h +++ b/remoting/host/chromoting_host.h @@ -16,6 +16,7 @@ #include "remoting/host/heartbeat_sender.h" #include "remoting/jingle_glue/jingle_client.h" #include "remoting/jingle_glue/jingle_thread.h" +#include "remoting/protocol/chromoting_server.h" class Task; @@ -27,7 +28,6 @@ class Encoder; class EventExecutor; class MutableHostConfig; class SessionManager; -class JingleChromotingServer; // A class to implement the functionality of a host process. // @@ -95,7 +95,8 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>, virtual void OnStateChange(JingleClient* client, JingleClient::State state); // Callback for ChromotingServer. - void OnNewClientConnection(ChromotingConnection* connection, bool* accept); + void OnNewClientConnection(ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response); private: enum State { @@ -131,7 +132,7 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>, // receive connection requests from chromoting client. scoped_refptr<JingleClient> jingle_client_; - scoped_refptr<JingleChromotingServer> chromotocol_server_; + scoped_refptr<ChromotingServer> chromotocol_server_; // Objects that takes care of sending heartbeats to the chromoting bot. scoped_refptr<HeartbeatSender> heartbeat_sender_; diff --git a/remoting/host/client_connection.cc b/remoting/host/client_connection.cc index 57554f4..4efbddf 100644 --- a/remoting/host/client_connection.cc +++ b/remoting/host/client_connection.cc @@ -81,8 +81,8 @@ ClientConnection::ClientConnection() {} void ClientConnection::OnConnectionStateChange( ChromotingConnection::State state) { if (state == ChromotingConnection::CONNECTED) { - events_reader_.Init( - connection_->GetEventsChannel(), + event_reader_.Init( + connection_->GetEventChannel(), NewCallback(this, &ClientConnection::OnMessageReceived)); video_writer_.Init(connection_->GetVideoChannel()); } diff --git a/remoting/host/client_connection.h b/remoting/host/client_connection.h index daac392..69a3309 100644 --- a/remoting/host/client_connection.h +++ b/remoting/host/client_connection.h @@ -107,7 +107,7 @@ class ClientConnection : public base::RefCountedThreadSafe<ClientConnection> { // The libjingle channel used to send and receive data from the remote client. scoped_refptr<ChromotingConnection> connection_; - EventsStreamReader events_reader_; + EventStreamReader event_reader_; VideoStreamWriter video_writer_; // The message loop that this object runs on. diff --git a/remoting/protocol/chromoting_connection.h b/remoting/protocol/chromoting_connection.h index 397b633..0f54104 100644 --- a/remoting/protocol/chromoting_connection.h +++ b/remoting/protocol/chromoting_connection.h @@ -8,6 +8,7 @@ #include <string> #include "base/callback.h" +#include "remoting/protocol/chromotocol_config.h" class MessageLoop; class Task; @@ -22,6 +23,7 @@ namespace remoting { // Provides access to the connection channels, but doesn't depend on the // protocol used for each channel. // TODO(sergeyu): Remove refcounting? +// TODO(sergeyu): Rename to ChromotocolConnection. class ChromotingConnection : public base::RefCountedThreadSafe<ChromotingConnection> { public: @@ -42,8 +44,8 @@ class ChromotingConnection // Reliable PseudoTCP channels for this connection. // TODO(sergeyu): Remove VideoChannel, and use RTP channels instead. // TODO(sergeyu): Make it possible to create/destroy new channels on-fly? + virtual net::Socket* GetEventChannel() = 0; virtual net::Socket* GetVideoChannel() = 0; - virtual net::Socket* GetEventsChannel() = 0; // Unreliable channels for this connection. virtual net::Socket* GetVideoRtpChannel() = 0; @@ -56,6 +58,20 @@ class ChromotingConnection // connection. virtual MessageLoop* message_loop() = 0; + // Configuration of the protocol requested by the client. + // Returned pointer is valid until connection is closed. + virtual const CandidateChromotocolConfig* candidate_config() = 0; + + // Protocol configuration. Can be called only after session has been accepted. + // Returned pointer is valid until connection is closed. + virtual const ChromotocolConfig* config() = 0; + + // Set protocol configuration for an incoming session. Must be called + // on the host before the connection is accepted, from + // ChromotingServer::NewConnectionCallback. Ownership of |config| is given + // to the connection. + virtual void set_config(const ChromotocolConfig* config) = 0; + // Closes connection. Callbacks are guaranteed not to be called after // |closed_task| is executed. virtual void Close(Task* closed_task) = 0; diff --git a/remoting/protocol/chromoting_server.h b/remoting/protocol/chromoting_server.h index 5b93534..c378c8b 100644 --- a/remoting/protocol/chromoting_server.h +++ b/remoting/protocol/chromoting_server.h @@ -16,13 +16,31 @@ class Task; namespace remoting { // Generic interface for Chromoting server. +// TODO(sergeyu): Rename to ChromotocolServer. +// TODO(sergeyu): Add more documentation on how this interface is used. class ChromotingServer : public base::RefCountedThreadSafe<ChromotingServer> { public: - typedef Callback2<ChromotingConnection*, bool*>::Type NewConnectionCallback; - - // Initializes connection to the host |jid|. + enum NewConnectionResponse { + ACCEPT, + INCOMPATIBLE, + DECLINE, + }; + + // NewConnectionCallback is called when a new connection is received. If the + // callback decides to accept the connection it should set the second + // argument to ACCEPT. Otherwise it should set it to DECLINE, or + // INCOMPATIBLE. INCOMPATIBLE indicates that the session has incompartible + // configuration, and cannot be accepted. + // If the callback accepts connection then it must also set configuration + // for the new connection using ChromotingConnection::set_config(). + typedef Callback2<ChromotingConnection*, NewConnectionResponse*>::Type + NewConnectionCallback; + + // Initializes connection to the host |jid|. Ownership of the + // |chromotocol_config| is passed to the new connection. virtual scoped_refptr<ChromotingConnection> Connect( const std::string& jid, + CandidateChromotocolConfig* chromotocol_config, ChromotingConnection::StateChangeCallback* state_change_callback) = 0; // Close connection. |close_task| is executed after the session client diff --git a/remoting/protocol/chromotocol_config.cc b/remoting/protocol/chromotocol_config.cc new file mode 100644 index 0000000..943fdbb --- /dev/null +++ b/remoting/protocol/chromotocol_config.cc @@ -0,0 +1,243 @@ +// Copyright (c) 2010 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/chromotocol_config.h" + +#include <algorithm> + +namespace remoting { + +const int kDefaultStreamVersion = 1; + +namespace { +const int kDefaultWidth = 800; +const int kDefaultHeight = 600; +} // namespace + +ChannelConfig::ChannelConfig() { + Reset(); +} + +ChannelConfig::ChannelConfig(TransportType transport, int version, Codec codec) + : transport(transport), + version(version), + codec(codec) { +} + +bool ChannelConfig::operator==(const ChannelConfig& b) const { + return transport == b.transport && version == b.version && codec == b.codec; +} + +void ChannelConfig::Reset() { + transport = TRANSPORT_STREAM; + version = kDefaultStreamVersion; + codec = CODEC_UNDEFINED; +} + +ScreenResolution::ScreenResolution() + : width(kDefaultWidth), + height(kDefaultHeight) { +} + +ScreenResolution::ScreenResolution(int width, int height) + : width(width), + height(height) { +} + +bool ScreenResolution::IsValid() const { + return width > 0 && height > 0; +} + +ChromotocolConfig::ChromotocolConfig() { } + +ChromotocolConfig::ChromotocolConfig(const ChromotocolConfig& config) + : control_config_(config.control_config_), + event_config_(config.event_config_), + video_config_(config.video_config_), + initial_resolution_(config.initial_resolution_) { +} + +ChromotocolConfig::~ChromotocolConfig() { } + +void ChromotocolConfig::SetControlConfig(const ChannelConfig& control_config) { + control_config_ = control_config; +} +void ChromotocolConfig::SetEventConfig(const ChannelConfig& event_config) { + event_config_ = event_config; +} +void ChromotocolConfig::SetVideoConfig(const ChannelConfig& video_config) { + video_config_ = video_config; +} +void ChromotocolConfig::SetInitialResolution(const ScreenResolution& resolution) { + initial_resolution_ = resolution; +} + +ChromotocolConfig* ChromotocolConfig::Clone() const { + return new ChromotocolConfig(*this); +} + +// static +ChromotocolConfig* ChromotocolConfig::CreateDefault() { + ChromotocolConfig* result = new ChromotocolConfig(); + result->SetControlConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED)); + result->SetEventConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED)); + result->SetVideoConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_ZIP)); + return result; +} + +CandidateChromotocolConfig::CandidateChromotocolConfig() { } + +CandidateChromotocolConfig::CandidateChromotocolConfig( + const CandidateChromotocolConfig& config) + : control_configs_(config.control_configs_), + event_configs_(config.event_configs_), + video_configs_(config.video_configs_), + initial_resolution_(config.initial_resolution_) { +} + +CandidateChromotocolConfig::~CandidateChromotocolConfig() { } + +void CandidateChromotocolConfig::AddControlConfig( + const ChannelConfig& control_config) { + control_configs_.push_back(control_config); +} + +void CandidateChromotocolConfig::AddEventConfig( + const ChannelConfig& event_config) { + event_configs_.push_back(event_config); +} + +void CandidateChromotocolConfig::AddVideoConfig( + const ChannelConfig& video_config) { + video_configs_.push_back(video_config); +} + +void CandidateChromotocolConfig::SetInitialResolution( + const ScreenResolution& resolution) { + initial_resolution_ = resolution; +} + +ChromotocolConfig* CandidateChromotocolConfig::Select( + const CandidateChromotocolConfig* client_config, + bool force_host_resolution) { + ChannelConfig control_config; + ChannelConfig event_config; + ChannelConfig video_config; + if (!SelectCommonChannelConfig( + control_configs_, client_config->control_configs_, &control_config) || + !SelectCommonChannelConfig( + event_configs_, client_config->event_configs_, &event_config) || + !SelectCommonChannelConfig( + video_configs_, client_config->video_configs_, &video_config)) { + return NULL; + } + + ChromotocolConfig* result = ChromotocolConfig::CreateDefault(); + result->SetControlConfig(control_config); + result->SetEventConfig(event_config); + result->SetVideoConfig(video_config); + + if (force_host_resolution) { + result->SetInitialResolution(initial_resolution()); + } else { + result->SetInitialResolution(client_config->initial_resolution()); + } + + return result; +} + +bool CandidateChromotocolConfig::IsSupported( + const ChromotocolConfig* config) const { + return + IsChannelConfigSupported(control_configs_, config->control_config()) && + IsChannelConfigSupported(event_configs_, config->event_config()) && + IsChannelConfigSupported(video_configs_, config->video_config()) && + config->initial_resolution().IsValid(); +} + +ChromotocolConfig* CandidateChromotocolConfig::GetFinalConfig() const { + if (control_configs_.size() != 1 || + event_configs_.size() != 1 || + video_configs_.size() != 1) { + return NULL; + } + + ChromotocolConfig* result = ChromotocolConfig::CreateDefault(); + result->SetControlConfig(control_configs_.front()); + result->SetEventConfig(event_configs_.front()); + result->SetVideoConfig(video_configs_.front()); + result->SetInitialResolution(initial_resolution_); + return result; +} + +// static +bool CandidateChromotocolConfig::SelectCommonChannelConfig( + const std::vector<ChannelConfig>& host_configs, + const std::vector<ChannelConfig>& client_configs, + ChannelConfig* config) { + // Usually each of these vectors will contain just several elements, + // so iterating over all of them is not a problem. + std::vector<ChannelConfig>::const_iterator it; + for (it = client_configs.begin(); it != client_configs.end(); ++it) { + if (IsChannelConfigSupported(host_configs, *it)) { + *config = *it; + return true; + } + } + return false; +} + +// static +bool CandidateChromotocolConfig::IsChannelConfigSupported( + const std::vector<ChannelConfig>& vector, + const ChannelConfig& value) { + return std::find(vector.begin(), vector.end(), value) != vector.end(); +} + +CandidateChromotocolConfig* CandidateChromotocolConfig::Clone() const { + return new CandidateChromotocolConfig(*this); +} + +// static +CandidateChromotocolConfig* CandidateChromotocolConfig::CreateEmpty() { + return new CandidateChromotocolConfig(); +} + +// static +CandidateChromotocolConfig* CandidateChromotocolConfig::CreateFrom( + const ChromotocolConfig* config) { + CandidateChromotocolConfig* result = CreateEmpty(); + result->AddControlConfig(config->control_config()); + result->AddEventConfig(config->event_config()); + result->AddVideoConfig(config->video_config()); + result->SetInitialResolution(config->initial_resolution()); + return result; +} + +// static +CandidateChromotocolConfig* CandidateChromotocolConfig::CreateDefault() { + CandidateChromotocolConfig* result = CreateEmpty(); + result->AddControlConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED)); + result->AddEventConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED)); + + result->AddVideoConfig(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + kDefaultStreamVersion, + ChannelConfig::CODEC_ZIP)); + result->AddVideoConfig(ChannelConfig(ChannelConfig::TRANSPORT_SRTP, + kDefaultStreamVersion, + ChannelConfig::CODEC_VP8)); + return result; +} + +} // namespace remoting diff --git a/remoting/protocol/chromotocol_config.h b/remoting/protocol/chromotocol_config.h new file mode 100644 index 0000000..bd0bcbc --- /dev/null +++ b/remoting/protocol/chromotocol_config.h @@ -0,0 +1,161 @@ +// Copyright (c) 2010 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. + +#ifndef REMOTING_PROTOCOL_CHROMOTOCOL_CONFIG_H_ +#define REMOTING_PROTOCOL_CHROMOTOCOL_CONFIG_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +namespace remoting { + +extern const int kDefaultStreamVersion; + +// Struct for configuration parameters of a single channel. +struct ChannelConfig { + enum TransportType { + TRANSPORT_STREAM, + TRANSPORT_DATAGRAM, + TRANSPORT_SRTP, + TRANSPORT_RTP_DTLS, + }; + + enum Codec { + CODEC_UNDEFINED, // Used for event and control channels. + CODEC_VP8, + CODEC_ZIP, + }; + + ChannelConfig(); + ChannelConfig(TransportType transport, int version, Codec codec); + + // operator== is overloaded so that std::find() works with + // std::vector<ChannelConfig>. + bool operator==(const ChannelConfig& b) const; + + void Reset(); + + TransportType transport; + int version; + Codec codec; +}; + +struct ScreenResolution { + ScreenResolution(); + ScreenResolution(int width, int height); + + bool IsValid() const; + + int width; + int height; +}; + +// ChromotocolConfig is used by ChromotingConnection to store negotiated +// chromotocol configuration. +class ChromotocolConfig { + public: + ~ChromotocolConfig(); + + const ChannelConfig& control_config() const { return control_config_; } + const ChannelConfig& event_config() const { return event_config_; } + const ChannelConfig& video_config() const { return video_config_; } + const ScreenResolution& initial_resolution() const { + return initial_resolution_; + } + + void SetControlConfig(const ChannelConfig& control_config); + void SetEventConfig(const ChannelConfig& event_config); + void SetVideoConfig(const ChannelConfig& video_config); + void SetInitialResolution(const ScreenResolution& initial_resolution); + + ChromotocolConfig* Clone() const; + + static ChromotocolConfig* CreateDefault(); + + private: + ChromotocolConfig(); + explicit ChromotocolConfig(const ChromotocolConfig& config); + ChromotocolConfig& operator=(const ChromotocolConfig& b); + + ChannelConfig control_config_; + ChannelConfig event_config_; + ChannelConfig video_config_; + ScreenResolution initial_resolution_; +}; + +// Defines session description that is sent from client to the host in the +// session-initiate message. It is different from the regular ChromotocolConfig +// because it allows one to specify multiple configurations for each channel. +class CandidateChromotocolConfig { + public: + ~CandidateChromotocolConfig(); + + const std::vector<ChannelConfig>& control_configs() const { + return control_configs_; + } + + const std::vector<ChannelConfig>& event_configs() const { + return event_configs_; + } + + const std::vector<ChannelConfig>& video_configs() const { + return video_configs_; + } + + const ScreenResolution& initial_resolution() const { + return initial_resolution_; + } + + void AddControlConfig(const ChannelConfig& control_config); + void AddEventConfig(const ChannelConfig& event_config); + void AddVideoConfig(const ChannelConfig& video_config); + void SetInitialResolution(const ScreenResolution& initial_resolution); + + // Selects session configuration that is supported by both participants. + // NULL is returned if such configuration doesn't exist. When selecting + // channel configuration priority is given to the configs listed first + // in |client_config|. + ChromotocolConfig* Select(const CandidateChromotocolConfig* client_config, + bool force_host_resolution); + + // Returns true if |config| is supported. + bool IsSupported(const ChromotocolConfig* config) const; + + // Extracts final protocol configuration. Must be used for the description + // received in the session-accept stanza. If the selection is ambiguous + // (e.g. there is more than one configuration for one of the channel) + // or undefined (e.g. no configurations for a channel) then NULL is returned. + ChromotocolConfig* GetFinalConfig() const; + + CandidateChromotocolConfig* Clone() const; + + static CandidateChromotocolConfig* CreateEmpty(); + static CandidateChromotocolConfig* CreateFrom( + const ChromotocolConfig* config); + static CandidateChromotocolConfig* CreateDefault(); + + private: + CandidateChromotocolConfig(); + explicit CandidateChromotocolConfig(const CandidateChromotocolConfig& config); + CandidateChromotocolConfig& operator=(const CandidateChromotocolConfig& b); + + static bool SelectCommonChannelConfig( + const std::vector<ChannelConfig>& host_configs_, + const std::vector<ChannelConfig>& client_configs_, + ChannelConfig* config); + static bool IsChannelConfigSupported(const std::vector<ChannelConfig>& vector, + const ChannelConfig& value); + + std::vector<ChannelConfig> control_configs_; + std::vector<ChannelConfig> event_configs_; + std::vector<ChannelConfig> video_configs_; + + ScreenResolution initial_resolution_; +}; + +} // namespace remoting + +#endif // REMOTING_PROTOCOL_CHROMOTOCOL_CONFIG_H_ diff --git a/remoting/protocol/fake_connection.cc b/remoting/protocol/fake_connection.cc index cffacb5..9f40e65 100644 --- a/remoting/protocol/fake_connection.cc +++ b/remoting/protocol/fake_connection.cc @@ -70,7 +70,9 @@ bool FakeSocket::SetSendBufferSize(int32 size) { } FakeChromotingConnection::FakeChromotingConnection() - : message_loop_(NULL), + : candidate_config_(CandidateChromotocolConfig::CreateDefault()), + config_(ChromotocolConfig::CreateDefault()), + message_loop_(NULL), jid_(kTestJid) { } @@ -84,8 +86,8 @@ void FakeChromotingConnection::SetStateChangeCallback( FakeSocket* FakeChromotingConnection::GetVideoChannel() { return &video_channel_; } -FakeSocket* FakeChromotingConnection::GetEventsChannel() { - return &events_channel_; +FakeSocket* FakeChromotingConnection::GetEventChannel() { + return &event_channel_; } FakeSocket* FakeChromotingConnection::GetVideoRtpChannel() { @@ -103,6 +105,19 @@ MessageLoop* FakeChromotingConnection::message_loop() { return message_loop_; } +const CandidateChromotocolConfig* FakeChromotingConnection::candidate_config() { + return candidate_config_.get(); +} + +const ChromotocolConfig* FakeChromotingConnection::config() { + CHECK(config_.get()); + return config_.get(); +} + +void FakeChromotingConnection::set_config(const ChromotocolConfig* config) { + config_.reset(config); +} + void FakeChromotingConnection::Close(Task* closed_task) { closed_ = true; closed_task->Run(); diff --git a/remoting/protocol/fake_connection.h b/remoting/protocol/fake_connection.h index cabce4a..33b120e 100644 --- a/remoting/protocol/fake_connection.h +++ b/remoting/protocol/fake_connection.h @@ -68,7 +68,7 @@ class FakeChromotingConnection : public ChromotingConnection { virtual void SetStateChangeCallback(StateChangeCallback* callback); virtual FakeSocket* GetVideoChannel(); - virtual FakeSocket* GetEventsChannel(); + virtual FakeSocket* GetEventChannel(); virtual FakeSocket* GetVideoRtpChannel(); virtual FakeSocket* GetVideoRtcpChannel(); @@ -76,13 +76,19 @@ class FakeChromotingConnection : public ChromotingConnection { virtual const std::string& jid(); virtual MessageLoop* message_loop(); + virtual const CandidateChromotocolConfig* candidate_config(); + virtual const ChromotocolConfig* config(); + virtual void set_config(const ChromotocolConfig* config); + virtual void Close(Task* closed_task); public: scoped_ptr<StateChangeCallback> callback_; + scoped_ptr<const CandidateChromotocolConfig> candidate_config_; + scoped_ptr<const ChromotocolConfig> config_; MessageLoop* message_loop_; FakeSocket video_channel_; - FakeSocket events_channel_; + FakeSocket event_channel_; FakeSocket video_rtp_channel_; FakeSocket video_rtcp_channel_; std::string jid_; diff --git a/remoting/protocol/host_message_dispatcher.h b/remoting/protocol/host_message_dispatcher.h index 3d1ac1b..aab7fbf 100644 --- a/remoting/protocol/host_message_dispatcher.h +++ b/remoting/protocol/host_message_dispatcher.h @@ -17,7 +17,7 @@ namespace remoting { class ChromotingClientMessage; class ChromotingConnection; -class EventsStreamReader; +class EventStreamReader; class HostControlMessageHandler; class HostEventMessageHandler; @@ -25,7 +25,7 @@ class HostEventMessageHandler; // ChromotingConnection. It dispatches messages to the corresponding // handler. // -// Internally it contains an EventsStreamReader that decodes data on +// Internally it contains an EventStreamReader that decodes data on // communications channels into protocol buffer messages. // EventStreamReader is registered with ChromotingConnection given to it. // @@ -54,13 +54,13 @@ class HostMessageDispatcher { // Message loop to dispatch the messages. scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; - // EventsStreamReader that runs on the control channel. It runs a loop + // EventStreamReader that runs on the control channel. It runs a loop // that parses data on the channel and then delegate the message to this // class. - scoped_ptr<EventsStreamReader> control_channel_reader_; + scoped_ptr<EventStreamReader> control_channel_reader_; - // EventsStreamReader that runs on the event channel. - scoped_ptr<EventsStreamReader> event_channel_reader_; + // EventStreamReader that runs on the event channel. + scoped_ptr<EventStreamReader> event_channel_reader_; // Event handlers for control channel and event channel respectively. // Method calls to these objects are made on the message loop given. diff --git a/remoting/protocol/jingle_chromoting_connection.cc b/remoting/protocol/jingle_chromoting_connection.cc index 2412627..10d8709 100644 --- a/remoting/protocol/jingle_chromoting_connection.cc +++ b/remoting/protocol/jingle_chromoting_connection.cc @@ -24,7 +24,7 @@ namespace { const char kVideoChannelName[] = "video"; const char kVideoRtpChannelName[] = "videortp"; const char kVideoRtcpChannelName[] = "videortcp"; -const char kEventsChannelName[] = "events"; +const char kEventChannelName[] = "event"; } // namespace JingleChromotingConnection::JingleChromotingConnection( @@ -33,7 +33,7 @@ JingleChromotingConnection::JingleChromotingConnection( state_(INITIALIZING), closed_(false), session_(NULL), - events_channel_(NULL), + event_channel_(NULL), video_channel_(NULL) { } @@ -79,9 +79,9 @@ net::Socket* JingleChromotingConnection::GetVideoChannel() { return video_channel_adapter_.get(); } -net::Socket* JingleChromotingConnection::GetEventsChannel() { +net::Socket* JingleChromotingConnection::GetEventChannel() { DCHECK_EQ(server_->message_loop(), MessageLoop::current()); - return events_channel_adapter_.get(); + return event_channel_adapter_.get(); } net::Socket* JingleChromotingConnection::GetVideoRtpChannel() { @@ -104,6 +104,30 @@ MessageLoop* JingleChromotingConnection::message_loop() { return server_->message_loop(); } +const CandidateChromotocolConfig* +JingleChromotingConnection::candidate_config() { + DCHECK(candidate_config_.get()); + return candidate_config_.get(); +} + +void JingleChromotingConnection::set_candidate_config( + const CandidateChromotocolConfig* candidate_config) { + DCHECK(!candidate_config_.get()); + DCHECK(candidate_config); + candidate_config_.reset(candidate_config); +} + +const ChromotocolConfig* JingleChromotingConnection::config() { + DCHECK(config_.get()); + return config_.get(); +} + +void JingleChromotingConnection::set_config(const ChromotocolConfig* config) { + DCHECK(!config_.get()); + DCHECK(config); + config_.reset(config); +} + void JingleChromotingConnection::Close(Task* closed_task) { if (MessageLoop::current() != server_->message_loop()) { server_->message_loop()->PostTask( @@ -113,12 +137,12 @@ void JingleChromotingConnection::Close(Task* closed_task) { } if (!closed_) { - if (events_channel_adapter_.get()) - events_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED); + if (event_channel_adapter_.get()) + event_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED); - if (events_channel_) { - events_channel_->OnSessionTerminate(session_); - events_channel_ = NULL; + if (event_channel_) { + event_channel_->OnSessionTerminate(session_); + event_channel_ = NULL; } if (video_channel_adapter_.get()) @@ -160,8 +184,11 @@ void JingleChromotingConnection::OnSessionState( break; case Session::STATE_SENTACCEPT: + OnAccept(false); + break; + case Session::STATE_RECEIVEDACCEPT: - OnAccept(); + OnAccept(true); break; case Session::STATE_RECEIVEDTERMINATE: @@ -185,11 +212,28 @@ void JingleChromotingConnection::OnInitiate(bool incoming) { SetState(CONNECTING); } -void JingleChromotingConnection::OnAccept() { +void JingleChromotingConnection::OnAccept(bool incoming) { const cricket::ContentInfo* content = session_->remote_description()->FirstContentByType( kChromotingXmlNamespace); - ASSERT(content != NULL); + CHECK(content); + + // Set config for outgoing connections. + if (incoming) { + const ChromotingContentDescription* content_description = + static_cast<const ChromotingContentDescription*>(content->description); + ChromotocolConfig* config = content_description->config()->GetFinalConfig(); + + // Terminate the session if the config we received is invalid. + if (!config || !candidate_config()->IsSupported(config)) { + LOG(ERROR) << "Terminating outgoing session after an " + "invalid session description has been received."; + session_->Terminate(); + return; + } + + set_config(config); + } // Create video RTP channels. video_rtp_channel_.reset(new TransportChannelSocketAdapter( @@ -197,12 +241,12 @@ void JingleChromotingConnection::OnAccept() { video_rtcp_channel_.reset(new TransportChannelSocketAdapter( session_->CreateChannel(content->name, kVideoRtcpChannelName))); - // Create events channel. - events_channel_ = + // Create event channel. + event_channel_ = new PseudoTcpChannel(talk_base::Thread::Current(), session_); - events_channel_->Connect(content->name, kEventsChannelName); - events_channel_adapter_.reset(new StreamSocketAdapter( - events_channel_->GetStream())); + event_channel_->Connect(content->name, kEventChannelName); + event_channel_adapter_.reset(new StreamSocketAdapter( + event_channel_->GetStream())); // Create video channel. // TODO(sergeyu): Remove video channel when we are ready to switch to RTP. @@ -216,11 +260,11 @@ void JingleChromotingConnection::OnAccept() { } void JingleChromotingConnection::OnTerminate() { - if (events_channel_adapter_.get()) - events_channel_adapter_->Close(net::ERR_CONNECTION_ABORTED); - if (events_channel_) { - events_channel_->OnSessionTerminate(session_); - events_channel_ = NULL; + if (event_channel_adapter_.get()) + event_channel_adapter_->Close(net::ERR_CONNECTION_ABORTED); + if (event_channel_) { + event_channel_->OnSessionTerminate(session_); + event_channel_ = NULL; } if (video_channel_adapter_.get()) diff --git a/remoting/protocol/jingle_chromoting_connection.h b/remoting/protocol/jingle_chromoting_connection.h index cc54182..3279a20 100644 --- a/remoting/protocol/jingle_chromoting_connection.h +++ b/remoting/protocol/jingle_chromoting_connection.h @@ -34,16 +34,21 @@ class JingleChromotingConnection : public ChromotingConnection, explicit JingleChromotingConnection(JingleChromotingServer* client); // ChromotingConnection interface. - virtual void SetStateChangeCallback(StateChangeCallback* callback) ; + virtual void SetStateChangeCallback(StateChangeCallback* callback); virtual net::Socket* GetVideoChannel(); - virtual net::Socket* GetEventsChannel(); + virtual net::Socket* GetEventChannel(); virtual net::Socket* GetVideoRtpChannel(); virtual net::Socket* GetVideoRtcpChannel(); virtual const std::string& jid(); virtual MessageLoop* message_loop(); + virtual const CandidateChromotocolConfig* candidate_config(); + virtual const ChromotocolConfig* config(); + + virtual void set_config(const ChromotocolConfig* config); + virtual void Close(Task* closed_task); protected: @@ -53,6 +58,7 @@ class JingleChromotingConnection : public ChromotingConnection, friend class JingleChromotingServer; // Called by JingleChromotingServer. + void set_candidate_config(const CandidateChromotocolConfig* candidate_config); void Init(cricket::Session* session); bool HasSession(cricket::Session* session); cricket::Session* ReleaseSession(); @@ -60,8 +66,9 @@ class JingleChromotingConnection : public ChromotingConnection, // Used for Session.SignalState sigslot. void OnSessionState(cricket::BaseSession* session, cricket::BaseSession::State state); + void OnInitiate(bool incoming); - void OnAccept(); + void OnAccept(bool incoming); void OnTerminate(); void SetState(State new_state); @@ -81,8 +88,11 @@ class JingleChromotingConnection : public ChromotingConnection, // The corresponding libjingle session. cricket::Session* session_; - cricket::PseudoTcpChannel* events_channel_; - scoped_ptr<StreamSocketAdapter> events_channel_adapter_; + scoped_ptr<const CandidateChromotocolConfig> candidate_config_; + scoped_ptr<const ChromotocolConfig> config_; + + cricket::PseudoTcpChannel* event_channel_; + scoped_ptr<StreamSocketAdapter> event_channel_adapter_; cricket::PseudoTcpChannel* video_channel_; scoped_ptr<StreamSocketAdapter> video_channel_adapter_; scoped_ptr<TransportChannelSocketAdapter> video_rtp_channel_; diff --git a/remoting/protocol/jingle_chromoting_connection_unittest.cc b/remoting/protocol/jingle_chromoting_connection_unittest.cc index 7a24ad2..abd00f9 100644 --- a/remoting/protocol/jingle_chromoting_connection_unittest.cc +++ b/remoting/protocol/jingle_chromoting_connection_unittest.cc @@ -45,7 +45,8 @@ const int kUdpWriteDelayMs = 10; class MockServerCallback { public: - MOCK_METHOD2(OnNewConnection, void(ChromotingConnection*, bool*)); + MOCK_METHOD2(OnNewConnection, void(ChromotingConnection*, + ChromotingServer::NewConnectionResponse*)); }; class MockConnectionCallback { @@ -62,6 +63,8 @@ class JingleChromotingConnectionTest : public testing::Test { host_connection_->SetStateChangeCallback( NewCallback(&host_connection_callback_, &MockConnectionCallback::OnStateChange)); + + connection->set_config(ChromotocolConfig::CreateDefault()); } protected: @@ -123,10 +126,9 @@ class JingleChromotingConnectionTest : public testing::Test { void InitiateConnection() { EXPECT_CALL(host_server_callback_, OnNewConnection(_, _)) .WillOnce(DoAll( - WithArg<0>( - Invoke(this, - &JingleChromotingConnectionTest::SetHostConnection)), - SetArgumentPointee<1>(true))); + WithArg<0>(Invoke( + this, &JingleChromotingConnectionTest::SetHostConnection)), + SetArgumentPointee<1>(ChromotingServer::ACCEPT))); base::WaitableEvent host_connected_event(false, false); EXPECT_CALL(host_connection_callback_, @@ -158,6 +160,7 @@ class JingleChromotingConnectionTest : public testing::Test { client_connection_ = client_server_->Connect( SessionManagerPair::kHostJid, + CandidateChromotocolConfig::CreateDefault(), NewCallback(&client_connection_callback_, &MockConnectionCallback::OnStateChange)); @@ -196,7 +199,7 @@ class JingleChromotingConnectionTest : public testing::Test { class ChannelTesterBase : public base::RefCountedThreadSafe<ChannelTesterBase> { public: enum ChannelType { - EVENTS, + EVENT, VIDEO, VIDEO_RTP, VIDEO_RTCP, @@ -243,8 +246,8 @@ class ChannelTesterBase : public base::RefCountedThreadSafe<ChannelTesterBase> { net::Socket* SelectChannel(ChromotingConnection* connection, ChannelType channel) { switch (channel) { - case EVENTS: - return connection->GetEventsChannel(); + case EVENT: + return connection->GetEventChannel(); case VIDEO: return connection->GetVideoChannel(); case VIDEO_RTP: @@ -512,7 +515,7 @@ TEST_F(JingleChromotingConnectionTest, RejectConnection) { // Reject incoming connection. EXPECT_CALL(host_server_callback_, OnNewConnection(_, _)) - .WillOnce(SetArgumentPointee<1>(false)); + .WillOnce(SetArgumentPointee<1>(ChromotingServer::DECLINE)); base::WaitableEvent done_event(false, false); EXPECT_CALL(client_connection_callback_, @@ -525,6 +528,7 @@ TEST_F(JingleChromotingConnectionTest, RejectConnection) { client_connection_ = client_server_->Connect( SessionManagerPair::kHostJid, + CandidateChromotocolConfig::CreateDefault(), NewCallback(&client_connection_callback_, &MockConnectionCallback::OnStateChange)); @@ -553,14 +557,14 @@ TEST_F(JingleChromotingConnectionTest, TestVideoChannel) { CloseConnections(); } -// Verify that data can be transmitted over the events channel. -TEST_F(JingleChromotingConnectionTest, TestEventsChannel) { +// Verify that data can be transmitted over the event channel. +TEST_F(JingleChromotingConnectionTest, TestEventChannel) { CreateServerPair(); InitiateConnection(); scoped_refptr<TCPChannelTester> tester = new TCPChannelTester(thread_.message_loop(), host_connection_, client_connection_); - tester->Start(ChannelTesterBase::EVENTS); + tester->Start(ChannelTesterBase::EVENT); tester->WaitFinished(); tester->CheckResults(); diff --git a/remoting/protocol/jingle_chromoting_server.cc b/remoting/protocol/jingle_chromoting_server.cc index 517967f..09ed2b7 100644 --- a/remoting/protocol/jingle_chromoting_server.cc +++ b/remoting/protocol/jingle_chromoting_server.cc @@ -5,6 +5,7 @@ #include "remoting/protocol/jingle_chromoting_server.h" #include "base/message_loop.h" +#include "base/string_number_conversions.h" #include "remoting/base/constants.h" #include "third_party/libjingle/source/talk/p2p/base/constants.h" #include "third_party/libjingle/source/talk/p2p/base/transport.h" @@ -15,19 +16,146 @@ using cricket::SessionDescription; using cricket::Session; using cricket::SessionManager; using buzz::QName; +using buzz::XmlElement; namespace remoting { namespace { -const char kDescriptionTag[] = "description"; -const char kTypeTag[] = "type"; + +const char kDefaultNs[] = ""; + +const char kChromotingContentName[] = "chromoting"; + +// Following constants are used to format session description in XML. +const char kDescriptionTag[] = "description"; +const char kControlTag[] = "control"; +const char kEventTag[] = "event"; +const char kVideoTag[] = "video"; +const char kResolutionTag[] = "initial-resolution"; + +const char kTransportAttr[] = "transport"; +const char kVersionAttr[] = "version"; +const char kCodecAttr[] = "codec"; +const char kWidthAttr[] = "width"; +const char kHeightAttr[] = "height"; + +const char kStreamTransport[] = "stream"; +const char kDatagramTransport[] = "datagram"; +const char kSrtpTransport[] = "srtp"; +const char kRtpDtlsTransport[] = "rtp-dtls"; + +const char kVp8Codec[] = "vp8"; +const char kZipCodec[] = "zip"; + +const char* GetTransportName(ChannelConfig::TransportType type) { + switch (type) { + case ChannelConfig::TRANSPORT_STREAM: + return kStreamTransport; + case ChannelConfig::TRANSPORT_DATAGRAM: + return kDatagramTransport; + case ChannelConfig::TRANSPORT_SRTP: + return kSrtpTransport; + case ChannelConfig::TRANSPORT_RTP_DTLS: + return kRtpDtlsTransport; + } + NOTREACHED(); + return NULL; +} + +const char* GetCodecName(ChannelConfig::Codec type) { + switch (type) { + case ChannelConfig::CODEC_VP8: + return kVp8Codec; + case ChannelConfig::CODEC_ZIP: + return kZipCodec; + default: + break; + } + NOTREACHED(); + return NULL; +} + + +// Format a channel configuration tag for chromotocol session description, +// e.g. for video channel: +// <video transport="srtp" version="1" codec="vp8" /> +XmlElement* FormatChannelConfig(const ChannelConfig& config, + const std::string& tag_name) { + XmlElement* result = new XmlElement( + QName(kChromotingXmlNamespace, tag_name)); + + result->AddAttr(QName(kDefaultNs, kTransportAttr), + GetTransportName(config.transport)); + + result->AddAttr(QName(kDefaultNs, kVersionAttr), + base::IntToString(config.version)); + + if (config.codec != ChannelConfig::CODEC_UNDEFINED) { + result->AddAttr(QName(kDefaultNs, kCodecAttr), + GetCodecName(config.codec)); + } + + return result; +} + +bool ParseTransportName(const std::string& value, + ChannelConfig::TransportType* transport) { + if (value == kStreamTransport) { + *transport = ChannelConfig::TRANSPORT_STREAM; + } else if (value == kDatagramTransport) { + *transport = ChannelConfig::TRANSPORT_DATAGRAM; + } else if (value == kSrtpTransport) { + *transport = ChannelConfig::TRANSPORT_SRTP; + } else if (value == kRtpDtlsTransport) { + *transport = ChannelConfig::TRANSPORT_RTP_DTLS; + } else { + return false; + } + return true; +} + +bool ParseCodecName(const std::string& value, ChannelConfig::Codec* codec) { + if (value == kVp8Codec) { + *codec = ChannelConfig::CODEC_VP8; + } else if (value == kZipCodec) { + *codec = ChannelConfig::CODEC_ZIP; + } else { + return false; + } + return true; +} + +// Returns false if the element is invalid. +bool ParseChannelConfig(const XmlElement* element, bool codec_required, + ChannelConfig* config) { + if (!ParseTransportName(element->Attr(QName(kDefaultNs, kTransportAttr)), + &config->transport) || + !base::StringToInt(element->Attr(QName(kDefaultNs, kVersionAttr)), + &config->version)) { + return false; + } + + if (codec_required) { + if (!ParseCodecName(element->Attr(QName(kDefaultNs, kCodecAttr)), + &config->codec)) { + return false; + } + } else { + config->codec = ChannelConfig::CODEC_UNDEFINED; + } + + return true; +} + } // namespace ChromotingContentDescription::ChromotingContentDescription( - const std::string& description) - : description_(description) { + const CandidateChromotocolConfig* config) + : candidate_config_(config) { } +ChromotingContentDescription::~ChromotingContentDescription() { } + JingleChromotingServer::JingleChromotingServer( MessageLoop* message_loop) : message_loop_(message_loop), @@ -91,13 +219,16 @@ void JingleChromotingServer::set_allow_local_ips(bool allow_local_ips) { scoped_refptr<ChromotingConnection> JingleChromotingServer::Connect( const std::string& jid, + CandidateChromotocolConfig* chromotocol_config, ChromotingConnection::StateChangeCallback* state_change_callback) { // Can be called from any thread. scoped_refptr<JingleChromotingConnection> connection = new JingleChromotingConnection(this); + connection->set_candidate_config(chromotocol_config); message_loop()->PostTask( FROM_HERE, NewRunnableMethod(this, &JingleChromotingServer::DoConnect, - connection, jid, state_change_callback)); + connection, jid, + state_change_callback)); return connection; } @@ -114,7 +245,8 @@ void JingleChromotingServer::DoConnect( connection->Init(session); connections_.push_back(connection); - session->Initiate(jid, CreateSessionDescription()); + session->Initiate( + jid, CreateSessionDescription(connection->candidate_config()->Clone())); } MessageLoop* JingleChromotingServer::message_loop() { @@ -128,7 +260,7 @@ void JingleChromotingServer::OnSessionCreate( // Allow local connections if neccessary. session->set_allow_local_ips(allow_local_ips_); - // If this is an outcoming session, the the connection object is already + // If this is an outcoming session the connection object is already // created. if (incoming) { JingleChromotingConnection* connection = @@ -154,29 +286,122 @@ void JingleChromotingServer::OnSessionDestroy(Session* session) { void JingleChromotingServer::AcceptConnection( JingleChromotingConnection* connection, Session* session) { - bool accept = false; - // Always reject connection if we are closed or there is no callback. - if (!closed_ && new_connection_callback_.get()) - new_connection_callback_->Run(connection, &accept); - - if (accept) - session->Accept(CreateSessionDescription()); - else + DCHECK_EQ(message_loop(), MessageLoop::current()); + + // Reject connection if we are closed. + if (closed_) { session->Reject(cricket::STR_TERMINATE_DECLINE); + return; + } + + const cricket::SessionDescription* session_description = + session->remote_description(); + const cricket::ContentInfo* content = + session_description->FirstContentByType(kChromotingXmlNamespace); + + CHECK(content); + + const ChromotingContentDescription* content_description = + static_cast<const ChromotingContentDescription*>(content->description); + connection->set_candidate_config(content_description->config()->Clone()); + + NewConnectionResponse response = ChromotingServer::DECLINE; + + // Always reject connection if there is no callback. + if (new_connection_callback_.get()) + new_connection_callback_->Run(connection, &response); + + switch (response) { + case ChromotingServer::ACCEPT: { + // Connection must be configured by the callback. + DCHECK(connection->config()); + CandidateChromotocolConfig* candidate_config = + CandidateChromotocolConfig::CreateFrom(connection->config()); + session->Accept(CreateSessionDescription(candidate_config)); + break; + } + + case ChromotingServer::INCOMPATIBLE: { + session->Reject(cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS); + break; + } + + case ChromotingServer::DECLINE: { + session->Reject(cricket::STR_TERMINATE_DECLINE); + break; + } + + default: { + NOTREACHED(); + } + } } +// Parse content description generated by WriteContent(). bool JingleChromotingServer::ParseContent( cricket::SignalingProtocol protocol, - const buzz::XmlElement* element, + const XmlElement* element, const cricket::ContentDescription** content, cricket::ParseError* error) { if (element->Name() == QName(kChromotingXmlNamespace, kDescriptionTag)) { - const buzz::XmlElement* type_elem = - element->FirstNamed(QName(kChromotingXmlNamespace, kTypeTag)); - if (type_elem != NULL) { - *content = new ChromotingContentDescription(type_elem->BodyText()); - return true; + scoped_ptr<CandidateChromotocolConfig> config( + CandidateChromotocolConfig::CreateEmpty()); + const XmlElement* child = NULL; + + // <control> tags. + QName control_tag(kChromotingXmlNamespace, kControlTag); + child = element->FirstNamed(control_tag); + while (child) { + ChannelConfig channel_config; + if (!ParseChannelConfig(child, false, &channel_config)) + return false; + config->AddControlConfig(channel_config); + child = element->NextNamed(control_tag); + } + + // <event> tags. + QName event_tag(kChromotingXmlNamespace, kEventTag); + child = element->FirstNamed(event_tag); + while (child) { + ChannelConfig channel_config; + if (!ParseChannelConfig(child, false, &channel_config)) + return false; + config->AddEventConfig(channel_config); + child = element->NextNamed(event_tag); + } + + // <video> tags. + QName video_tag(kChromotingXmlNamespace, kVideoTag); + child = element->FirstNamed(video_tag); + while (child) { + ChannelConfig channel_config; + if (!ParseChannelConfig(child, true, &channel_config)) + return false; + config->AddVideoConfig(channel_config); + child = element->NextNamed(video_tag); + } + + // <initial-resolution> tag. + child = element->FirstNamed(QName(kChromotingXmlNamespace, kResolutionTag)); + if (!child) + return false; // Resolution must always be specified. + int width; + int height; + if (!base::StringToInt(child->Attr(QName(kDefaultNs, kWidthAttr)), + &width) || + !base::StringToInt(child->Attr(QName(kDefaultNs, kHeightAttr)), + &height)) { + return false; } + ScreenResolution resolution(width, height); + if (!resolution.IsValid()) { + return false; + } + + config->SetInitialResolution(resolution); + + *content = new ChromotingContentDescription(config.release()); + return true; } LOG(ERROR) << "Invalid description: " << element->Str(); return false; @@ -185,34 +410,60 @@ bool JingleChromotingServer::ParseContent( // WriteContent creates content description for chromoting session. The // description looks as follows: // <description xmlns="google:remoting"> -// <type>description_text</type> +// <control transport="stream" version="1" /> +// <event transport="datagram" version="1" /> +// <video transport="srtp" codec="vp8" version="1" /> +// <initial-resolution width="800" height="600" /> // </description> -// Currently description_text is always empty. // -// TODO(sergeyu): Add more information to the content description. E.g. -// protocol version, etc. bool JingleChromotingServer::WriteContent( cricket::SignalingProtocol protocol, const cricket::ContentDescription* content, - buzz::XmlElement** elem, + XmlElement** elem, cricket::WriteError* error) { const ChromotingContentDescription* desc = static_cast<const ChromotingContentDescription*>(content); - QName desc_tag(kChromotingXmlNamespace, kDescriptionTag); - buzz::XmlElement* root = new buzz::XmlElement(desc_tag, true); - QName type_tag(kChromotingXmlNamespace, kTypeTag); - buzz::XmlElement* type_elem = new buzz::XmlElement(type_tag); - type_elem->SetBodyText(desc->description()); - root->AddElement(type_elem); + XmlElement* root = new XmlElement( + QName(kChromotingXmlNamespace, kDescriptionTag), true); + + const CandidateChromotocolConfig* config = desc->config(); + std::vector<ChannelConfig>::const_iterator it; + + for (it = config->control_configs().begin(); + it != config->control_configs().end(); ++it) { + root->AddElement(FormatChannelConfig(*it, kControlTag)); + } + + for (it = config->event_configs().begin(); + it != config->event_configs().end(); ++it) { + root->AddElement(FormatChannelConfig(*it, kEventTag)); + } + + for (it = config->video_configs().begin(); + it != config->video_configs().end(); ++it) { + root->AddElement(FormatChannelConfig(*it, kVideoTag)); + } + + XmlElement* resolution_tag = new XmlElement( + QName(kChromotingXmlNamespace, kResolutionTag)); + resolution_tag->AddAttr(QName(kDefaultNs, kWidthAttr), + base::IntToString( + config->initial_resolution().width)); + resolution_tag->AddAttr(QName(kDefaultNs, kHeightAttr), + base::IntToString( + config->initial_resolution().height)); + root->AddElement(resolution_tag); + *elem = root; return true; } -SessionDescription* JingleChromotingServer::CreateSessionDescription() { +SessionDescription* JingleChromotingServer::CreateSessionDescription( + const CandidateChromotocolConfig* config) { SessionDescription* desc = new SessionDescription(); - desc->AddContent("chromoting", kChromotingXmlNamespace, - new ChromotingContentDescription("")); + desc->AddContent(kChromotingContentName, kChromotingXmlNamespace, + new ChromotingContentDescription(config)); return desc; } diff --git a/remoting/protocol/jingle_chromoting_server.h b/remoting/protocol/jingle_chromoting_server.h index 7dff47d..b802057 100644 --- a/remoting/protocol/jingle_chromoting_server.h +++ b/remoting/protocol/jingle_chromoting_server.h @@ -24,16 +24,22 @@ class SessionManager; namespace remoting { -// ContentDescription used for chromoting sessions. -// -// TODO(sergeyu): Do we need host_id or some other information in the content -// description? +// ContentDescription used for chromoting sessions. It simply wraps +// CandidateChromotocolConfig. CandidateChromotocolConfig doesn't inherit +// from ContentDescription to avoid dependency on libjingle in +// ChromotingConnection interface. class ChromotingContentDescription : public cricket::ContentDescription { public: - ChromotingContentDescription(const std::string& description); - const std::string& description() const { return description_; } + explicit ChromotingContentDescription( + const CandidateChromotocolConfig* config); + ~ChromotingContentDescription(); + + const CandidateChromotocolConfig* config() const { + return candidate_config_.get(); + } + private: - std::string description_; + scoped_ptr<const CandidateChromotocolConfig> candidate_config_; }; // This class implements SessionClient for Chromoting sessions. It acts as a @@ -55,6 +61,7 @@ class JingleChromotingServer // ChromotingServer interface. virtual scoped_refptr<ChromotingConnection> Connect( const std::string& jid, + CandidateChromotocolConfig* chromotocol_config, ChromotingConnection::StateChangeCallback* state_change_callback); virtual void Close(Task* closed_task); @@ -79,7 +86,8 @@ class JingleChromotingServer ChromotingConnection::StateChangeCallback* state_change_callback); // Creates outgoing session description for an incoming connection. - cricket::SessionDescription* CreateSessionDescription(); + cricket::SessionDescription* CreateSessionDescription( + const CandidateChromotocolConfig* config); // cricket::SessionClient interface. virtual void OnSessionCreate(cricket::Session* session, diff --git a/remoting/protocol/protocol_test_client.cc b/remoting/protocol/protocol_test_client.cc index 7c15b0e..946931a 100644 --- a/remoting/protocol/protocol_test_client.cc +++ b/remoting/protocol/protocol_test_client.cc @@ -98,8 +98,9 @@ class ProtocolTestClient virtual void OnStateChange(JingleClient* client, JingleClient::State state); // callback for JingleChromotingServer interface. - virtual void OnNewChromotocolConnection(ChromotingConnection* connection, - bool* accept); + virtual void OnNewChromotocolConnection( + ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response); private: typedef std::list<scoped_refptr<ProtocolTestConnection> > ConnectionsList; @@ -138,7 +139,7 @@ void ProtocolTestConnection::DoWrite( return; } - net::Socket* channel = connection_->GetEventsChannel(); + net::Socket* channel = connection_->GetEventChannel(); if (channel != NULL) { int result = channel->Write(buf, size, &write_cb_); if (result < 0) { @@ -161,7 +162,7 @@ void ProtocolTestConnection::Read() { void ProtocolTestConnection::DoRead() { read_buffer_ = new net::IOBuffer(kBufferSize); while (true) { - int result = connection_->GetEventsChannel()->Read( + int result = connection_->GetEventChannel()->Read( read_buffer_, kBufferSize, &read_cb_); if (result < 0) { if (result != net::ERR_IO_PENDING) @@ -277,16 +278,16 @@ void ProtocolTestClient::OnStateChange( server_->Init( client_->GetFullJid(), client_->session_manager(), - NewCallback(this, - &ProtocolTestClient::OnNewChromotocolConnection)); + NewCallback(this, &ProtocolTestClient::OnNewChromotocolConnection)); server_->set_allow_local_ips(true); if (host_jid_ != "") { ProtocolTestConnection* connection = new ProtocolTestConnection(this, client_->message_loop()); connection->Init(server_->Connect( - host_jid_, NewCallback(connection, - &ProtocolTestConnection::OnStateChange))); + host_jid_, CandidateChromotocolConfig::CreateDefault(), + NewCallback(connection, + &ProtocolTestConnection::OnStateChange))); connections_.push_back(connection); } } else if (state == JingleClient::CLOSED) { @@ -295,8 +296,13 @@ void ProtocolTestClient::OnStateChange( } void ProtocolTestClient::OnNewChromotocolConnection( - ChromotingConnection* connection, bool* accept) { + ChromotingConnection* connection, + ChromotingServer::NewConnectionResponse* response) { std::cerr << "Accepting connection from " << connection->jid() << std::endl; + + connection->set_config(ChromotocolConfig::CreateDefault()); + *response = ChromotingServer::ACCEPT; + ProtocolTestConnection* test_connection = new ProtocolTestConnection(this, client_->message_loop()); connection->SetStateChangeCallback( @@ -304,7 +310,6 @@ void ProtocolTestClient::OnNewChromotocolConnection( test_connection->Init(connection); AutoLock auto_lock(connections_lock_); connections_.push_back(test_connection); - *accept = true; } void ProtocolTestClient::OnFinishedClosing() { diff --git a/remoting/protocol/stream_reader.cc b/remoting/protocol/stream_reader.cc index be6a237..23d3dec 100644 --- a/remoting/protocol/stream_reader.cc +++ b/remoting/protocol/stream_reader.cc @@ -9,17 +9,17 @@ namespace remoting { -// EventsStreamReader class. -EventsStreamReader::EventsStreamReader() { } -EventsStreamReader::~EventsStreamReader() { } +// EventStreamReader class. +EventStreamReader::EventStreamReader() { } +EventStreamReader::~EventStreamReader() { } -void EventsStreamReader::Init(net::Socket* socket, - OnMessageCallback* on_message_callback) { +void EventStreamReader::Init(net::Socket* socket, + OnMessageCallback* on_message_callback) { on_message_callback_.reset(on_message_callback); SocketReaderBase::Init(socket); } -void EventsStreamReader::OnDataReceived(net::IOBuffer* buffer, int data_size) { +void EventStreamReader::OnDataReceived(net::IOBuffer* buffer, int data_size) { ClientMessageList messages_list; messages_decoder_.ParseClientMessages(buffer, data_size, &messages_list); for (ClientMessageList::iterator it = messages_list.begin(); diff --git a/remoting/protocol/stream_reader.h b/remoting/protocol/stream_reader.h index 21c7bda..7f8e004 100644 --- a/remoting/protocol/stream_reader.h +++ b/remoting/protocol/stream_reader.h @@ -11,10 +11,10 @@ namespace remoting { -class EventsStreamReader : public SocketReaderBase { +class EventStreamReader : public SocketReaderBase { public: - EventsStreamReader(); - ~EventsStreamReader(); + EventStreamReader(); + ~EventStreamReader(); // The OnMessageCallback is called whenever a new message is received. // Ownership of the message is passed the callback. @@ -22,7 +22,7 @@ class EventsStreamReader : public SocketReaderBase { // Initialize the reader and start reading. Must be called on the thread // |socket| belongs to. The callback will be called when a new message is - // received. EventsStreamReader owns |on_message_callback|, doesn't own + // received. EventStreamReader owns |on_message_callback|, doesn't own // |socket|. void Init(net::Socket* socket, OnMessageCallback* on_message_callback); @@ -33,7 +33,7 @@ class EventsStreamReader : public SocketReaderBase { MessagesDecoder messages_decoder_; scoped_ptr<OnMessageCallback> on_message_callback_; - DISALLOW_COPY_AND_ASSIGN(EventsStreamReader); + DISALLOW_COPY_AND_ASSIGN(EventStreamReader); }; class VideoStreamReader : public SocketReaderBase { diff --git a/remoting/protocol/stream_writer.cc b/remoting/protocol/stream_writer.cc index 3269a1b..7650edf 100644 --- a/remoting/protocol/stream_writer.cc +++ b/remoting/protocol/stream_writer.cc @@ -34,7 +34,7 @@ void StreamWriterBase::Close() { buffered_writer_->Close(); } -bool EventsStreamWriter::SendMessage( +bool EventStreamWriter::SendMessage( const ChromotingClientMessage& message) { return buffered_writer_->Write(SerializeAndFrameMessage(message)); } diff --git a/remoting/protocol/stream_writer.h b/remoting/protocol/stream_writer.h index 4bc2b76..cc1c4b1 100644 --- a/remoting/protocol/stream_writer.h +++ b/remoting/protocol/stream_writer.h @@ -34,7 +34,7 @@ class StreamWriterBase { scoped_refptr<BufferedSocketWriter> buffered_writer_; }; -class EventsStreamWriter : public StreamWriterBase { +class EventStreamWriter : public StreamWriterBase { public: // Sends the |message| or returns false if called before Init(). // Can be called on any thread. diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 72ed762f..5e5c3ff 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -365,6 +365,8 @@ 'protocol/jingle_chromoting_connection.h', 'protocol/jingle_chromoting_server.cc', 'protocol/jingle_chromoting_server.h', + 'protocol/chromotocol_config.cc', + 'protocol/chromotocol_config.h', 'protocol/rtp_reader.cc', 'protocol/rtp_reader.h', 'protocol/rtp_utils.cc', |