MD Downloads: prevent search text from overlapping with the cancel search (X)
[chromium-blink-merge.git] / remoting / protocol / content_description.cc
blobccc10b813f60505a10a2dc4dd3275ceb9e84bf2d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/protocol/content_description.h"
7 #include "base/base64.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "remoting/base/constants.h"
11 #include "remoting/protocol/authenticator.h"
12 #include "remoting/protocol/name_value_map.h"
13 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
15 using buzz::QName;
16 using buzz::XmlElement;
18 namespace remoting {
19 namespace protocol {
21 const char ContentDescription::kChromotingContentName[] = "chromoting";
23 namespace {
25 const char kDefaultNs[] = "";
27 // Following constants are used to format session description in XML.
28 const char kDescriptionTag[] = "description";
29 const char kStandardIceTag[] = "standard-ice";
30 const char kControlTag[] = "control";
31 const char kEventTag[] = "event";
32 const char kVideoTag[] = "video";
33 const char kAudioTag[] = "audio";
34 const char kVp9ExperimentTag[] = "vp9-experiment";
35 const char kDeprecatedResolutionTag[] = "initial-resolution";
36 const char kQuicConfigTag[] = "quic-config";
38 const char kTransportAttr[] = "transport";
39 const char kVersionAttr[] = "version";
40 const char kCodecAttr[] = "codec";
41 const char kDeprecatedWidthAttr[] = "width";
42 const char kDeprecatedHeightAttr[] = "height";
44 const NameMapElement<ChannelConfig::TransportType> kTransports[] = {
45 { ChannelConfig::TRANSPORT_STREAM, "stream" },
46 { ChannelConfig::TRANSPORT_MUX_STREAM, "mux-stream" },
47 { ChannelConfig::TRANSPORT_QUIC_STREAM, "quic-stream" },
48 { ChannelConfig::TRANSPORT_DATAGRAM, "datagram" },
49 { ChannelConfig::TRANSPORT_NONE, "none" },
52 const NameMapElement<ChannelConfig::Codec> kCodecs[] = {
53 { ChannelConfig::CODEC_VERBATIM, "verbatim" },
54 { ChannelConfig::CODEC_VP8, "vp8" },
55 { ChannelConfig::CODEC_VP9, "vp9" },
56 { ChannelConfig::CODEC_ZIP, "zip" },
57 { ChannelConfig::CODEC_OPUS, "opus" },
58 { ChannelConfig::CODEC_SPEEX, "speex" },
61 // Format a channel configuration tag for chromotocol session description,
62 // e.g. for video channel:
63 // <video transport="stream" version="1" codec="vp8" />
64 XmlElement* FormatChannelConfig(const ChannelConfig& config,
65 const std::string& tag_name) {
66 XmlElement* result = new XmlElement(
67 QName(kChromotingXmlNamespace, tag_name));
69 result->AddAttr(QName(kDefaultNs, kTransportAttr),
70 ValueToName(kTransports, config.transport));
72 if (config.transport != ChannelConfig::TRANSPORT_NONE) {
73 result->AddAttr(QName(kDefaultNs, kVersionAttr),
74 base::IntToString(config.version));
76 if (config.codec != ChannelConfig::CODEC_UNDEFINED) {
77 result->AddAttr(QName(kDefaultNs, kCodecAttr),
78 ValueToName(kCodecs, config.codec));
82 return result;
85 // Returns false if the element is invalid.
86 bool ParseChannelConfig(const XmlElement* element, bool codec_required,
87 ChannelConfig* config) {
88 if (!NameToValue(
89 kTransports, element->Attr(QName(kDefaultNs, kTransportAttr)),
90 &config->transport)) {
91 return false;
94 // Version is not required when transport="none".
95 if (config->transport != ChannelConfig::TRANSPORT_NONE) {
96 if (!base::StringToInt(element->Attr(QName(kDefaultNs, kVersionAttr)),
97 &config->version)) {
98 return false;
101 // Codec is not required when transport="none".
102 if (codec_required) {
103 if (!NameToValue(kCodecs, element->Attr(QName(kDefaultNs, kCodecAttr)),
104 &config->codec)) {
105 return false;
107 } else {
108 config->codec = ChannelConfig::CODEC_UNDEFINED;
110 } else {
111 config->version = 0;
112 config->codec = ChannelConfig::CODEC_UNDEFINED;
115 return true;
118 } // namespace
120 ContentDescription::ContentDescription(
121 scoped_ptr<CandidateSessionConfig> config,
122 scoped_ptr<buzz::XmlElement> authenticator_message,
123 const std::string& quic_config_message)
124 : candidate_config_(config.Pass()),
125 authenticator_message_(authenticator_message.Pass()),
126 quic_config_message_(quic_config_message) {
129 ContentDescription::~ContentDescription() { }
131 // ToXml() creates content description for chromoting session. The
132 // description looks as follows:
133 // <description xmlns="google:remoting">
134 // <standard-ice/>
135 // <control transport="stream" version="1" />
136 // <event transport="datagram" version="1" />
137 // <video transport="stream" codec="vp8" version="1" />
138 // <audio transport="stream" codec="opus" version="1" />
139 // <authentication>
140 // Message created by Authenticator implementation.
141 // </authentication>
142 // </description>
144 XmlElement* ContentDescription::ToXml() const {
145 XmlElement* root = new XmlElement(
146 QName(kChromotingXmlNamespace, kDescriptionTag), true);
148 if (config()->standard_ice()) {
149 root->AddElement(
150 new buzz::XmlElement(QName(kChromotingXmlNamespace, kStandardIceTag)));
153 for (const ChannelConfig& channel_config : config()->control_configs()) {
154 root->AddElement(FormatChannelConfig(channel_config, kControlTag));
157 for (const ChannelConfig& channel_config : config()->event_configs()) {
158 root->AddElement(FormatChannelConfig(channel_config, kEventTag));
161 for (const ChannelConfig& channel_config : config()->video_configs()) {
162 root->AddElement(FormatChannelConfig(channel_config, kVideoTag));
165 for (const ChannelConfig& channel_config : config()->audio_configs()) {
166 root->AddElement(FormatChannelConfig(channel_config, kAudioTag));
169 // Older endpoints require an initial-resolution tag, but otherwise ignore it.
170 XmlElement* resolution_tag = new XmlElement(
171 QName(kChromotingXmlNamespace, kDeprecatedResolutionTag));
172 resolution_tag->AddAttr(QName(kDefaultNs, kDeprecatedWidthAttr), "640");
173 resolution_tag->AddAttr(QName(kDefaultNs, kDeprecatedHeightAttr), "480");
174 root->AddElement(resolution_tag);
176 if (authenticator_message_) {
177 DCHECK(Authenticator::IsAuthenticatorMessage(authenticator_message_.get()));
178 root->AddElement(new XmlElement(*authenticator_message_));
181 if (!quic_config_message_.empty()) {
182 XmlElement* quic_config_tag =
183 new XmlElement(QName(kChromotingXmlNamespace, kQuicConfigTag));
184 root->AddElement(quic_config_tag);
185 std::string config_base64;
186 base::Base64Encode(quic_config_message_, &config_base64);
187 quic_config_tag->SetBodyText(config_base64);
190 if (config()->vp9_experiment_enabled()) {
191 root->AddElement(
192 new XmlElement(QName(kChromotingXmlNamespace, kVp9ExperimentTag)));
195 return root;
198 // static
199 // Adds the channel configs corresponding to |tag_name|,
200 // found in |element|, to |configs|.
201 bool ContentDescription::ParseChannelConfigs(
202 const XmlElement* const element,
203 const char tag_name[],
204 bool codec_required,
205 bool optional,
206 std::list<ChannelConfig>* const configs) {
207 QName tag(kChromotingXmlNamespace, tag_name);
208 const XmlElement* child = element->FirstNamed(tag);
209 while (child) {
210 ChannelConfig channel_config;
211 if (ParseChannelConfig(child, codec_required, &channel_config)) {
212 configs->push_back(channel_config);
214 child = child->NextNamed(tag);
216 if (optional && configs->empty()) {
217 // If there's no mention of the tag, implicitly assume disabled channel.
218 configs->push_back(ChannelConfig::None());
220 return true;
223 // static
224 scoped_ptr<ContentDescription> ContentDescription::ParseXml(
225 const XmlElement* element) {
226 if (element->Name() != QName(kChromotingXmlNamespace, kDescriptionTag)) {
227 LOG(ERROR) << "Invalid description: " << element->Str();
228 return nullptr;
230 scoped_ptr<CandidateSessionConfig> config(
231 CandidateSessionConfig::CreateEmpty());
233 config->set_standard_ice(
234 element->FirstNamed(QName(kChromotingXmlNamespace, kStandardIceTag)) !=
235 nullptr);
237 if (!ParseChannelConfigs(element, kControlTag, false, false,
238 config->mutable_control_configs()) ||
239 !ParseChannelConfigs(element, kEventTag, false, false,
240 config->mutable_event_configs()) ||
241 !ParseChannelConfigs(element, kVideoTag, true, false,
242 config->mutable_video_configs()) ||
243 !ParseChannelConfigs(element, kAudioTag, true, true,
244 config->mutable_audio_configs())) {
245 return nullptr;
248 // Check if VP9 experiment is enabled.
249 if (element->FirstNamed(QName(kChromotingXmlNamespace, kVp9ExperimentTag))) {
250 config->set_vp9_experiment_enabled(true);
253 scoped_ptr<XmlElement> authenticator_message;
254 const XmlElement* child = Authenticator::FindAuthenticatorMessage(element);
255 if (child)
256 authenticator_message.reset(new XmlElement(*child));
258 std::string quic_config_message;
259 const XmlElement* quic_config_tag =
260 element->FirstNamed(QName(kChromotingXmlNamespace, kQuicConfigTag));
261 if (quic_config_tag) {
262 if (!base::Base64Decode(quic_config_tag->BodyText(),
263 &quic_config_message)) {
264 LOG(ERROR) << "Failed to parse QUIC config.";
265 return nullptr;
269 return make_scoped_ptr(new ContentDescription(
270 config.Pass(), authenticator_message.Pass(), quic_config_message));
273 } // namespace protocol
274 } // namespace remoting