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