summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-24 21:08:35 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-24 21:08:35 +0000
commit8922bdbce7e19faf46604138b6bf0e5276f1db3c (patch)
tree74cc718706f2dcee7cd4eff96f507dbcb2db472b
parent476f1c53ebdd8a5ddcadef5c34123f5492887176 (diff)
downloadchromium_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
-rw-r--r--remoting/client/jingle_host_connection.cc18
-rw-r--r--remoting/client/jingle_host_connection.h5
-rw-r--r--remoting/host/chromoting_host.cc39
-rw-r--r--remoting/host/chromoting_host.h7
-rw-r--r--remoting/host/client_connection.cc4
-rw-r--r--remoting/host/client_connection.h2
-rw-r--r--remoting/protocol/chromoting_connection.h18
-rw-r--r--remoting/protocol/chromoting_server.h24
-rw-r--r--remoting/protocol/chromotocol_config.cc243
-rw-r--r--remoting/protocol/chromotocol_config.h161
-rw-r--r--remoting/protocol/fake_connection.cc21
-rw-r--r--remoting/protocol/fake_connection.h10
-rw-r--r--remoting/protocol/host_message_dispatcher.h12
-rw-r--r--remoting/protocol/jingle_chromoting_connection.cc88
-rw-r--r--remoting/protocol/jingle_chromoting_connection.h20
-rw-r--r--remoting/protocol/jingle_chromoting_connection_unittest.cc28
-rw-r--r--remoting/protocol/jingle_chromoting_server.cc321
-rw-r--r--remoting/protocol/jingle_chromoting_server.h24
-rw-r--r--remoting/protocol/protocol_test_client.cc25
-rw-r--r--remoting/protocol/stream_reader.cc12
-rw-r--r--remoting/protocol/stream_reader.h10
-rw-r--r--remoting/protocol/stream_writer.cc2
-rw-r--r--remoting/protocol/stream_writer.h2
-rw-r--r--remoting/remoting.gyp2
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',