diff options
author | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-03 21:56:42 +0000 |
---|---|---|
committer | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-03 21:56:42 +0000 |
commit | 230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a (patch) | |
tree | 8aa02e4a85f0d314a4419a434037e163a41981c8 /remoting/protocol/jingle_session_manager.cc | |
parent | 3296839602c5c54cae0b0fb0a9d6623a5ba65895 (diff) | |
download | chromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.zip chromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.tar.gz chromium_src-230d2fd2e1cafaab5cd5e4d2a9f64b4ae272550a.tar.bz2 |
Rename classes for Chromoting:
ChromotocolServer -> protocol::SessionManager
ChromotocolConnection -> protocol::Session
BUG=none
TEST=compiles + make chromoting connection
Review URL: http://codereview.chromium.org/4313001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64971 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol/jingle_session_manager.cc')
-rw-r--r-- | remoting/protocol/jingle_session_manager.cc | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/remoting/protocol/jingle_session_manager.cc b/remoting/protocol/jingle_session_manager.cc new file mode 100644 index 0000000..8784f83 --- /dev/null +++ b/remoting/protocol/jingle_session_manager.cc @@ -0,0 +1,478 @@ +// 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/jingle_session_manager.h" + +#include "base/message_loop.h" +#include "base/string_number_conversions.h" +#include "remoting/base/constants.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "third_party/libjingle/source/talk/p2p/base/constants.h" +#include "third_party/libjingle/source/talk/p2p/base/transport.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" + +using cricket::ContentDescription; +using cricket::SessionDescription; +using buzz::QName; +using buzz::XmlElement; + +namespace remoting { + +namespace protocol { + +namespace { + +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 + +ContentDescription::ContentDescription( + const CandidateChromotocolConfig* config) + : candidate_config_(config) { +} + +ContentDescription::~ContentDescription() { } + +JingleSessionManager::JingleSessionManager( + JingleThread* jingle_thread) + : jingle_thread_(jingle_thread), + cricket_session_manager_(NULL), + allow_local_ips_(false), + closed_(false) { + DCHECK(jingle_thread_); +} + +void JingleSessionManager::Init( + const std::string& local_jid, + cricket::SessionManager* cricket_session_manager, + IncomingSessionCallback* incoming_session_callback) { + if (MessageLoop::current() != message_loop()) { + message_loop()->PostTask( + FROM_HERE, NewRunnableMethod( + this, &JingleSessionManager::Init, + local_jid, cricket_session_manager, incoming_session_callback)); + return; + } + + DCHECK(cricket_session_manager); + DCHECK(incoming_session_callback); + + local_jid_ = local_jid; + incoming_session_callback_.reset(incoming_session_callback); + cricket_session_manager_ = cricket_session_manager; + cricket_session_manager_->AddClient(kChromotingXmlNamespace, this); +} + +JingleSessionManager::~JingleSessionManager() { + DCHECK(closed_); +} + +void JingleSessionManager::Close(Task* closed_task) { + if (MessageLoop::current() != message_loop()) { + message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleSessionManager::Close, + closed_task)); + return; + } + + if (!closed_) { + // Close all connections. + cricket_session_manager_->RemoveClient(kChromotingXmlNamespace); + while (!sessions_.empty()) { + cricket::Session* session = sessions_.front()->ReleaseSession(); + cricket_session_manager_->DestroySession(session); + sessions_.pop_front(); + } + closed_ = true; + } + + closed_task->Run(); + delete closed_task; +} + +void JingleSessionManager::set_allow_local_ips(bool allow_local_ips) { + allow_local_ips_ = allow_local_ips; +} + +scoped_refptr<protocol::Session> JingleSessionManager::Connect( + const std::string& jid, + CandidateChromotocolConfig* chromotocol_config, + protocol::Session::StateChangeCallback* state_change_callback) { + // Can be called from any thread. + scoped_refptr<JingleSession> jingle_session( + new JingleSession(this)); + jingle_session->set_candidate_config(chromotocol_config); + message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &JingleSessionManager::DoConnect, + jingle_session, jid, + state_change_callback)); + return jingle_session; +} + +void JingleSessionManager::DoConnect( + scoped_refptr<JingleSession> jingle_session, + const std::string& jid, + protocol::Session::StateChangeCallback* state_change_callback) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + cricket::Session* cricket_session = cricket_session_manager_->CreateSession( + local_jid_, kChromotingXmlNamespace); + + // Initialize connection object before we send initiate stanza. + jingle_session->SetStateChangeCallback(state_change_callback); + jingle_session->Init(cricket_session); + sessions_.push_back(jingle_session); + + cricket_session->Initiate( + jid, + CreateSessionDescription(jingle_session->candidate_config()->Clone())); +} + +JingleThread* JingleSessionManager::jingle_thread() { + return jingle_thread_; +} + +MessageLoop* JingleSessionManager::message_loop() { + return jingle_thread_->message_loop(); +} + +void JingleSessionManager::OnSessionCreate( + cricket::Session* cricket_session, bool incoming) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + // Allow local connections if neccessary. + cricket_session->set_allow_local_ips(allow_local_ips_); + + // If this is an outcoming session the connection object is already + // created. + if (incoming) { + JingleSession* jingle_session = new JingleSession(this); + sessions_.push_back(make_scoped_refptr(jingle_session)); + jingle_session->Init(cricket_session); + } +} + +void JingleSessionManager::OnSessionDestroy(cricket::Session* cricket_session) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + std::list<scoped_refptr<JingleSession> >::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); ++it) { + if ((*it)->HasSession(cricket_session)) { + (*it)->ReleaseSession(); + sessions_.erase(it); + return; + } + } +} + +void JingleSessionManager::AcceptConnection( + JingleSession* jingle_session, + cricket::Session* cricket_session) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + // Reject connection if we are closed. + if (closed_) { + cricket_session->Reject(cricket::STR_TERMINATE_DECLINE); + return; + } + + const cricket::SessionDescription* session_description = + cricket_session->remote_description(); + const cricket::ContentInfo* content = + session_description->FirstContentByType(kChromotingXmlNamespace); + + CHECK(content); + + const ContentDescription* content_description = + static_cast<const ContentDescription*>(content->description); + jingle_session->set_candidate_config(content_description->config()->Clone()); + + IncomingSessionResponse response = protocol::SessionManager::DECLINE; + + // Always reject connection if there is no callback. + if (incoming_session_callback_.get()) + incoming_session_callback_->Run(jingle_session, &response); + + switch (response) { + case protocol::SessionManager::ACCEPT: { + // Connection must be configured by the callback. + DCHECK(jingle_session->config()); + CandidateChromotocolConfig* candidate_config = + CandidateChromotocolConfig::CreateFrom(jingle_session->config()); + cricket_session->Accept(CreateSessionDescription(candidate_config)); + break; + } + + case protocol::SessionManager::INCOMPATIBLE: { + cricket_session->Reject(cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS); + break; + } + + case protocol::SessionManager::DECLINE: { + cricket_session->Reject(cricket::STR_TERMINATE_DECLINE); + break; + } + + default: { + NOTREACHED(); + } + } +} + +// Parse content description generated by WriteContent(). +bool JingleSessionManager::ParseContent( + cricket::SignalingProtocol protocol, + const XmlElement* element, + const cricket::ContentDescription** content, + cricket::ParseError* error) { + if (element->Name() == QName(kChromotingXmlNamespace, kDescriptionTag)) { + 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 ContentDescription(config.release()); + return true; + } + LOG(ERROR) << "Invalid description: " << element->Str(); + return false; +} + +// WriteContent creates content description for chromoting session. The +// description looks as follows: +// <description xmlns="google:remoting"> +// <control transport="stream" version="1" /> +// <event transport="datagram" version="1" /> +// <video transport="srtp" codec="vp8" version="1" /> +// <initial-resolution width="800" height="600" /> +// </description> +// +bool JingleSessionManager::WriteContent( + cricket::SignalingProtocol protocol, + const cricket::ContentDescription* content, + XmlElement** elem, + cricket::WriteError* error) { + const ContentDescription* desc = + static_cast<const ContentDescription*>(content); + + 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* JingleSessionManager::CreateSessionDescription( + const CandidateChromotocolConfig* config) { + SessionDescription* desc = new SessionDescription(); + desc->AddContent(JingleSession::kChromotingContentName, + kChromotingXmlNamespace, + new ContentDescription(config)); + return desc; +} + +} // namespace protocol + +} // namespace remoting |