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/p2p/base/constants.h" 35 #include "talk/p2p/base/parsing.h" 36 #include "talk/session/media/mediasessionclient.h" 37 #include "webrtc/libjingle/xmllite/xmlelement.h" 38 #include "webrtc/base/logging.h" 39 #include "webrtc/base/stringencode.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 rtc::FromString(string, ssrc); 53 } 54 55 // Builds a <view> element according to the following spec: 56 // goto/jinglemuc 57 buzz::XmlElement* CreateViewElem(const std::string& name, 58 const std::string& type) { 59 buzz::XmlElement* view_elem = 60 new buzz::XmlElement(QN_JINGLE_DRAFT_VIEW, true); 61 view_elem->AddAttr(QN_NAME, name); 62 view_elem->SetAttr(QN_TYPE, type); 63 return view_elem; 64 } 65 66 buzz::XmlElement* CreateVideoViewElem(const std::string& content_name, 67 const std::string& type) { 68 return CreateViewElem(content_name, type); 69 } 70 71 buzz::XmlElement* CreateNoneVideoViewElem(const std::string& content_name) { 72 return CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_NONE); 73 } 74 75 buzz::XmlElement* CreateStaticVideoViewElem(const std::string& content_name, 76 const StaticVideoView& view) { 77 buzz::XmlElement* view_elem = 78 CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_STATIC); 79 AddXmlAttr(view_elem, QN_SSRC, view.selector.ssrc); 80 81 buzz::XmlElement* params_elem = new buzz::XmlElement(QN_JINGLE_DRAFT_PARAMS); 82 AddXmlAttr(params_elem, QN_WIDTH, view.width); 83 AddXmlAttr(params_elem, QN_HEIGHT, view.height); 84 AddXmlAttr(params_elem, QN_FRAMERATE, view.framerate); 85 AddXmlAttr(params_elem, QN_PREFERENCE, view.preference); 86 view_elem->AddElement(params_elem); 87 88 return view_elem; 89 } 90 91 } // namespace 92 93 bool MediaStreams::GetAudioStream( 94 const StreamSelector& selector, StreamParams* stream) { 95 return GetStream(audio_, selector, stream); 96 } 97 98 bool MediaStreams::GetVideoStream( 99 const StreamSelector& selector, StreamParams* stream) { 100 return GetStream(video_, selector, stream); 101 } 102 103 bool MediaStreams::GetDataStream( 104 const StreamSelector& selector, StreamParams* stream) { 105 return GetStream(data_, selector, stream); 106 } 107 108 void MediaStreams::CopyFrom(const MediaStreams& streams) { 109 audio_ = streams.audio_; 110 video_ = streams.video_; 111 data_ = streams.data_; 112 } 113 114 void MediaStreams::AddAudioStream(const StreamParams& stream) { 115 AddStream(&audio_, stream); 116 } 117 118 void MediaStreams::AddVideoStream(const StreamParams& stream) { 119 AddStream(&video_, stream); 120 } 121 122 void MediaStreams::AddDataStream(const StreamParams& stream) { 123 AddStream(&data_, stream); 124 } 125 126 bool MediaStreams::RemoveAudioStream( 127 const StreamSelector& selector) { 128 return RemoveStream(&audio_, selector); 129 } 130 131 bool MediaStreams::RemoveVideoStream( 132 const StreamSelector& selector) { 133 return RemoveStream(&video_, selector); 134 } 135 136 bool MediaStreams::RemoveDataStream( 137 const StreamSelector& selector) { 138 return RemoveStream(&data_, selector); 139 } 140 141 bool IsJingleViewRequest(const buzz::XmlElement* action_elem) { 142 return action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW) != NULL; 143 } 144 145 bool ParseStaticVideoView(const buzz::XmlElement* view_elem, 146 StaticVideoView* view, 147 ParseError* error) { 148 uint32 ssrc; 149 if (!ParseSsrc(view_elem->Attr(QN_SSRC), &ssrc)) { 150 return BadParse("Invalid or missing view ssrc.", error); 151 } 152 view->selector = StreamSelector(ssrc); 153 154 const buzz::XmlElement* params_elem = 155 view_elem->FirstNamed(QN_JINGLE_DRAFT_PARAMS); 156 if (params_elem) { 157 view->width = GetXmlAttr(params_elem, QN_WIDTH, 0); 158 view->height = GetXmlAttr(params_elem, QN_HEIGHT, 0); 159 view->framerate = GetXmlAttr(params_elem, QN_FRAMERATE, 0); 160 view->preference = GetXmlAttr(params_elem, QN_PREFERENCE, 0); 161 } else { 162 return BadParse("Missing view params.", error); 163 } 164 165 return true; 166 } 167 168 bool ParseJingleViewRequest(const buzz::XmlElement* action_elem, 169 ViewRequest* view_request, 170 ParseError* error) { 171 for (const buzz::XmlElement* view_elem = 172 action_elem->FirstNamed(QN_JINGLE_DRAFT_VIEW); 173 view_elem != NULL; 174 view_elem = view_elem->NextNamed(QN_JINGLE_DRAFT_VIEW)) { 175 std::string type = view_elem->Attr(QN_TYPE); 176 if (STR_JINGLE_DRAFT_VIEW_TYPE_NONE == type) { 177 view_request->static_video_views.clear(); 178 return true; 179 } else if (STR_JINGLE_DRAFT_VIEW_TYPE_STATIC == type) { 180 StaticVideoView static_video_view(StreamSelector(0), 0, 0, 0); 181 if (!ParseStaticVideoView(view_elem, &static_video_view, error)) { 182 return false; 183 } 184 view_request->static_video_views.push_back(static_video_view); 185 } else { 186 LOG(LS_INFO) << "Ingnoring unknown view type: " << type; 187 } 188 } 189 return true; 190 } 191 192 bool WriteJingleViewRequest(const std::string& content_name, 193 const ViewRequest& request, 194 XmlElements* elems, 195 WriteError* error) { 196 if (request.static_video_views.empty()) { 197 elems->push_back(CreateNoneVideoViewElem(content_name)); 198 } else { 199 for (StaticVideoViews::const_iterator view = 200 request.static_video_views.begin(); 201 view != request.static_video_views.end(); ++view) { 202 elems->push_back(CreateStaticVideoViewElem(content_name, *view)); 203 } 204 } 205 return true; 206 } 207 208 bool ParseSsrcAsLegacyStream(const buzz::XmlElement* desc_elem, 209 std::vector<StreamParams>* streams, 210 ParseError* error) { 211 const std::string ssrc_str = desc_elem->Attr(QN_SSRC); 212 if (!ssrc_str.empty()) { 213 uint32 ssrc; 214 if (!ParseSsrc(ssrc_str, &ssrc)) { 215 return BadParse("Missing or invalid ssrc.", error); 216 } 217 218 streams->push_back(StreamParams::CreateLegacy(ssrc)); 219 } 220 return true; 221 } 222 223 bool ParseSsrcs(const buzz::XmlElement* parent_elem, 224 std::vector<uint32>* ssrcs, 225 ParseError* error) { 226 for (const buzz::XmlElement* ssrc_elem = 227 parent_elem->FirstNamed(QN_JINGLE_DRAFT_SSRC); 228 ssrc_elem != NULL; 229 ssrc_elem = ssrc_elem->NextNamed(QN_JINGLE_DRAFT_SSRC)) { 230 uint32 ssrc; 231 if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) { 232 return BadParse("Missing or invalid ssrc.", error); 233 } 234 235 ssrcs->push_back(ssrc); 236 } 237 return true; 238 } 239 240 bool ParseSsrcGroups(const buzz::XmlElement* parent_elem, 241 std::vector<SsrcGroup>* ssrc_groups, 242 ParseError* error) { 243 for (const buzz::XmlElement* group_elem = 244 parent_elem->FirstNamed(QN_JINGLE_DRAFT_SSRC_GROUP); 245 group_elem != NULL; 246 group_elem = group_elem->NextNamed(QN_JINGLE_DRAFT_SSRC_GROUP)) { 247 std::string semantics = group_elem->Attr(QN_SEMANTICS); 248 std::vector<uint32> ssrcs; 249 if (!ParseSsrcs(group_elem, &ssrcs, error)) { 250 return false; 251 } 252 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs)); 253 } 254 return true; 255 } 256 257 bool ParseJingleStream(const buzz::XmlElement* stream_elem, 258 std::vector<StreamParams>* streams, 259 ParseError* error) { 260 StreamParams stream; 261 // We treat the nick as a stream groupid. 262 stream.groupid = stream_elem->Attr(QN_NICK); 263 stream.id = stream_elem->Attr(QN_NAME); 264 stream.type = stream_elem->Attr(QN_TYPE); 265 stream.display = stream_elem->Attr(QN_DISPLAY); 266 stream.cname = stream_elem->Attr(QN_CNAME); 267 if (!ParseSsrcs(stream_elem, &(stream.ssrcs), error)) { 268 return false; 269 } 270 std::vector<SsrcGroup> ssrc_groups; 271 if (!ParseSsrcGroups(stream_elem, &(stream.ssrc_groups), error)) { 272 return false; 273 } 274 streams->push_back(stream); 275 return true; 276 } 277 278 bool ParseJingleRtpHeaderExtensions(const buzz::XmlElement* parent_elem, 279 std::vector<RtpHeaderExtension>* hdrexts, 280 ParseError* error) { 281 for (const buzz::XmlElement* hdrext_elem = 282 parent_elem->FirstNamed(QN_JINGLE_RTP_HDREXT); 283 hdrext_elem != NULL; 284 hdrext_elem = hdrext_elem->NextNamed(QN_JINGLE_RTP_HDREXT)) { 285 std::string uri = hdrext_elem->Attr(QN_URI); 286 int id = GetXmlAttr(hdrext_elem, QN_ID, 0); 287 if (id <= 0) { 288 return BadParse("Invalid RTP header extension id.", error); 289 } 290 hdrexts->push_back(RtpHeaderExtension(uri, id)); 291 } 292 return true; 293 } 294 295 bool HasJingleStreams(const buzz::XmlElement* desc_elem) { 296 const buzz::XmlElement* streams_elem = 297 desc_elem->FirstNamed(QN_JINGLE_DRAFT_STREAMS); 298 return (streams_elem != NULL); 299 } 300 301 bool ParseJingleStreams(const buzz::XmlElement* desc_elem, 302 std::vector<StreamParams>* streams, 303 ParseError* error) { 304 const buzz::XmlElement* streams_elem = 305 desc_elem->FirstNamed(QN_JINGLE_DRAFT_STREAMS); 306 if (streams_elem == NULL) { 307 return BadParse("Missing streams element.", error); 308 } 309 for (const buzz::XmlElement* stream_elem = 310 streams_elem->FirstNamed(QN_JINGLE_DRAFT_STREAM); 311 stream_elem != NULL; 312 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_STREAM)) { 313 if (!ParseJingleStream(stream_elem, streams, error)) { 314 return false; 315 } 316 } 317 return true; 318 } 319 320 void WriteSsrcs(const std::vector<uint32>& ssrcs, 321 buzz::XmlElement* parent_elem) { 322 for (std::vector<uint32>::const_iterator ssrc = ssrcs.begin(); 323 ssrc != ssrcs.end(); ++ssrc) { 324 buzz::XmlElement* ssrc_elem = 325 new buzz::XmlElement(QN_JINGLE_DRAFT_SSRC, false); 326 SetXmlBody(ssrc_elem, *ssrc); 327 328 parent_elem->AddElement(ssrc_elem); 329 } 330 } 331 332 void WriteSsrcGroups(const std::vector<SsrcGroup>& groups, 333 buzz::XmlElement* parent_elem) { 334 for (std::vector<SsrcGroup>::const_iterator group = groups.begin(); 335 group != groups.end(); ++group) { 336 buzz::XmlElement* group_elem = 337 new buzz::XmlElement(QN_JINGLE_DRAFT_SSRC_GROUP, false); 338 AddXmlAttrIfNonEmpty(group_elem, QN_SEMANTICS, group->semantics); 339 WriteSsrcs(group->ssrcs, group_elem); 340 341 parent_elem->AddElement(group_elem); 342 } 343 } 344 345 void WriteJingleStream(const StreamParams& stream, 346 buzz::XmlElement* parent_elem) { 347 buzz::XmlElement* stream_elem = 348 new buzz::XmlElement(QN_JINGLE_DRAFT_STREAM, false); 349 // We treat the nick as a stream groupid. 350 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream.groupid); 351 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream.id); 352 AddXmlAttrIfNonEmpty(stream_elem, QN_TYPE, stream.type); 353 AddXmlAttrIfNonEmpty(stream_elem, QN_DISPLAY, stream.display); 354 AddXmlAttrIfNonEmpty(stream_elem, QN_CNAME, stream.cname); 355 WriteSsrcs(stream.ssrcs, stream_elem); 356 WriteSsrcGroups(stream.ssrc_groups, stream_elem); 357 358 parent_elem->AddElement(stream_elem); 359 } 360 361 void WriteJingleStreams(const std::vector<StreamParams>& streams, 362 buzz::XmlElement* parent_elem) { 363 buzz::XmlElement* streams_elem = 364 new buzz::XmlElement(QN_JINGLE_DRAFT_STREAMS, true); 365 for (std::vector<StreamParams>::const_iterator stream = streams.begin(); 366 stream != streams.end(); ++stream) { 367 WriteJingleStream(*stream, streams_elem); 368 } 369 370 parent_elem->AddElement(streams_elem); 371 } 372 373 void WriteJingleRtpHeaderExtensions( 374 const std::vector<RtpHeaderExtension>& hdrexts, 375 buzz::XmlElement* parent_elem) { 376 for (std::vector<RtpHeaderExtension>::const_iterator hdrext = hdrexts.begin(); 377 hdrext != hdrexts.end(); ++hdrext) { 378 buzz::XmlElement* hdrext_elem = 379 new buzz::XmlElement(QN_JINGLE_RTP_HDREXT, false); 380 AddXmlAttr(hdrext_elem, QN_URI, hdrext->uri); 381 AddXmlAttr(hdrext_elem, QN_ID, hdrext->id); 382 parent_elem->AddElement(hdrext_elem); 383 } 384 } 385 386 387 } // namespace cricket 388