|  | // Copyright (c) 2012 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/session_config.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  |  | 
|  | namespace remoting { | 
|  | namespace protocol { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool IsChannelConfigSupported(const std::list<ChannelConfig>& list, | 
|  | const ChannelConfig& value) { | 
|  | return std::find(list.begin(), list.end(), value) != list.end(); | 
|  | } | 
|  |  | 
|  | bool SelectCommonChannelConfig(const std::list<ChannelConfig>& host_configs, | 
|  | const std::list<ChannelConfig>& client_configs, | 
|  | ChannelConfig* config) { | 
|  | // Usually each of these lists will contain just a few elements, so iterating | 
|  | // over all of them is not a problem. | 
|  | std::list<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; | 
|  | } | 
|  |  | 
|  | void UpdateConfigListToPreferTransport(std::list<ChannelConfig>* configs, | 
|  | ChannelConfig::TransportType transport) { | 
|  | std::vector<ChannelConfig> sorted(configs->begin(), configs->end()); | 
|  | std::stable_sort(sorted.begin(), sorted.end(), | 
|  | [transport](const ChannelConfig& a, const ChannelConfig& b) { | 
|  | // |a| must precede |b| if |a| uses preferred transport and | 
|  | // |b| doesn't. | 
|  | return a.transport == transport && | 
|  | b.transport != transport; | 
|  | }); | 
|  | configs->assign(sorted.begin(), sorted.end()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | const int kDefaultStreamVersion = 2; | 
|  | const int kControlStreamVersion = 3; | 
|  |  | 
|  | ChannelConfig ChannelConfig::None() { | 
|  | return ChannelConfig(); | 
|  | } | 
|  |  | 
|  | ChannelConfig::ChannelConfig(TransportType transport, int version, Codec codec) | 
|  | : transport(transport), | 
|  | version(version), | 
|  | codec(codec) { | 
|  | } | 
|  |  | 
|  | bool ChannelConfig::operator==(const ChannelConfig& b) const { | 
|  | // If the transport field is set to NONE then all other fields are irrelevant. | 
|  | if (transport == ChannelConfig::TRANSPORT_NONE) | 
|  | return transport == b.transport; | 
|  | return transport == b.transport && version == b.version && codec == b.codec; | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<SessionConfig> SessionConfig::SelectCommon( | 
|  | const CandidateSessionConfig* client_config, | 
|  | const CandidateSessionConfig* host_config) { | 
|  | // Use WebRTC if both host and client support it. | 
|  | if (client_config->webrtc_supported() && host_config->webrtc_supported()) | 
|  | return base::WrapUnique(new SessionConfig(Protocol::WEBRTC)); | 
|  |  | 
|  | // Reject connection if ICE is not supported by either of the peers. | 
|  | if (!host_config->ice_supported() || !client_config->ice_supported()) | 
|  | return nullptr; | 
|  |  | 
|  | std::unique_ptr<SessionConfig> result(new SessionConfig(Protocol::ICE)); | 
|  | ChannelConfig control_config; | 
|  | ChannelConfig event_config; | 
|  | ChannelConfig video_config; | 
|  | ChannelConfig audio_config; | 
|  |  | 
|  | // If neither host nor the client have VP9 experiment enabled then remove it | 
|  | // from the list of host video configs. | 
|  | std::list<ChannelConfig> host_video_configs = host_config->video_configs(); | 
|  | if (!client_config->vp9_experiment_enabled() && | 
|  | !host_config->vp9_experiment_enabled()) { | 
|  | host_video_configs.remove_if([](const ChannelConfig& config) { | 
|  | return config.codec == ChannelConfig::CODEC_VP9; | 
|  | }); | 
|  | } | 
|  |  | 
|  | if (!SelectCommonChannelConfig(host_config->control_configs(), | 
|  | client_config->control_configs(), | 
|  | &result->control_config_) || | 
|  | !SelectCommonChannelConfig(host_config->event_configs(), | 
|  | client_config->event_configs(), | 
|  | &result->event_config_) || | 
|  | !SelectCommonChannelConfig(host_video_configs, | 
|  | client_config->video_configs(), | 
|  | &result->video_config_) || | 
|  | !SelectCommonChannelConfig(host_config->audio_configs(), | 
|  | client_config->audio_configs(), | 
|  | &result->audio_config_)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<SessionConfig> SessionConfig::GetFinalConfig( | 
|  | const CandidateSessionConfig* candidate_config) { | 
|  | if (candidate_config->webrtc_supported()) { | 
|  | if (candidate_config->ice_supported()) { | 
|  | LOG(ERROR) << "Received candidate config is ambiguous."; | 
|  | return nullptr; | 
|  | } | 
|  | return base::WrapUnique(new SessionConfig(Protocol::WEBRTC)); | 
|  | } | 
|  |  | 
|  | if (!candidate_config->ice_supported()) | 
|  | return nullptr; | 
|  |  | 
|  | if (candidate_config->control_configs().size() != 1 || | 
|  | candidate_config->event_configs().size() != 1 || | 
|  | candidate_config->video_configs().size() != 1 || | 
|  | candidate_config->audio_configs().size() != 1) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SessionConfig> result(new SessionConfig(Protocol::ICE)); | 
|  | result->control_config_ = candidate_config->control_configs().front(); | 
|  | result->event_config_ = candidate_config->event_configs().front(); | 
|  | result->video_config_ = candidate_config->video_configs().front(); | 
|  | result->audio_config_ = candidate_config->audio_configs().front(); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<SessionConfig> SessionConfig::ForTest() { | 
|  | std::unique_ptr<SessionConfig> result(new SessionConfig(Protocol::ICE)); | 
|  | result->control_config_ = ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM, | 
|  | kControlStreamVersion, | 
|  | ChannelConfig::CODEC_UNDEFINED); | 
|  | result->event_config_ = ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_UNDEFINED); | 
|  | result->video_config_ = ChannelConfig(ChannelConfig::TRANSPORT_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_VP8); | 
|  | result->audio_config_ = ChannelConfig(ChannelConfig::TRANSPORT_NONE, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_UNDEFINED); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SessionConfig> SessionConfig::ForTestWithAudio() { | 
|  | std::unique_ptr<SessionConfig> result(ForTest()); | 
|  | result->audio_config_ = ChannelConfig(ChannelConfig::TRANSPORT_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_OPUS); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SessionConfig> SessionConfig::ForTestWithVerbatimVideo() { | 
|  | std::unique_ptr<SessionConfig> result = ForTest(); | 
|  | result->video_config_ = ChannelConfig(ChannelConfig::TRANSPORT_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_VERBATIM); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SessionConfig> SessionConfig::ForTestWithWebrtc() { | 
|  | return base::WrapUnique(new SessionConfig(Protocol::WEBRTC)); | 
|  | } | 
|  |  | 
|  | const ChannelConfig& SessionConfig::control_config() const { | 
|  | DCHECK(protocol_ == Protocol::ICE); | 
|  | return control_config_; | 
|  | } | 
|  | const ChannelConfig& SessionConfig::event_config() const { | 
|  | DCHECK(protocol_ == Protocol::ICE); | 
|  | return event_config_; | 
|  | } | 
|  | const ChannelConfig& SessionConfig::video_config() const { | 
|  | DCHECK(protocol_ == Protocol::ICE); | 
|  | return video_config_; | 
|  | } | 
|  | const ChannelConfig& SessionConfig::audio_config() const { | 
|  | DCHECK(protocol_ == Protocol::ICE); | 
|  | return audio_config_; | 
|  | } | 
|  |  | 
|  | SessionConfig::SessionConfig(Protocol protocol) : protocol_(protocol) {} | 
|  |  | 
|  | CandidateSessionConfig::CandidateSessionConfig() {} | 
|  | CandidateSessionConfig::CandidateSessionConfig( | 
|  | const CandidateSessionConfig& config) = default; | 
|  | CandidateSessionConfig::~CandidateSessionConfig() {} | 
|  |  | 
|  | bool CandidateSessionConfig::IsSupported(const SessionConfig& config) const { | 
|  | switch (config.protocol()) { | 
|  | case SessionConfig::Protocol::ICE: | 
|  | return ice_supported() && | 
|  | IsChannelConfigSupported(control_configs_, | 
|  | config.control_config()) && | 
|  | IsChannelConfigSupported(event_configs_, config.event_config()) && | 
|  | IsChannelConfigSupported(video_configs_, config.video_config()) && | 
|  | IsChannelConfigSupported(audio_configs_, config.audio_config()); | 
|  |  | 
|  | case SessionConfig::Protocol::WEBRTC: | 
|  | return webrtc_supported(); | 
|  | } | 
|  |  | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<CandidateSessionConfig> CandidateSessionConfig::Clone() const { | 
|  | return base::WrapUnique(new CandidateSessionConfig(*this)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<CandidateSessionConfig> CandidateSessionConfig::CreateEmpty() { | 
|  | return base::WrapUnique(new CandidateSessionConfig()); | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<CandidateSessionConfig> CandidateSessionConfig::CreateFrom( | 
|  | const SessionConfig& config) { | 
|  | std::unique_ptr<CandidateSessionConfig> result = CreateEmpty(); | 
|  |  | 
|  | switch (config.protocol()) { | 
|  | case SessionConfig::Protocol::WEBRTC: | 
|  | result->set_webrtc_supported(true); | 
|  | result->set_ice_supported(false); | 
|  | break; | 
|  |  | 
|  | case SessionConfig::Protocol::ICE: | 
|  | result->set_webrtc_supported(false); | 
|  | result->set_ice_supported(true); | 
|  | result->mutable_control_configs()->push_back(config.control_config()); | 
|  | result->mutable_event_configs()->push_back(config.event_config()); | 
|  | result->mutable_video_configs()->push_back(config.video_config()); | 
|  | result->mutable_audio_configs()->push_back(config.audio_config()); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // static | 
|  | std::unique_ptr<CandidateSessionConfig> | 
|  | CandidateSessionConfig::CreateDefault() { | 
|  | std::unique_ptr<CandidateSessionConfig> result = CreateEmpty(); | 
|  |  | 
|  | result->set_ice_supported(true); | 
|  |  | 
|  | // Control channel. | 
|  | result->mutable_control_configs()->push_back( | 
|  | ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM, | 
|  | kControlStreamVersion, | 
|  | ChannelConfig::CODEC_UNDEFINED)); | 
|  |  | 
|  | // Event channel. | 
|  | result->mutable_event_configs()->push_back( | 
|  | ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_UNDEFINED)); | 
|  |  | 
|  | // Video channel. | 
|  | result->mutable_video_configs()->push_back( | 
|  | ChannelConfig(ChannelConfig::TRANSPORT_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_VP9)); | 
|  | result->mutable_video_configs()->push_back( | 
|  | ChannelConfig(ChannelConfig::TRANSPORT_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_VP8)); | 
|  |  | 
|  | // Audio channel. | 
|  | result->mutable_audio_configs()->push_back( | 
|  | ChannelConfig(ChannelConfig::TRANSPORT_MUX_STREAM, | 
|  | kDefaultStreamVersion, | 
|  | ChannelConfig::CODEC_OPUS)); | 
|  | result->mutable_audio_configs()->push_back(ChannelConfig::None()); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void CandidateSessionConfig::DisableAudioChannel() { | 
|  | mutable_audio_configs()->clear(); | 
|  | mutable_audio_configs()->push_back(ChannelConfig()); | 
|  | } | 
|  |  | 
|  | void CandidateSessionConfig::PreferTransport( | 
|  | ChannelConfig::TransportType transport) { | 
|  | UpdateConfigListToPreferTransport(&control_configs_, transport); | 
|  | UpdateConfigListToPreferTransport(&event_configs_, transport); | 
|  | UpdateConfigListToPreferTransport(&video_configs_, transport); | 
|  | UpdateConfigListToPreferTransport(&audio_configs_, transport); | 
|  | } | 
|  |  | 
|  | }  // namespace protocol | 
|  | }  // namespace remoting |