| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2004 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 #include <string> | |
| 29 | |
| 30 #include "webrtc/libjingle/session/media/mediasessionclient.h" | |
| 31 | |
| 32 #include "talk/media/base/capturemanager.h" | |
| 33 #include "talk/media/base/cryptoparams.h" | |
| 34 #include "talk/media/sctp/sctpdataengine.h" | |
| 35 #include "talk/session/media/srtpfilter.h" | |
| 36 #include "webrtc/base/helpers.h" | |
| 37 #include "webrtc/base/logging.h" | |
| 38 #include "webrtc/base/stringencode.h" | |
| 39 #include "webrtc/base/stringutils.h" | |
| 40 #include "webrtc/libjingle/session/media/mediamessages.h" | |
| 41 #include "webrtc/libjingle/xmllite/qname.h" | |
| 42 #include "webrtc/libjingle/xmllite/xmlconstants.h" | |
| 43 #include "webrtc/libjingle/xmpp/constants.h" | |
| 44 #include "webrtc/p2p/base/constants.h" | |
| 45 | |
| 46 namespace cricket { | |
| 47 | |
| 48 #if !defined(DISABLE_MEDIA_ENGINE_FACTORY) | |
| 49 MediaSessionClient::MediaSessionClient( | |
| 50 const buzz::Jid& jid, SessionManager *manager) | |
| 51 : jid_(jid), | |
| 52 session_manager_(manager), | |
| 53 focus_call_(NULL), | |
| 54 channel_manager_(new ChannelManager(session_manager_->worker_thread())), | |
| 55 desc_factory_(channel_manager_, | |
| 56 session_manager_->transport_desc_factory()), | |
| 57 multisession_enabled_(false) { | |
| 58 Construct(); | |
| 59 } | |
| 60 #endif | |
| 61 | |
| 62 MediaSessionClient::MediaSessionClient( | |
| 63 const buzz::Jid& jid, SessionManager *manager, | |
| 64 MediaEngineInterface* media_engine, | |
| 65 DataEngineInterface* data_media_engine, | |
| 66 DeviceManagerInterface* device_manager) | |
| 67 : jid_(jid), | |
| 68 session_manager_(manager), | |
| 69 focus_call_(NULL), | |
| 70 channel_manager_(new ChannelManager( | |
| 71 media_engine, data_media_engine, | |
| 72 device_manager, new CaptureManager(), | |
| 73 session_manager_->worker_thread())), | |
| 74 desc_factory_(channel_manager_, | |
| 75 session_manager_->transport_desc_factory()), | |
| 76 multisession_enabled_(false) { | |
| 77 Construct(); | |
| 78 } | |
| 79 | |
| 80 void MediaSessionClient::Construct() { | |
| 81 // Register ourselves as the handler of audio and video sessions. | |
| 82 session_manager_->AddClient(NS_JINGLE_RTP, this); | |
| 83 // Forward device notifications. | |
| 84 SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange); | |
| 85 // Bring up the channel manager. | |
| 86 // In previous versions of ChannelManager, this was done automatically | |
| 87 // in the constructor. | |
| 88 channel_manager_->Init(); | |
| 89 } | |
| 90 | |
| 91 MediaSessionClient::~MediaSessionClient() { | |
| 92 // Destroy all calls | |
| 93 std::map<uint32, Call *>::iterator it; | |
| 94 while (calls_.begin() != calls_.end()) { | |
| 95 std::map<uint32, Call *>::iterator it = calls_.begin(); | |
| 96 DestroyCall((*it).second); | |
| 97 } | |
| 98 | |
| 99 // Delete channel manager. This will wait for the channels to exit | |
| 100 delete channel_manager_; | |
| 101 | |
| 102 // Remove ourselves from the client map. | |
| 103 session_manager_->RemoveClient(NS_JINGLE_RTP); | |
| 104 } | |
| 105 | |
| 106 Call *MediaSessionClient::CreateCall() { | |
| 107 Call *call = new Call(this); | |
| 108 calls_[call->id()] = call; | |
| 109 SignalCallCreate(call); | |
| 110 return call; | |
| 111 } | |
| 112 | |
| 113 void MediaSessionClient::OnSessionCreate(Session *session, | |
| 114 bool received_initiate) { | |
| 115 if (received_initiate) { | |
| 116 session->SignalState.connect(this, &MediaSessionClient::OnSessionState); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 void MediaSessionClient::OnSessionState(BaseSession* base_session, | |
| 121 BaseSession::State state) { | |
| 122 // MediaSessionClient can only be used with a Session*, so it's | |
| 123 // safe to cast here. | |
| 124 Session* session = static_cast<Session*>(base_session); | |
| 125 | |
| 126 if (state == Session::STATE_RECEIVEDINITIATE) { | |
| 127 // The creation of the call must happen after the session has | |
| 128 // processed the initiate message because we need the | |
| 129 // remote_description to know what content names to use in the | |
| 130 // call. | |
| 131 | |
| 132 // If our accept would have no codecs, then we must reject this call. | |
| 133 const SessionDescription* offer = session->remote_description(); | |
| 134 const SessionDescription* accept = CreateAnswer(offer, CallOptions()); | |
| 135 const ContentInfo* audio_content = GetFirstAudioContent(accept); | |
| 136 bool audio_rejected = (!audio_content) ? true : audio_content->rejected; | |
| 137 const AudioContentDescription* audio_desc = (!audio_content) ? NULL : | |
| 138 static_cast<const AudioContentDescription*>(audio_content->description); | |
| 139 | |
| 140 // For some reason, we need a call even if we reject. So, either find a | |
| 141 // matching call or create a new one. | |
| 142 // The matching of existing calls is used to support the multi-session mode | |
| 143 // required for p2p handoffs: ie. once a MUC call is established, a new | |
| 144 // session may be established for the same call but is direct between the | |
| 145 // clients. To indicate that this is the case, the initiator of the incoming | |
| 146 // session is set to be the same as the remote name of the MUC for the | |
| 147 // existing session, thus the client can know that this is a new session for | |
| 148 // the existing call, rather than a whole new call. | |
| 149 Call* call = NULL; | |
| 150 if (multisession_enabled_) { | |
| 151 call = FindCallByRemoteName(session->initiator_name()); | |
| 152 } | |
| 153 | |
| 154 if (call == NULL) { | |
| 155 // Could not find a matching call, so create a new one. | |
| 156 call = CreateCall(); | |
| 157 } | |
| 158 | |
| 159 session_map_[session->id()] = call; | |
| 160 call->IncomingSession(session, offer); | |
| 161 | |
| 162 if (audio_rejected || !audio_desc || audio_desc->codecs().size() == 0) { | |
| 163 session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS); | |
| 164 } | |
| 165 delete accept; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void MediaSessionClient::DestroyCall(Call *call) { | |
| 170 // Change focus away, signal destruction | |
| 171 | |
| 172 if (call == focus_call_) | |
| 173 SetFocus(NULL); | |
| 174 SignalCallDestroy(call); | |
| 175 | |
| 176 // Remove it from calls_ map and delete | |
| 177 | |
| 178 std::map<uint32, Call *>::iterator it = calls_.find(call->id()); | |
| 179 if (it != calls_.end()) | |
| 180 calls_.erase(it); | |
| 181 | |
| 182 delete call; | |
| 183 } | |
| 184 | |
| 185 void MediaSessionClient::OnSessionDestroy(Session *session) { | |
| 186 // Find the call this session is in, remove it | |
| 187 SessionMap::iterator it = session_map_.find(session->id()); | |
| 188 ASSERT(it != session_map_.end()); | |
| 189 if (it != session_map_.end()) { | |
| 190 Call *call = (*it).second; | |
| 191 session_map_.erase(it); | |
| 192 call->RemoveSession(session); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 Call *MediaSessionClient::GetFocus() { | |
| 197 return focus_call_; | |
| 198 } | |
| 199 | |
| 200 void MediaSessionClient::SetFocus(Call *call) { | |
| 201 Call *old_focus_call = focus_call_; | |
| 202 if (focus_call_ != call) { | |
| 203 if (focus_call_ != NULL) | |
| 204 focus_call_->EnableChannels(false); | |
| 205 focus_call_ = call; | |
| 206 if (focus_call_ != NULL) | |
| 207 focus_call_->EnableChannels(true); | |
| 208 SignalFocus(focus_call_, old_focus_call); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) { | |
| 213 // Move all sessions from call to call_to_join, delete call. | |
| 214 // If call_to_join has focus, added sessions should have enabled channels. | |
| 215 | |
| 216 if (focus_call_ == call) | |
| 217 SetFocus(NULL); | |
| 218 call_to_join->Join(call, focus_call_ == call_to_join); | |
| 219 DestroyCall(call); | |
| 220 } | |
| 221 | |
| 222 Session *MediaSessionClient::CreateSession(Call *call) { | |
| 223 std::string id; | |
| 224 return CreateSession(id, call); | |
| 225 } | |
| 226 | |
| 227 Session *MediaSessionClient::CreateSession(const std::string& id, Call* call) { | |
| 228 const std::string& type = NS_JINGLE_RTP; | |
| 229 Session *session = session_manager_->CreateSession(id, jid().Str(), type); | |
| 230 session_map_[session->id()] = call; | |
| 231 return session; | |
| 232 } | |
| 233 | |
| 234 Call *MediaSessionClient::FindCallByRemoteName(const std::string &remote_name) { | |
| 235 SessionMap::const_iterator call; | |
| 236 for (call = session_map_.begin(); call != session_map_.end(); ++call) { | |
| 237 std::vector<Session *> sessions = call->second->sessions(); | |
| 238 std::vector<Session *>::const_iterator session; | |
| 239 for (session = sessions.begin(); session != sessions.end(); ++session) { | |
| 240 if (remote_name == (*session)->remote_name()) { | |
| 241 return call->second; | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 return NULL; | |
| 247 } | |
| 248 | |
| 249 // TODO(pthatcher): Move all of the parsing and writing functions into | |
| 250 // mediamessages.cc, with unit tests. | |
| 251 bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) { | |
| 252 int id = GetXmlAttr(element, QN_ID, -1); | |
| 253 if (id < 0) | |
| 254 return false; | |
| 255 | |
| 256 std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY); | |
| 257 int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0); | |
| 258 int bitrate = GetXmlAttr(element, QN_BITRATE, 0); | |
| 259 int channels = GetXmlAttr(element, QN_CHANNELS, 1); | |
| 260 *out = AudioCodec(id, name, clockrate, bitrate, channels, 0); | |
| 261 return true; | |
| 262 } | |
| 263 | |
| 264 bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) { | |
| 265 int id = GetXmlAttr(element, QN_ID, -1); | |
| 266 if (id < 0) | |
| 267 return false; | |
| 268 | |
| 269 std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY); | |
| 270 int width = GetXmlAttr(element, QN_WIDTH, 0); | |
| 271 int height = GetXmlAttr(element, QN_HEIGHT, 0); | |
| 272 int framerate = GetXmlAttr(element, QN_FRAMERATE, 0); | |
| 273 | |
| 274 *out = VideoCodec(id, name, width, height, framerate, 0); | |
| 275 return true; | |
| 276 } | |
| 277 | |
| 278 // Parses an ssrc string as a legacy stream. If it fails, returns | |
| 279 // false and fills an error message. | |
| 280 bool ParseSsrcAsLegacyStream(const std::string& ssrc_str, | |
| 281 std::vector<StreamParams>* streams, | |
| 282 ParseError* error) { | |
| 283 if (!ssrc_str.empty()) { | |
| 284 uint32 ssrc; | |
| 285 if (!rtc::FromString(ssrc_str, &ssrc)) { | |
| 286 return BadParse("Missing or invalid ssrc.", error); | |
| 287 } | |
| 288 | |
| 289 streams->push_back(StreamParams::CreateLegacy(ssrc)); | |
| 290 } | |
| 291 return true; | |
| 292 } | |
| 293 | |
| 294 void ParseGingleSsrc(const buzz::XmlElement* parent_elem, | |
| 295 const buzz::QName& name, | |
| 296 MediaContentDescription* media) { | |
| 297 const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name); | |
| 298 if (ssrc_elem) { | |
| 299 ParseError error; | |
| 300 ParseSsrcAsLegacyStream( | |
| 301 ssrc_elem->BodyText(), &(media->mutable_streams()), &error); | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 bool ParseCryptoParams(const buzz::XmlElement* element, | |
| 306 CryptoParams* out, | |
| 307 ParseError* error) { | |
| 308 if (!element->HasAttr(QN_CRYPTO_SUITE)) { | |
| 309 return BadParse("crypto: crypto-suite attribute missing ", error); | |
| 310 } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) { | |
| 311 return BadParse("crypto: key-params attribute missing ", error); | |
| 312 } else if (!element->HasAttr(QN_CRYPTO_TAG)) { | |
| 313 return BadParse("crypto: tag attribute missing ", error); | |
| 314 } | |
| 315 | |
| 316 const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE); | |
| 317 const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS); | |
| 318 const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0); | |
| 319 const std::string& session_params = | |
| 320 element->Attr(QN_CRYPTO_SESSION_PARAMS); // Optional. | |
| 321 | |
| 322 *out = CryptoParams(tag, crypto_suite, key_params, session_params); | |
| 323 return true; | |
| 324 } | |
| 325 | |
| 326 | |
| 327 // Parse the first encryption element found with a matching 'usage' | |
| 328 // element. | |
| 329 // <usage/> is specific to Gingle. In Jingle, <crypto/> is already | |
| 330 // scoped to a content. | |
| 331 // Return false if there was an encryption element and it could not be | |
| 332 // parsed. | |
| 333 bool ParseGingleEncryption(const buzz::XmlElement* desc, | |
| 334 const buzz::QName& usage, | |
| 335 MediaContentDescription* media, | |
| 336 ParseError* error) { | |
| 337 for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION); | |
| 338 encryption != NULL; | |
| 339 encryption = encryption->NextNamed(QN_ENCRYPTION)) { | |
| 340 if (encryption->FirstNamed(usage) != NULL) { | |
| 341 if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { | |
| 342 media->set_crypto_required(CT_SDES); | |
| 343 } | |
| 344 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); | |
| 345 crypto != NULL; | |
| 346 crypto = crypto->NextNamed(QN_CRYPTO)) { | |
| 347 CryptoParams params; | |
| 348 if (!ParseCryptoParams(crypto, ¶ms, error)) { | |
| 349 return false; | |
| 350 } | |
| 351 media->AddCrypto(params); | |
| 352 } | |
| 353 break; | |
| 354 } | |
| 355 } | |
| 356 return true; | |
| 357 } | |
| 358 | |
| 359 void ParseBandwidth(const buzz::XmlElement* parent_elem, | |
| 360 MediaContentDescription* media) { | |
| 361 const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH); | |
| 362 int bandwidth_kbps = -1; | |
| 363 if (bw_elem && rtc::FromString(bw_elem->BodyText(), &bandwidth_kbps)) { | |
| 364 if (bandwidth_kbps >= 0) { | |
| 365 media->set_bandwidth(bandwidth_kbps * 1000); | |
| 366 } | |
| 367 } | |
| 368 } | |
| 369 | |
| 370 bool ParseGingleAudioContent(const buzz::XmlElement* content_elem, | |
| 371 ContentDescription** content, | |
| 372 ParseError* error) { | |
| 373 AudioContentDescription* audio = new AudioContentDescription(); | |
| 374 | |
| 375 int preference = kMaxPayloadId; | |
| 376 if (content_elem->FirstElement()) { | |
| 377 for (const buzz::XmlElement* codec_elem = | |
| 378 content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE); | |
| 379 codec_elem != NULL; | |
| 380 codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) { | |
| 381 AudioCodec codec; | |
| 382 if (ParseGingleAudioCodec(codec_elem, &codec)) { | |
| 383 codec.preference = preference--; | |
| 384 audio->AddCodec(codec); | |
| 385 } | |
| 386 } | |
| 387 } else { | |
| 388 // For backward compatibility, we can assume the other client is | |
| 389 // an old version of Talk if it has no audio payload types at all. | |
| 390 audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1)); | |
| 391 audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0)); | |
| 392 } | |
| 393 | |
| 394 ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio); | |
| 395 | |
| 396 if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE, | |
| 397 audio, error)) { | |
| 398 return false; | |
| 399 } | |
| 400 | |
| 401 *content = audio; | |
| 402 return true; | |
| 403 } | |
| 404 | |
| 405 bool ParseGingleVideoContent(const buzz::XmlElement* content_elem, | |
| 406 ContentDescription** content, | |
| 407 ParseError* error) { | |
| 408 VideoContentDescription* video = new VideoContentDescription(); | |
| 409 | |
| 410 int preference = kMaxPayloadId; | |
| 411 for (const buzz::XmlElement* codec_elem = | |
| 412 content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE); | |
| 413 codec_elem != NULL; | |
| 414 codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) { | |
| 415 VideoCodec codec; | |
| 416 if (ParseGingleVideoCodec(codec_elem, &codec)) { | |
| 417 codec.preference = preference--; | |
| 418 video->AddCodec(codec); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video); | |
| 423 ParseBandwidth(content_elem, video); | |
| 424 | |
| 425 if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE, | |
| 426 video, error)) { | |
| 427 return false; | |
| 428 } | |
| 429 | |
| 430 *content = video; | |
| 431 return true; | |
| 432 } | |
| 433 | |
| 434 void ParsePayloadTypeParameters(const buzz::XmlElement* element, | |
| 435 std::map<std::string, std::string>* paramap) { | |
| 436 for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER); | |
| 437 param != NULL; param = param->NextNamed(QN_PARAMETER)) { | |
| 438 std::string name = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME, | |
| 439 buzz::STR_EMPTY); | |
| 440 std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE, | |
| 441 buzz::STR_EMPTY); | |
| 442 if (!name.empty() && !value.empty()) { | |
| 443 paramap->insert(make_pair(name, value)); | |
| 444 } | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 void ParseFeedbackParams(const buzz::XmlElement* element, | |
| 449 FeedbackParams* params) { | |
| 450 for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB); | |
| 451 param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) { | |
| 452 std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY); | |
| 453 std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY); | |
| 454 if (!type.empty()) { | |
| 455 params->Add(FeedbackParam(type, subtype)); | |
| 456 } | |
| 457 } | |
| 458 } | |
| 459 | |
| 460 void AddFeedbackParams(const FeedbackParams& additional_params, | |
| 461 FeedbackParams* params) { | |
| 462 for (size_t i = 0; i < additional_params.params().size(); ++i) { | |
| 463 params->Add(additional_params.params()[i]); | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 int FindWithDefault(const std::map<std::string, std::string>& map, | |
| 468 const std::string& key, const int def) { | |
| 469 std::map<std::string, std::string>::const_iterator iter = map.find(key); | |
| 470 return (iter == map.end()) ? def : atoi(iter->second.c_str()); | |
| 471 } | |
| 472 | |
| 473 | |
| 474 // Parse the first encryption element found. | |
| 475 // Return false if there was an encryption element and it could not be | |
| 476 // parsed. | |
| 477 bool ParseJingleEncryption(const buzz::XmlElement* content_elem, | |
| 478 MediaContentDescription* media, | |
| 479 ParseError* error) { | |
| 480 const buzz::XmlElement* encryption = | |
| 481 content_elem->FirstNamed(QN_ENCRYPTION); | |
| 482 if (encryption == NULL) { | |
| 483 return true; | |
| 484 } | |
| 485 | |
| 486 if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { | |
| 487 media->set_crypto_required(CT_SDES); | |
| 488 } | |
| 489 | |
| 490 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); | |
| 491 crypto != NULL; | |
| 492 crypto = crypto->NextNamed(QN_CRYPTO)) { | |
| 493 CryptoParams params; | |
| 494 if (!ParseCryptoParams(crypto, ¶ms, error)) { | |
| 495 return false; | |
| 496 } | |
| 497 media->AddCrypto(params); | |
| 498 } | |
| 499 return true; | |
| 500 } | |
| 501 | |
| 502 bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) { | |
| 503 int id = GetXmlAttr(elem, QN_ID, -1); | |
| 504 if (id < 0) | |
| 505 return false; | |
| 506 | |
| 507 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); | |
| 508 int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0); | |
| 509 int channels = GetXmlAttr(elem, QN_CHANNELS, 1); | |
| 510 | |
| 511 std::map<std::string, std::string> paramap; | |
| 512 ParsePayloadTypeParameters(elem, ¶map); | |
| 513 int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0); | |
| 514 | |
| 515 *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0); | |
| 516 ParseFeedbackParams(elem, &codec->feedback_params); | |
| 517 return true; | |
| 518 } | |
| 519 | |
| 520 bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) { | |
| 521 int id = GetXmlAttr(elem, QN_ID, -1); | |
| 522 if (id < 0) | |
| 523 return false; | |
| 524 | |
| 525 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); | |
| 526 | |
| 527 std::map<std::string, std::string> paramap; | |
| 528 ParsePayloadTypeParameters(elem, ¶map); | |
| 529 int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0); | |
| 530 int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0); | |
| 531 int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0); | |
| 532 | |
| 533 *codec = VideoCodec(id, name, width, height, framerate, 0); | |
| 534 codec->params = paramap; | |
| 535 ParseFeedbackParams(elem, &codec->feedback_params); | |
| 536 return true; | |
| 537 } | |
| 538 | |
| 539 bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) { | |
| 540 int id = GetXmlAttr(elem, QN_ID, -1); | |
| 541 if (id < 0) | |
| 542 return false; | |
| 543 | |
| 544 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY); | |
| 545 | |
| 546 *codec = DataCodec(id, name, 0); | |
| 547 ParseFeedbackParams(elem, &codec->feedback_params); | |
| 548 return true; | |
| 549 } | |
| 550 | |
| 551 bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem, | |
| 552 MediaContentDescription* media, | |
| 553 ParseError* error) { | |
| 554 if (HasJingleStreams(desc_elem)) { | |
| 555 if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) { | |
| 556 return false; | |
| 557 } | |
| 558 } else { | |
| 559 const std::string ssrc_str = desc_elem->Attr(QN_SSRC); | |
| 560 if (!ParseSsrcAsLegacyStream( | |
| 561 ssrc_str, &(media->mutable_streams()), error)) { | |
| 562 return false; | |
| 563 } | |
| 564 } | |
| 565 return true; | |
| 566 } | |
| 567 | |
| 568 bool ParseJingleAudioContent(const buzz::XmlElement* content_elem, | |
| 569 ContentDescription** content, | |
| 570 ParseError* error) { | |
| 571 rtc::scoped_ptr<AudioContentDescription> audio( | |
| 572 new AudioContentDescription()); | |
| 573 | |
| 574 FeedbackParams content_feedback_params; | |
| 575 ParseFeedbackParams(content_elem, &content_feedback_params); | |
| 576 | |
| 577 int preference = kMaxPayloadId; | |
| 578 for (const buzz::XmlElement* payload_elem = | |
| 579 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 580 payload_elem != NULL; | |
| 581 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { | |
| 582 AudioCodec codec; | |
| 583 if (ParseJingleAudioCodec(payload_elem, &codec)) { | |
| 584 AddFeedbackParams(content_feedback_params, &codec.feedback_params); | |
| 585 codec.preference = preference--; | |
| 586 audio->AddCodec(codec); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) { | |
| 591 return false; | |
| 592 } | |
| 593 | |
| 594 if (!ParseJingleEncryption(content_elem, audio.get(), error)) { | |
| 595 return false; | |
| 596 } | |
| 597 | |
| 598 audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); | |
| 599 | |
| 600 RtpHeaderExtensions hdrexts; | |
| 601 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) { | |
| 602 return false; | |
| 603 } | |
| 604 audio->set_rtp_header_extensions(hdrexts); | |
| 605 | |
| 606 *content = audio.release(); | |
| 607 return true; | |
| 608 } | |
| 609 | |
| 610 bool ParseJingleVideoContent(const buzz::XmlElement* content_elem, | |
| 611 ContentDescription** content, | |
| 612 ParseError* error) { | |
| 613 rtc::scoped_ptr<VideoContentDescription> video( | |
| 614 new VideoContentDescription()); | |
| 615 | |
| 616 FeedbackParams content_feedback_params; | |
| 617 ParseFeedbackParams(content_elem, &content_feedback_params); | |
| 618 | |
| 619 int preference = kMaxPayloadId; | |
| 620 for (const buzz::XmlElement* payload_elem = | |
| 621 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 622 payload_elem != NULL; | |
| 623 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { | |
| 624 VideoCodec codec; | |
| 625 if (ParseJingleVideoCodec(payload_elem, &codec)) { | |
| 626 AddFeedbackParams(content_feedback_params, &codec.feedback_params); | |
| 627 codec.preference = preference--; | |
| 628 video->AddCodec(codec); | |
| 629 } | |
| 630 } | |
| 631 | |
| 632 if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) { | |
| 633 return false; | |
| 634 } | |
| 635 ParseBandwidth(content_elem, video.get()); | |
| 636 | |
| 637 if (!ParseJingleEncryption(content_elem, video.get(), error)) { | |
| 638 return false; | |
| 639 } | |
| 640 | |
| 641 video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); | |
| 642 | |
| 643 RtpHeaderExtensions hdrexts; | |
| 644 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) { | |
| 645 return false; | |
| 646 } | |
| 647 video->set_rtp_header_extensions(hdrexts); | |
| 648 | |
| 649 *content = video.release(); | |
| 650 return true; | |
| 651 } | |
| 652 | |
| 653 bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem, | |
| 654 ContentDescription** content, | |
| 655 ParseError* error) { | |
| 656 rtc::scoped_ptr<DataContentDescription> data( | |
| 657 new DataContentDescription()); | |
| 658 data->set_protocol(kMediaProtocolSctp); | |
| 659 | |
| 660 for (const buzz::XmlElement* stream_elem = | |
| 661 content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM); | |
| 662 stream_elem != NULL; | |
| 663 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) { | |
| 664 StreamParams stream; | |
| 665 stream.groupid = stream_elem->Attr(QN_NICK); | |
| 666 stream.id = stream_elem->Attr(QN_NAME); | |
| 667 uint32 sid; | |
| 668 if (!rtc::FromString(stream_elem->Attr(QN_SID), &sid)) { | |
| 669 return BadParse("Missing or invalid sid.", error); | |
| 670 } | |
| 671 if (sid > kMaxSctpSid) { | |
| 672 return BadParse("SID is greater than max value.", error); | |
| 673 } | |
| 674 | |
| 675 stream.ssrcs.push_back(sid); | |
| 676 data->mutable_streams().push_back(stream); | |
| 677 } | |
| 678 | |
| 679 *content = data.release(); | |
| 680 return true; | |
| 681 } | |
| 682 | |
| 683 bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem, | |
| 684 ContentDescription** content, | |
| 685 ParseError* error) { | |
| 686 DataContentDescription* data = new DataContentDescription(); | |
| 687 | |
| 688 FeedbackParams content_feedback_params; | |
| 689 ParseFeedbackParams(content_elem, &content_feedback_params); | |
| 690 | |
| 691 int preference = kMaxPayloadId; | |
| 692 for (const buzz::XmlElement* payload_elem = | |
| 693 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 694 payload_elem != NULL; | |
| 695 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) { | |
| 696 DataCodec codec; | |
| 697 if (ParseJingleDataCodec(payload_elem, &codec)) { | |
| 698 AddFeedbackParams(content_feedback_params, &codec.feedback_params); | |
| 699 codec.preference = preference--; | |
| 700 data->AddCodec(codec); | |
| 701 } | |
| 702 } | |
| 703 | |
| 704 if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) { | |
| 705 return false; | |
| 706 } | |
| 707 ParseBandwidth(content_elem, data); | |
| 708 | |
| 709 if (!ParseJingleEncryption(content_elem, data, error)) { | |
| 710 return false; | |
| 711 } | |
| 712 | |
| 713 data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL); | |
| 714 | |
| 715 *content = data; | |
| 716 return true; | |
| 717 } | |
| 718 | |
| 719 bool MediaSessionClient::ParseContent(SignalingProtocol protocol, | |
| 720 const buzz::XmlElement* content_elem, | |
| 721 ContentDescription** content, | |
| 722 ParseError* error) { | |
| 723 if (protocol == PROTOCOL_GINGLE) { | |
| 724 const std::string& content_type = content_elem->Name().Namespace(); | |
| 725 if (NS_GINGLE_AUDIO == content_type) { | |
| 726 return ParseGingleAudioContent(content_elem, content, error); | |
| 727 } else if (NS_GINGLE_VIDEO == content_type) { | |
| 728 return ParseGingleVideoContent(content_elem, content, error); | |
| 729 } else { | |
| 730 return BadParse("Unknown content type: " + content_type, error); | |
| 731 } | |
| 732 } else { | |
| 733 const std::string& content_type = content_elem->Name().Namespace(); | |
| 734 // We use the XMLNS of the <description> element to determine if | |
| 735 // it's RTP or SCTP. | |
| 736 if (content_type == NS_JINGLE_DRAFT_SCTP) { | |
| 737 return ParseJingleSctpDataContent(content_elem, content, error); | |
| 738 } | |
| 739 | |
| 740 std::string media; | |
| 741 if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error)) | |
| 742 return false; | |
| 743 | |
| 744 if (media == JINGLE_CONTENT_MEDIA_AUDIO) { | |
| 745 return ParseJingleAudioContent(content_elem, content, error); | |
| 746 } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) { | |
| 747 return ParseJingleVideoContent(content_elem, content, error); | |
| 748 } else if (media == JINGLE_CONTENT_MEDIA_DATA) { | |
| 749 return ParseJingleRtpDataContent(content_elem, content, error); | |
| 750 } else { | |
| 751 return BadParse("Unknown media: " + media, error); | |
| 752 } | |
| 753 } | |
| 754 } | |
| 755 | |
| 756 buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) { | |
| 757 buzz::XmlElement* payload_type = | |
| 758 new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true); | |
| 759 AddXmlAttr(payload_type, QN_ID, codec.id); | |
| 760 payload_type->AddAttr(QN_NAME, codec.name); | |
| 761 if (codec.clockrate > 0) | |
| 762 AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate); | |
| 763 if (codec.bitrate > 0) | |
| 764 AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate); | |
| 765 if (codec.channels > 1) | |
| 766 AddXmlAttr(payload_type, QN_CHANNELS, codec.channels); | |
| 767 return payload_type; | |
| 768 } | |
| 769 | |
| 770 buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) { | |
| 771 buzz::XmlElement* payload_type = | |
| 772 new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true); | |
| 773 AddXmlAttr(payload_type, QN_ID, codec.id); | |
| 774 payload_type->AddAttr(QN_NAME, codec.name); | |
| 775 AddXmlAttr(payload_type, QN_WIDTH, codec.width); | |
| 776 AddXmlAttr(payload_type, QN_HEIGHT, codec.height); | |
| 777 AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate); | |
| 778 return payload_type; | |
| 779 } | |
| 780 | |
| 781 buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) { | |
| 782 buzz::XmlElement* elem = new buzz::XmlElement(name, true); | |
| 783 if (ssrc) { | |
| 784 SetXmlBody(elem, ssrc); | |
| 785 } | |
| 786 return elem; | |
| 787 } | |
| 788 | |
| 789 buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) { | |
| 790 int kbps = bps / 1000; | |
| 791 buzz::XmlElement* elem = new buzz::XmlElement(name); | |
| 792 elem->AddAttr(buzz::QN_TYPE, "AS"); | |
| 793 SetXmlBody(elem, kbps); | |
| 794 return elem; | |
| 795 } | |
| 796 | |
| 797 // For Jingle, usage_qname is empty. | |
| 798 buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos, | |
| 799 bool required) { | |
| 800 buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION); | |
| 801 | |
| 802 if (required) { | |
| 803 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true"); | |
| 804 } | |
| 805 | |
| 806 for (CryptoParamsVec::const_iterator i = cryptos.begin(); | |
| 807 i != cryptos.end(); | |
| 808 ++i) { | |
| 809 buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO); | |
| 810 | |
| 811 AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag); | |
| 812 crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite); | |
| 813 crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params); | |
| 814 if (!i->session_params.empty()) { | |
| 815 crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params); | |
| 816 } | |
| 817 encryption_elem->AddElement(crypto_elem); | |
| 818 } | |
| 819 return encryption_elem; | |
| 820 } | |
| 821 | |
| 822 buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos, | |
| 823 const buzz::QName& usage_qname, | |
| 824 bool required) { | |
| 825 buzz::XmlElement* encryption_elem = | |
| 826 CreateJingleEncryptionElem(cryptos, required); | |
| 827 | |
| 828 if (required) { | |
| 829 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true"); | |
| 830 } | |
| 831 | |
| 832 buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname); | |
| 833 encryption_elem->AddElement(usage_elem); | |
| 834 | |
| 835 return encryption_elem; | |
| 836 } | |
| 837 | |
| 838 buzz::XmlElement* CreateGingleAudioContentElem( | |
| 839 const AudioContentDescription* audio, | |
| 840 bool crypto_required) { | |
| 841 buzz::XmlElement* elem = | |
| 842 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true); | |
| 843 | |
| 844 for (AudioCodecs::const_iterator codec = audio->codecs().begin(); | |
| 845 codec != audio->codecs().end(); ++codec) { | |
| 846 elem->AddElement(CreateGingleAudioCodecElem(*codec)); | |
| 847 } | |
| 848 if (audio->has_ssrcs()) { | |
| 849 elem->AddElement(CreateGingleSsrcElem( | |
| 850 QN_GINGLE_AUDIO_SRCID, audio->first_ssrc())); | |
| 851 } | |
| 852 | |
| 853 const CryptoParamsVec& cryptos = audio->cryptos(); | |
| 854 if (!cryptos.empty()) { | |
| 855 elem->AddElement(CreateGingleEncryptionElem(cryptos, | |
| 856 QN_GINGLE_AUDIO_CRYPTO_USAGE, | |
| 857 crypto_required)); | |
| 858 } | |
| 859 return elem; | |
| 860 } | |
| 861 | |
| 862 buzz::XmlElement* CreateGingleVideoContentElem( | |
| 863 const VideoContentDescription* video, | |
| 864 bool crypto_required) { | |
| 865 buzz::XmlElement* elem = | |
| 866 new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true); | |
| 867 | |
| 868 for (VideoCodecs::const_iterator codec = video->codecs().begin(); | |
| 869 codec != video->codecs().end(); ++codec) { | |
| 870 elem->AddElement(CreateGingleVideoCodecElem(*codec)); | |
| 871 } | |
| 872 if (video->has_ssrcs()) { | |
| 873 elem->AddElement(CreateGingleSsrcElem( | |
| 874 QN_GINGLE_VIDEO_SRCID, video->first_ssrc())); | |
| 875 } | |
| 876 if (video->bandwidth() != kAutoBandwidth) { | |
| 877 elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH, | |
| 878 video->bandwidth())); | |
| 879 } | |
| 880 | |
| 881 const CryptoParamsVec& cryptos = video->cryptos(); | |
| 882 if (!cryptos.empty()) { | |
| 883 elem->AddElement(CreateGingleEncryptionElem(cryptos, | |
| 884 QN_GINGLE_VIDEO_CRYPTO_USAGE, | |
| 885 crypto_required)); | |
| 886 } | |
| 887 | |
| 888 return elem; | |
| 889 } | |
| 890 | |
| 891 template <class T> | |
| 892 buzz::XmlElement* CreatePayloadTypeParameterElem( | |
| 893 const std::string& name, T value) { | |
| 894 buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER); | |
| 895 | |
| 896 elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name); | |
| 897 AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value); | |
| 898 | |
| 899 return elem; | |
| 900 } | |
| 901 | |
| 902 void AddRtcpFeedbackElem(buzz::XmlElement* elem, | |
| 903 const FeedbackParams& feedback_params) { | |
| 904 std::vector<FeedbackParam>::const_iterator it; | |
| 905 for (it = feedback_params.params().begin(); | |
| 906 it != feedback_params.params().end(); ++it) { | |
| 907 buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB); | |
| 908 fb_elem->AddAttr(QN_TYPE, it->id()); | |
| 909 fb_elem->AddAttr(QN_SUBTYPE, it->param()); | |
| 910 elem->AddElement(fb_elem); | |
| 911 } | |
| 912 } | |
| 913 | |
| 914 buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) { | |
| 915 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 916 | |
| 917 AddXmlAttr(elem, QN_ID, codec.id); | |
| 918 elem->AddAttr(QN_NAME, codec.name); | |
| 919 if (codec.clockrate > 0) { | |
| 920 AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate); | |
| 921 } | |
| 922 if (codec.bitrate > 0) { | |
| 923 elem->AddElement(CreatePayloadTypeParameterElem( | |
| 924 PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate)); | |
| 925 } | |
| 926 if (codec.channels > 1) { | |
| 927 AddXmlAttr(elem, QN_CHANNELS, codec.channels); | |
| 928 } | |
| 929 | |
| 930 AddRtcpFeedbackElem(elem, codec.feedback_params); | |
| 931 | |
| 932 return elem; | |
| 933 } | |
| 934 | |
| 935 buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) { | |
| 936 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 937 | |
| 938 AddXmlAttr(elem, QN_ID, codec.id); | |
| 939 elem->AddAttr(QN_NAME, codec.name); | |
| 940 elem->AddElement(CreatePayloadTypeParameterElem( | |
| 941 PAYLOADTYPE_PARAMETER_WIDTH, codec.width)); | |
| 942 elem->AddElement(CreatePayloadTypeParameterElem( | |
| 943 PAYLOADTYPE_PARAMETER_HEIGHT, codec.height)); | |
| 944 elem->AddElement(CreatePayloadTypeParameterElem( | |
| 945 PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate)); | |
| 946 | |
| 947 AddRtcpFeedbackElem(elem, codec.feedback_params); | |
| 948 | |
| 949 CodecParameterMap::const_iterator param_iter; | |
| 950 for (param_iter = codec.params.begin(); param_iter != codec.params.end(); | |
| 951 ++param_iter) { | |
| 952 elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first, | |
| 953 param_iter->second)); | |
| 954 } | |
| 955 | |
| 956 return elem; | |
| 957 } | |
| 958 | |
| 959 buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) { | |
| 960 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE); | |
| 961 | |
| 962 AddXmlAttr(elem, QN_ID, codec.id); | |
| 963 elem->AddAttr(QN_NAME, codec.name); | |
| 964 | |
| 965 AddRtcpFeedbackElem(elem, codec.feedback_params); | |
| 966 | |
| 967 return elem; | |
| 968 } | |
| 969 | |
| 970 void WriteLegacyJingleSsrc(const MediaContentDescription* media, | |
| 971 buzz::XmlElement* elem) { | |
| 972 if (media->has_ssrcs()) { | |
| 973 AddXmlAttr(elem, QN_SSRC, media->first_ssrc()); | |
| 974 } | |
| 975 } | |
| 976 | |
| 977 void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media, | |
| 978 buzz::XmlElement* desc_elem) { | |
| 979 if (!media->multistream()) { | |
| 980 WriteLegacyJingleSsrc(media, desc_elem); | |
| 981 } else { | |
| 982 WriteJingleStreams(media->streams(), desc_elem); | |
| 983 } | |
| 984 } | |
| 985 | |
| 986 buzz::XmlElement* CreateJingleAudioContentElem( | |
| 987 const AudioContentDescription* audio, bool crypto_required) { | |
| 988 buzz::XmlElement* elem = | |
| 989 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); | |
| 990 | |
| 991 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO); | |
| 992 WriteJingleStreamsOrLegacySsrc(audio, elem); | |
| 993 | |
| 994 for (AudioCodecs::const_iterator codec = audio->codecs().begin(); | |
| 995 codec != audio->codecs().end(); ++codec) { | |
| 996 elem->AddElement(CreateJingleAudioCodecElem(*codec)); | |
| 997 } | |
| 998 | |
| 999 const CryptoParamsVec& cryptos = audio->cryptos(); | |
| 1000 if (!cryptos.empty()) { | |
| 1001 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); | |
| 1002 } | |
| 1003 | |
| 1004 if (audio->rtcp_mux()) { | |
| 1005 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); | |
| 1006 } | |
| 1007 | |
| 1008 WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem); | |
| 1009 | |
| 1010 return elem; | |
| 1011 } | |
| 1012 | |
| 1013 buzz::XmlElement* CreateJingleVideoContentElem( | |
| 1014 const VideoContentDescription* video, bool crypto_required) { | |
| 1015 buzz::XmlElement* elem = | |
| 1016 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); | |
| 1017 | |
| 1018 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO); | |
| 1019 WriteJingleStreamsOrLegacySsrc(video, elem); | |
| 1020 | |
| 1021 for (VideoCodecs::const_iterator codec = video->codecs().begin(); | |
| 1022 codec != video->codecs().end(); ++codec) { | |
| 1023 elem->AddElement(CreateJingleVideoCodecElem(*codec)); | |
| 1024 } | |
| 1025 | |
| 1026 const CryptoParamsVec& cryptos = video->cryptos(); | |
| 1027 if (!cryptos.empty()) { | |
| 1028 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); | |
| 1029 } | |
| 1030 | |
| 1031 if (video->rtcp_mux()) { | |
| 1032 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); | |
| 1033 } | |
| 1034 | |
| 1035 if (video->bandwidth() != kAutoBandwidth) { | |
| 1036 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH, | |
| 1037 video->bandwidth())); | |
| 1038 } | |
| 1039 | |
| 1040 WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem); | |
| 1041 | |
| 1042 return elem; | |
| 1043 } | |
| 1044 | |
| 1045 buzz::XmlElement* CreateJingleSctpDataContentElem( | |
| 1046 const DataContentDescription* data) { | |
| 1047 buzz::XmlElement* content_elem = | |
| 1048 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true); | |
| 1049 for (std::vector<StreamParams>::const_iterator | |
| 1050 stream = data->streams().begin(); | |
| 1051 stream != data->streams().end(); ++stream) { | |
| 1052 buzz::XmlElement* stream_elem = | |
| 1053 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false); | |
| 1054 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid); | |
| 1055 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id); | |
| 1056 if (!stream->ssrcs.empty()) { | |
| 1057 AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]); | |
| 1058 } | |
| 1059 content_elem->AddElement(stream_elem); | |
| 1060 } | |
| 1061 return content_elem;; | |
| 1062 } | |
| 1063 | |
| 1064 buzz::XmlElement* CreateJingleRtpDataContentElem( | |
| 1065 const DataContentDescription* data, bool crypto_required) { | |
| 1066 | |
| 1067 buzz::XmlElement* elem = | |
| 1068 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true); | |
| 1069 | |
| 1070 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA); | |
| 1071 WriteJingleStreamsOrLegacySsrc(data, elem); | |
| 1072 | |
| 1073 for (DataCodecs::const_iterator codec = data->codecs().begin(); | |
| 1074 codec != data->codecs().end(); ++codec) { | |
| 1075 elem->AddElement(CreateJingleDataCodecElem(*codec)); | |
| 1076 } | |
| 1077 | |
| 1078 const CryptoParamsVec& cryptos = data->cryptos(); | |
| 1079 if (!cryptos.empty()) { | |
| 1080 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required)); | |
| 1081 } | |
| 1082 | |
| 1083 if (data->rtcp_mux()) { | |
| 1084 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX)); | |
| 1085 } | |
| 1086 | |
| 1087 if (data->bandwidth() != kAutoBandwidth) { | |
| 1088 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH, | |
| 1089 data->bandwidth())); | |
| 1090 } | |
| 1091 | |
| 1092 return elem; | |
| 1093 } | |
| 1094 | |
| 1095 bool IsSctp(const DataContentDescription* data) { | |
| 1096 return (data->protocol() == kMediaProtocolSctp || | |
| 1097 data->protocol() == kMediaProtocolDtlsSctp); | |
| 1098 } | |
| 1099 | |
| 1100 buzz::XmlElement* CreateJingleDataContentElem( | |
| 1101 const DataContentDescription* data, bool crypto_required) { | |
| 1102 if (IsSctp(data)) { | |
| 1103 return CreateJingleSctpDataContentElem(data); | |
| 1104 } else { | |
| 1105 return CreateJingleRtpDataContentElem(data, crypto_required); | |
| 1106 } | |
| 1107 } | |
| 1108 | |
| 1109 bool MediaSessionClient::IsWritable(SignalingProtocol protocol, | |
| 1110 const ContentDescription* content) { | |
| 1111 const MediaContentDescription* media = | |
| 1112 static_cast<const MediaContentDescription*>(content); | |
| 1113 if (protocol == PROTOCOL_GINGLE && | |
| 1114 media->type() == MEDIA_TYPE_DATA) { | |
| 1115 return false; | |
| 1116 } | |
| 1117 return true; | |
| 1118 } | |
| 1119 | |
| 1120 bool MediaSessionClient::WriteContent(SignalingProtocol protocol, | |
| 1121 const ContentDescription* content, | |
| 1122 buzz::XmlElement** elem, | |
| 1123 WriteError* error) { | |
| 1124 const MediaContentDescription* media = | |
| 1125 static_cast<const MediaContentDescription*>(content); | |
| 1126 bool crypto_required = secure() == SEC_REQUIRED; | |
| 1127 | |
| 1128 if (media->type() == MEDIA_TYPE_AUDIO) { | |
| 1129 const AudioContentDescription* audio = | |
| 1130 static_cast<const AudioContentDescription*>(media); | |
| 1131 if (protocol == PROTOCOL_GINGLE) { | |
| 1132 *elem = CreateGingleAudioContentElem(audio, crypto_required); | |
| 1133 } else { | |
| 1134 *elem = CreateJingleAudioContentElem(audio, crypto_required); | |
| 1135 } | |
| 1136 } else if (media->type() == MEDIA_TYPE_VIDEO) { | |
| 1137 const VideoContentDescription* video = | |
| 1138 static_cast<const VideoContentDescription*>(media); | |
| 1139 if (protocol == PROTOCOL_GINGLE) { | |
| 1140 *elem = CreateGingleVideoContentElem(video, crypto_required); | |
| 1141 } else { | |
| 1142 *elem = CreateJingleVideoContentElem(video, crypto_required); | |
| 1143 } | |
| 1144 } else if (media->type() == MEDIA_TYPE_DATA) { | |
| 1145 const DataContentDescription* data = | |
| 1146 static_cast<const DataContentDescription*>(media); | |
| 1147 if (protocol == PROTOCOL_GINGLE) { | |
| 1148 return BadWrite("Data channel not supported with Gingle.", error); | |
| 1149 } else { | |
| 1150 *elem = CreateJingleDataContentElem(data, crypto_required); | |
| 1151 } | |
| 1152 } else { | |
| 1153 return BadWrite("Unknown content type: " + | |
| 1154 rtc::ToString<int>(media->type()), error); | |
| 1155 } | |
| 1156 | |
| 1157 return true; | |
| 1158 } | |
| 1159 | |
| 1160 } // namespace cricket | |
| OLD | NEW |