1 /* 2 * libjingle 3 * Copyright 2010 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * Documentation is in mediamessages.h. 30 */ 31 32 #include "talk/session/media/mediamessages.h" 33 34 #include "talk/base/logging.h" 35 #include "talk/base/stringencode.h" 36 #include "talk/p2p/base/constants.h" 37 #include "talk/p2p/base/parsing.h" 38 #include "talk/session/media/mediasessionclient.h" 39 #include "talk/xmllite/xmlelement.h" 40 41 namespace cricket { 42 43 namespace { 44 45 // NOTE: There is no check here for duplicate streams, so check before 46 // adding. 47 void AddStream(std::vector<StreamParams>* streams, const StreamParams& stream) { 48 streams->push_back(stream); 49 } 50 51 bool ParseSsrc(const std::string& string, uint32* ssrc) { 52 return talk_base::FromString(string, ssrc); 53 } 54 55 bool ParseSsrc(const buzz::XmlElement* element, uint32* ssrc) { 56 if (element == NULL) { 57 return false; 58 } 59 return ParseSsrc(element->BodyText(), ssrc); 60 } 61 62 // Builds a <view> element according to the following spec: 63 // goto/jinglemuc 64 buzz::XmlElement* CreateViewElem(const std::string& name, 65 const std::string& type) { 66 buzz::XmlElement* view_elem = 67 new buzz::XmlElement(QN_JINGLE_DRAFT_VIEW, true); 68 view_elem->AddAttr(QN_NAME, name); 69 view_elem->SetAttr(QN_TYPE, type); 70 return view_elem; 71 } 72 73 buzz::XmlElement* CreateVideoViewElem(const std::string& content_name, 74 const std::string& type) { 75 return CreateViewElem(content_name, type); 76 } 77 78 buzz::XmlElement* CreateNoneVideoViewElem(const std::string& content_name) { 79 return CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_NONE); 80 } 81 82 buzz::XmlElement* CreateStaticVideoViewElem(const std::string& content_name, 83 const StaticVideoView& view) { 84 buzz::XmlElement* view_elem = 85 CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_STATIC); 86 AddXmlAttr(view_elem, QN_SSRC, view.selector.ssrc); 87 88 buzz::XmlElement* params_elem = new buzz::XmlElement(QN_JINGLE_DRAFT_PARAMS); 89 AddXmlAttr(params_elem, QN_WIDTH, view.width); 90 AddXmlAttr(params_elem, QN_HEIGHT, view.height); 91 AddXmlAttr(params_elem, QN_FRAMERATE, view.framerate); 92 AddXmlAttr(params_elem, QN_PREFERENCE, view.preference); 93 view_elem->AddElement(params_elem); 94 95 return view_elem; 96 } 97 98 } // namespace 99 100 bool MediaStreams::GetAudioStream( 101 const StreamSelector& selector, StreamParams* stream) { 102 return GetStream(audio_, selector, stream); 103 } 104 105 bool MediaStreams::GetVideoStream( 106 const StreamSelector& selector, StreamParams* stream) { 107 return GetStream(video_, selector, stream); 108 } 109 110 bool MediaStreams::GetDataStream( 111 const StreamSelector& selector, StreamParams* stream) { 112 return GetStream(data_, selector, stream); 113 } 114 115 void MediaStreams::CopyFrom(const MediaStreams& streams) { 116 audio_ = streams.audio_; 117 video_ = streams.video_; 118 data_ = streams.data_; 119 } 120 121 void MediaStreams::AddAudioStream(const StreamParams& stream) { 122 AddStream(&audio_, stream); 123 } 124 125 void MediaStreams::AddVideoStream(const StreamParams& stream) { 126 AddStream(&video_, stream); 127 } 128 129 void MediaStreams::AddDataStream(const StreamParams& stream) { 130 AddStream(&data_, stream); 131 } 132 133 bool MediaStreams::RemoveAudioStream( 134 const StreamSelector& selector) { 135 return RemoveStream(&audio_, selector); 136 } 137 138 bool MediaStreams::RemoveVideoStream( 139 const StreamSelector& selector) { 140 return RemoveStream(&video_, selector); 141 } 142 143 bool MediaStreams::RemoveDataStream( 144 const StreamSelector& selector) { 145 return RemoveStream(&data_, selector); 146 } 147 148 bool IsJingleViewRequest(const buzz::XmlElement* action_elem) { 149 return action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW) != NULL; 150 } 151 152 bool ParseStaticVideoView(const buzz::XmlElement* view_elem, 153 StaticVideoView* view, 154 ParseError* error) { 155 uint32 ssrc; 156 if (!ParseSsrc(view_elem->Attr(QN_SSRC), &ssrc)) { 157 return BadParse("Invalid or missing view ssrc.", error); 158 } 159 view->selector = StreamSelector(ssrc); 160 161 const buzz::XmlElement* params_elem = 162 view_elem->FirstNamed(QN_JINGLE_DRAFT_PARAMS); 163 if (params_elem) { 164 view->width = GetXmlAttr(params_elem, QN_WIDTH, 0); 165 view->height = GetXmlAttr(params_elem, QN_HEIGHT, 0); 166 view->framerate = GetXmlAttr(params_elem, QN_FRAMERATE, 0); 167 view->preference = GetXmlAttr(params_elem, QN_PREFERENCE, 0); 168 } else { 169 return BadParse("Missing view params.", error); 170 } 171 172 return true; 173 } 174 175 bool ParseJingleViewRequest(const buzz::XmlElement* action_elem, 176 ViewRequest* view_request, 177 ParseError* error) { 178 for (const buzz::XmlElement* view_elem = 179 action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW); 180 view_elem != NULL; 181 view_elem = view_elem->NextNamed(QN_JINGLE_DRAFT_VIEW)) { 182 std::string type = view_elem->Attr(QN_TYPE); 183 if (STR_JINGLE_DRAFT_VIEW_TYPE_NONE == type) { 184 view_request->static_video_views.clear(); 185 return true; 186 } else if (STR_JINGLE_DRAFT_VIEW_TYPE_STATIC == type) { 187 StaticVideoView static_video_view(StreamSelector(0), 0, 0, 0); 188 if (!ParseStaticVideoView(view_elem, &static_video_view, error)) { 189 return false; 190 } 191 view_request->static_video_views.push_back(static_video_view); 192 } else { 193 LOG(LS_INFO) << "Ingnoring unknown view type: " << type; 194 } 195 } 196 return true; 197 } 198 199 bool WriteJingleViewRequest(const std::string& content_name, 200 const ViewRequest& request, 201 XmlElements* elems, 202 WriteError* error) { 203 if (request.static_video_views.empty()) { 204 elems->push_back(CreateNoneVideoViewElem(content_name)); 205 } else { 206 for (StaticVideoViews::const_iterator view = 207 request.static_video_views.begin(); 208 view != request.static_video_views.end(); ++view) { 209 elems->push_back(CreateStaticVideoViewElem(content_name, *view)); 210 } 211 } 212 return true; 213 } 214 215 bool ParseSsrcAsLegacyStream(const buzz::XmlElement* desc_elem, 216 std::vector<StreamParams>* streams, 217 ParseError* error) { 218 const std::string ssrc_str = desc_elem->Attr(QN_SSRC); 219 if (!ssrc_str.empty()) { 220 uint32 ssrc; 221 if (!ParseSsrc(ssrc_str, &ssrc)) { 222 return BadParse("Missing or invalid ssrc.", error); 223 } 224 225 streams->push_back(StreamParams::CreateLegacy(ssrc)); 226 } 227 return true; 228 } 229 230 bool ParseSsrcs(const buzz::XmlElement* parent_elem, 231 std::vector<uint32>* ssrcs, 232 ParseError* error) { 233 for (const buzz::XmlElement* ssrc_elem = 234 parent_elem->FirstNamed(QN_JINGLE_DRAFT_SSRC); 235 ssrc_elem != NULL; 236 ssrc_elem = ssrc_elem->NextNamed(QN_JINGLE_DRAFT_SSRC)) { 237 uint32 ssrc; 238 if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) { 239 return BadParse("Missing or invalid ssrc.", error); 240 } 241 242 ssrcs->push_back(ssrc); 243 } 244 return true; 245 } 246 247 bool ParseSsrcGroups(const buzz::XmlElement* parent_elem, 248 std::vector<SsrcGroup>* ssrc_groups, 249 ParseError* error) { 250 for (const buzz::XmlElement* group_elem = 251 parent_elem->FirstNamed(QN_JINGLE_DRAFT_SSRC_GROUP); 252 group_elem != NULL; 253 group_elem = group_elem->NextNamed(QN_JINGLE_DRAFT_SSRC_GROUP)) { 254 std::string semantics = group_elem->Attr(QN_SEMANTICS); 255 std::vector<uint32> ssrcs; 256 if (!ParseSsrcs(group_elem, &ssrcs, error)) { 257 return false; 258 } 259 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs)); 260 } 261 return true; 262 } 263 264 bool ParseJingleStream(const buzz::XmlElement* stream_elem, 265 std::vector<StreamParams>* streams, 266 ParseError* error) { 267 StreamParams stream; 268 // We treat the nick as a stream groupid. 269 stream.groupid = stream_elem->Attr(QN_NICK); 270 stream.id = stream_elem->Attr(QN_NAME); 271 stream.type = stream_elem->Attr(QN_TYPE); 272 stream.display = stream_elem->Attr(QN_DISPLAY); 273 stream.cname = stream_elem->Attr(QN_CNAME); 274 if (!ParseSsrcs(stream_elem, &(stream.ssrcs), error)) { 275 return false; 276 } 277 std::vector<SsrcGroup> ssrc_groups; 278 if (!ParseSsrcGroups(stream_elem, &(stream.ssrc_groups), error)) { 279 return false; 280 } 281 streams->push_back(stream); 282 return true; 283 } 284 285 bool ParseJingleRtpHeaderExtensions(const buzz::XmlElement* parent_elem, 286 std::vector<RtpHeaderExtension>* hdrexts, 287 ParseError* error) { 288 for (const buzz::XmlElement* hdrext_elem = 289 parent_elem->FirstNamed(QN_JINGLE_RTP_HDREXT); 290 hdrext_elem != NULL; 291 hdrext_elem = hdrext_elem->NextNamed(QN_JINGLE_RTP_HDREXT)) { 292 std::string uri = hdrext_elem->Attr(QN_URI); 293 int id = GetXmlAttr(hdrext_elem, QN_ID, 0); 294 if (id <= 0) { 295 return BadParse("Invalid RTP header extension id.", error); 296 } 297 hdrexts->push_back(RtpHeaderExtension(uri, id)); 298 } 299 return true; 300 } 301 302 bool HasJingleStreams(const buzz::XmlElement* desc_elem) { 303 const buzz::XmlElement* streams_elem = 304 desc_elem->FirstNamed(QN_JINGLE_DRAFT_STREAMS); 305 return (streams_elem != NULL); 306 } 307 308 bool ParseJingleStreams(const buzz::XmlElement* desc_elem, 309 std::vector<StreamParams>* streams, 310 ParseError* error) { 311 const buzz::XmlElement* streams_elem = 312 desc_elem->FirstNamed(QN_JINGLE_DRAFT_STREAMS); 313 if (streams_elem == NULL) { 314 return BadParse("Missing streams element.", error); 315 } 316 for (const buzz::XmlElement* stream_elem = 317 streams_elem->FirstNamed(QN_JINGLE_DRAFT_STREAM); 318 stream_elem != NULL; 319 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_STREAM)) { 320 if (!ParseJingleStream(stream_elem, streams, error)) { 321 return false; 322 } 323 } 324 return true; 325 } 326 327 void WriteSsrcs(const std::vector<uint32>& ssrcs, 328 buzz::XmlElement* parent_elem) { 329 for (std::vector<uint32>::const_iterator ssrc = ssrcs.begin(); 330 ssrc != ssrcs.end(); ++ssrc) { 331 buzz::XmlElement* ssrc_elem = 332 new buzz::XmlElement(QN_JINGLE_DRAFT_SSRC, false); 333 SetXmlBody(ssrc_elem, *ssrc); 334 335 parent_elem->AddElement(ssrc_elem); 336 } 337 } 338 339 void WriteSsrcGroups(const std::vector<SsrcGroup>& groups, 340 buzz::XmlElement* parent_elem) { 341 for (std::vector<SsrcGroup>::const_iterator group = groups.begin(); 342 group != groups.end(); ++group) { 343 buzz::XmlElement* group_elem = 344 new buzz::XmlElement(QN_JINGLE_DRAFT_SSRC_GROUP, false); 345 AddXmlAttrIfNonEmpty(group_elem, QN_SEMANTICS, group->semantics); 346 WriteSsrcs(group->ssrcs, group_elem); 347 348 parent_elem->AddElement(group_elem); 349 } 350 } 351 352 void WriteJingleStream(const StreamParams& stream, 353 buzz::XmlElement* parent_elem) { 354 buzz::XmlElement* stream_elem = 355 new buzz::XmlElement(QN_JINGLE_DRAFT_STREAM, false); 356 // We treat the nick as a stream groupid. 357 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream.groupid); 358 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream.id); 359 AddXmlAttrIfNonEmpty(stream_elem, QN_TYPE, stream.type); 360 AddXmlAttrIfNonEmpty(stream_elem, QN_DISPLAY, stream.display); 361 AddXmlAttrIfNonEmpty(stream_elem, QN_CNAME, stream.cname); 362 WriteSsrcs(stream.ssrcs, stream_elem); 363 WriteSsrcGroups(stream.ssrc_groups, stream_elem); 364 365 parent_elem->AddElement(stream_elem); 366 } 367 368 void WriteJingleStreams(const std::vector<StreamParams>& streams, 369 buzz::XmlElement* parent_elem) { 370 buzz::XmlElement* streams_elem = 371 new buzz::XmlElement(QN_JINGLE_DRAFT_STREAMS, true); 372 for (std::vector<StreamParams>::const_iterator stream = streams.begin(); 373 stream != streams.end(); ++stream) { 374 WriteJingleStream(*stream, streams_elem); 375 } 376 377 parent_elem->AddElement(streams_elem); 378 } 379 380 void WriteJingleRtpHeaderExtensions( 381 const std::vector<RtpHeaderExtension>& hdrexts, 382 buzz::XmlElement* parent_elem) { 383 for (std::vector<RtpHeaderExtension>::const_iterator hdrext = hdrexts.begin(); 384 hdrext != hdrexts.end(); ++hdrext) { 385 buzz::XmlElement* hdrext_elem = 386 new buzz::XmlElement(QN_JINGLE_RTP_HDREXT, false); 387 AddXmlAttr(hdrext_elem, QN_URI, hdrext->uri); 388 AddXmlAttr(hdrext_elem, QN_ID, hdrext->id); 389 parent_elem->AddElement(hdrext_elem); 390 } 391 } 392 393 394 } // namespace cricket 395