Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / remoting / protocol / content_description.cc
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.
4
5 #include "remoting/protocol/content_description.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "remoting/base/constants.h"
10 #include "remoting/protocol/authenticator.h"
11 #include "remoting/protocol/name_value_map.h"
12 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
13
14 using buzz::QName;
15 using buzz::XmlElement;
16
17 namespace remoting {
18 namespace protocol {
19
20 const char ContentDescription::kChromotingContentName[] = "chromoting";
21
22 namespace {
23
24 const char kDefaultNs[] = "";
25
26 // Following constants are used to format session description in XML.
27 const char kDescriptionTag[] = "description";
28 const char kControlTag[] = "control";
29 const char kEventTag[] = "event";
30 const char kVideoTag[] = "video";
31 const char kAudioTag[] = "audio";
32 const char kDeprecatedResolutionTag[] = "initial-resolution";
33
34 const char kTransportAttr[] = "transport";
35 const char kVersionAttr[] = "version";
36 const char kCodecAttr[] = "codec";
37 const char kDeprecatedWidthAttr[] = "width";
38 const char kDeprecatedHeightAttr[] = "height";
39
40 const NameMapElement<ChannelConfig::TransportType> kTransports[] = {
41   { ChannelConfig::TRANSPORT_STREAM, "stream" },
42   { ChannelConfig::TRANSPORT_MUX_STREAM, "mux-stream" },
43   { ChannelConfig::TRANSPORT_DATAGRAM, "datagram" },
44   { ChannelConfig::TRANSPORT_NONE, "none" },
45 };
46
47 const NameMapElement<ChannelConfig::Codec> kCodecs[] = {
48   { ChannelConfig::CODEC_VERBATIM, "verbatim" },
49   { ChannelConfig::CODEC_VP8, "vp8" },
50   { ChannelConfig::CODEC_VP9, "vp9" },
51   { ChannelConfig::CODEC_ZIP, "zip" },
52   { ChannelConfig::CODEC_OPUS, "opus" },
53   { ChannelConfig::CODEC_SPEEX, "speex" },
54 };
55
56 // Format a channel configuration tag for chromotocol session description,
57 // e.g. for video channel:
58 //    <video transport="stream" version="1" codec="vp8" />
59 XmlElement* FormatChannelConfig(const ChannelConfig& config,
60                                 const std::string& tag_name) {
61   XmlElement* result = new XmlElement(
62       QName(kChromotingXmlNamespace, tag_name));
63
64   result->AddAttr(QName(kDefaultNs, kTransportAttr),
65                   ValueToName(kTransports, config.transport));
66
67   if (config.transport != ChannelConfig::TRANSPORT_NONE) {
68     result->AddAttr(QName(kDefaultNs, kVersionAttr),
69                     base::IntToString(config.version));
70
71     if (config.codec != ChannelConfig::CODEC_UNDEFINED) {
72       result->AddAttr(QName(kDefaultNs, kCodecAttr),
73                       ValueToName(kCodecs, config.codec));
74     }
75   }
76
77   return result;
78 }
79
80 // Returns false if the element is invalid.
81 bool ParseChannelConfig(const XmlElement* element, bool codec_required,
82                         ChannelConfig* config) {
83   if (!NameToValue(
84           kTransports, element->Attr(QName(kDefaultNs, kTransportAttr)),
85           &config->transport)) {
86     return false;
87   }
88
89   // Version is not required when transport="none".
90   if (config->transport != ChannelConfig::TRANSPORT_NONE) {
91     if (!base::StringToInt(element->Attr(QName(kDefaultNs, kVersionAttr)),
92                            &config->version)) {
93       return false;
94     }
95
96     // Codec is not required when transport="none".
97     if (codec_required) {
98       if (!NameToValue(kCodecs, element->Attr(QName(kDefaultNs, kCodecAttr)),
99                        &config->codec)) {
100         return false;
101       }
102     } else {
103       config->codec = ChannelConfig::CODEC_UNDEFINED;
104     }
105   } else {
106     config->version = 0;
107     config->codec = ChannelConfig::CODEC_UNDEFINED;
108   }
109
110   return true;
111 }
112
113 }  // namespace
114
115 ContentDescription::ContentDescription(
116     scoped_ptr<CandidateSessionConfig> config,
117     scoped_ptr<buzz::XmlElement> authenticator_message)
118     : candidate_config_(config.Pass()),
119       authenticator_message_(authenticator_message.Pass()) {
120 }
121
122 ContentDescription::~ContentDescription() { }
123
124 ContentDescription* ContentDescription::Copy() const {
125   if (!candidate_config_.get() || !authenticator_message_.get()) {
126     return NULL;
127   }
128   scoped_ptr<XmlElement> message(new XmlElement(*authenticator_message_));
129   return new ContentDescription(candidate_config_->Clone(), message.Pass());
130 }
131
132 // ToXml() creates content description for chromoting session. The
133 // description looks as follows:
134 //   <description xmlns="google:remoting">
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>
143 //
144 XmlElement* ContentDescription::ToXml() const {
145   XmlElement* root = new XmlElement(
146       QName(kChromotingXmlNamespace, kDescriptionTag), true);
147
148   std::list<ChannelConfig>::const_iterator it;
149
150   for (it = config()->control_configs().begin();
151        it != config()->control_configs().end(); ++it) {
152     root->AddElement(FormatChannelConfig(*it, kControlTag));
153   }
154
155   for (it = config()->event_configs().begin();
156        it != config()->event_configs().end(); ++it) {
157     root->AddElement(FormatChannelConfig(*it, kEventTag));
158   }
159
160   for (it = config()->video_configs().begin();
161        it != config()->video_configs().end(); ++it) {
162     root->AddElement(FormatChannelConfig(*it, kVideoTag));
163   }
164
165   for (it = config()->audio_configs().begin();
166        it != config()->audio_configs().end(); ++it) {
167     ChannelConfig config = *it;
168     root->AddElement(FormatChannelConfig(config, kAudioTag));
169   }
170
171   // Older endpoints require an initial-resolution tag, but otherwise ignore it.
172   XmlElement* resolution_tag = new XmlElement(
173       QName(kChromotingXmlNamespace, kDeprecatedResolutionTag));
174   resolution_tag->AddAttr(QName(kDefaultNs, kDeprecatedWidthAttr), "640");
175   resolution_tag->AddAttr(QName(kDefaultNs, kDeprecatedHeightAttr), "480");
176   root->AddElement(resolution_tag);
177
178   if (authenticator_message_.get()) {
179     DCHECK(Authenticator::IsAuthenticatorMessage(authenticator_message_.get()));
180     root->AddElement(new XmlElement(*authenticator_message_));
181   }
182
183   return root;
184 }
185
186 // static
187 // Adds the channel configs corresponding to |tag_name|,
188 // found in |element|, to |configs|.
189 bool ContentDescription::ParseChannelConfigs(
190     const XmlElement* const element,
191     const char tag_name[],
192     bool codec_required,
193     bool optional,
194     std::list<ChannelConfig>* const configs) {
195
196   QName tag(kChromotingXmlNamespace, tag_name);
197   const XmlElement* child = element->FirstNamed(tag);
198   while (child) {
199     ChannelConfig channel_config;
200     if (ParseChannelConfig(child, codec_required, &channel_config)) {
201       configs->push_back(channel_config);
202     }
203     child = child->NextNamed(tag);
204   }
205   if (optional && configs->empty()) {
206       // If there's no mention of the tag, implicitly assume disabled channel.
207       configs->push_back(ChannelConfig::None());
208   }
209   return true;
210 }
211
212 // static
213 scoped_ptr<ContentDescription> ContentDescription::ParseXml(
214     const XmlElement* element) {
215   if (element->Name() != QName(kChromotingXmlNamespace, kDescriptionTag)) {
216     LOG(ERROR) << "Invalid description: " << element->Str();
217     return scoped_ptr<ContentDescription>();
218   }
219   scoped_ptr<CandidateSessionConfig> config(
220       CandidateSessionConfig::CreateEmpty());
221   if (!ParseChannelConfigs(element, kControlTag, false, false,
222                            config->mutable_control_configs()) ||
223       !ParseChannelConfigs(element, kEventTag, false, false,
224                            config->mutable_event_configs()) ||
225       !ParseChannelConfigs(element, kVideoTag, true, false,
226                            config->mutable_video_configs()) ||
227       !ParseChannelConfigs(element, kAudioTag, true, true,
228                            config->mutable_audio_configs())) {
229     return scoped_ptr<ContentDescription>();
230   }
231
232   scoped_ptr<XmlElement> authenticator_message;
233   const XmlElement* child = Authenticator::FindAuthenticatorMessage(element);
234   if (child)
235     authenticator_message.reset(new XmlElement(*child));
236
237   return scoped_ptr<ContentDescription>(
238       new ContentDescription(config.Pass(), authenticator_message.Pass()));
239 }
240
241 }  // namespace protocol
242 }  // namespace remoting