blob: a5a652cc78dfa88d5f63c4518b668942543ca776 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
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/base/helpers.h"
33#include "talk/base/logging.h"
34#include "talk/base/stringencode.h"
35#include "talk/base/stringutils.h"
36#include "talk/media/base/cryptoparams.h"
37#include "talk/media/base/capturemanager.h"
mallinath@webrtc.org1112c302013-09-23 20:34:45 +000038#include "talk/media/sctp/sctpdataengine.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000039#include "talk/p2p/base/constants.h"
40#include "talk/p2p/base/parsing.h"
41#include "talk/session/media/mediamessages.h"
42#include "talk/session/media/srtpfilter.h"
43#include "talk/xmllite/qname.h"
44#include "talk/xmllite/xmlconstants.h"
45#include "talk/xmpp/constants.h"
46
47namespace cricket {
48
49#if !defined(DISABLE_MEDIA_ENGINE_FACTORY)
50MediaSessionClient::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_,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000057 session_manager_->transport_desc_factory()),
58 multisession_enabled_(false) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059 Construct();
60}
61#endif
62
63MediaSessionClient::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_,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000076 session_manager_->transport_desc_factory()),
77 multisession_enabled_(false) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078 Construct();
79}
80
81void 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
92MediaSessionClient::~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
107Call *MediaSessionClient::CreateCall() {
108 Call *call = new Call(this);
109 calls_[call->id()] = call;
110 SignalCallCreate(call);
111 return call;
112}
113
114void MediaSessionClient::OnSessionCreate(Session *session,
115 bool received_initiate) {
116 if (received_initiate) {
117 session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
118 }
119}
120
121void 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
170void 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
186void 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
197Call *MediaSessionClient::GetFocus() {
198 return focus_call_;
199}
200
201void 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
213void 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
223Session *MediaSessionClient::CreateSession(Call *call) {
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000224 std::string id;
225 return CreateSession(id, call);
226}
227
228Session *MediaSessionClient::CreateSession(const std::string& id, Call* call) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229 const std::string& type = NS_JINGLE_RTP;
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000230 Session *session = session_manager_->CreateSession(id, jid().Str(), type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231 session_map_[session->id()] = call;
232 return session;
233}
234
235Call *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.
252bool 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
265bool 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.
281bool ParseSsrcAsLegacyStream(const std::string& ssrc_str,
282 std::vector<StreamParams>* streams,
283 ParseError* error) {
284 if (!ssrc_str.empty()) {
285 uint32 ssrc;
286 if (!talk_base::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
295void 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
306bool 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.
334bool 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) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000342 if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
343 media->set_crypto_required(CT_SDES);
344 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345 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, &params, error)) {
350 return false;
351 }
352 media->AddCrypto(params);
353 }
354 break;
355 }
356 }
357 return true;
358}
359
360void 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 && talk_base::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
365 if (bandwidth_kbps >= 0) {
366 media->set_bandwidth(bandwidth_kbps * 1000);
367 }
368 }
369}
370
371bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
372 ContentDescription** content,
373 ParseError* error) {
374 AudioContentDescription* audio = new AudioContentDescription();
375
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 audio->AddCodec(codec);
384 }
385 }
386 } else {
387 // For backward compatibility, we can assume the other client is
388 // an old version of Talk if it has no audio payload types at all.
389 audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
390 audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
391 }
392
393 ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
394
395 if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
396 audio, error)) {
397 return false;
398 }
399
400 *content = audio;
401 return true;
402}
403
404bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
405 ContentDescription** content,
406 ParseError* error) {
407 VideoContentDescription* video = new VideoContentDescription();
408
409 for (const buzz::XmlElement* codec_elem =
410 content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
411 codec_elem != NULL;
412 codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
413 VideoCodec codec;
414 if (ParseGingleVideoCodec(codec_elem, &codec)) {
415 video->AddCodec(codec);
416 }
417 }
418
419 ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
420 ParseBandwidth(content_elem, video);
421
422 if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
423 video, error)) {
424 return false;
425 }
426
427 *content = video;
428 return true;
429}
430
431void ParsePayloadTypeParameters(const buzz::XmlElement* element,
432 std::map<std::string, std::string>* paramap) {
433 for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
434 param != NULL; param = param->NextNamed(QN_PARAMETER)) {
435 std::string name = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
436 buzz::STR_EMPTY);
437 std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
438 buzz::STR_EMPTY);
439 if (!name.empty() && !value.empty()) {
440 paramap->insert(make_pair(name, value));
441 }
442 }
443}
444
wu@webrtc.org91053e72013-08-10 07:18:04 +0000445void ParseFeedbackParams(const buzz::XmlElement* element,
446 FeedbackParams* params) {
447 for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB);
448 param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) {
449 std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY);
450 std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY);
451 if (!type.empty()) {
452 params->Add(FeedbackParam(type, subtype));
453 }
454 }
455}
456
457void AddFeedbackParams(const FeedbackParams& additional_params,
458 FeedbackParams* params) {
459 for (size_t i = 0; i < additional_params.params().size(); ++i) {
460 params->Add(additional_params.params()[i]);
461 }
462}
463
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464int FindWithDefault(const std::map<std::string, std::string>& map,
465 const std::string& key, const int def) {
466 std::map<std::string, std::string>::const_iterator iter = map.find(key);
467 return (iter == map.end()) ? def : atoi(iter->second.c_str());
468}
469
470
471// Parse the first encryption element found.
472// Return false if there was an encryption element and it could not be
473// parsed.
474bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
475 MediaContentDescription* media,
476 ParseError* error) {
477 const buzz::XmlElement* encryption =
478 content_elem->FirstNamed(QN_ENCRYPTION);
479 if (encryption == NULL) {
480 return true;
481 }
482
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000483 if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
484 media->set_crypto_required(CT_SDES);
485 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486
487 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
488 crypto != NULL;
489 crypto = crypto->NextNamed(QN_CRYPTO)) {
490 CryptoParams params;
491 if (!ParseCryptoParams(crypto, &params, error)) {
492 return false;
493 }
494 media->AddCrypto(params);
495 }
496 return true;
497}
498
499bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
500 int id = GetXmlAttr(elem, QN_ID, -1);
501 if (id < 0)
502 return false;
503
504 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
505 int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
506 int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
507
508 std::map<std::string, std::string> paramap;
509 ParsePayloadTypeParameters(elem, &paramap);
510 int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
511
512 *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000513 ParseFeedbackParams(elem, &codec->feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000514 return true;
515}
516
517bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
518 int id = GetXmlAttr(elem, QN_ID, -1);
519 if (id < 0)
520 return false;
521
522 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
523
524 std::map<std::string, std::string> paramap;
525 ParsePayloadTypeParameters(elem, &paramap);
526 int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
527 int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
528 int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
529
530 *codec = VideoCodec(id, name, width, height, framerate, 0);
531 codec->params = paramap;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000532 ParseFeedbackParams(elem, &codec->feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000533 return true;
534}
535
536bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
537 int id = GetXmlAttr(elem, QN_ID, -1);
538 if (id < 0)
539 return false;
540
541 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
542
543 *codec = DataCodec(id, name, 0);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000544 ParseFeedbackParams(elem, &codec->feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000545 return true;
546}
547
548bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
549 MediaContentDescription* media,
550 ParseError* error) {
551 if (HasJingleStreams(desc_elem)) {
552 if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
553 return false;
554 }
555 } else {
556 const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
557 if (!ParseSsrcAsLegacyStream(
558 ssrc_str, &(media->mutable_streams()), error)) {
559 return false;
560 }
561 }
562 return true;
563}
564
565bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
566 ContentDescription** content,
567 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000568 talk_base::scoped_ptr<AudioContentDescription> audio(
569 new AudioContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570
wu@webrtc.org91053e72013-08-10 07:18:04 +0000571 FeedbackParams content_feedback_params;
572 ParseFeedbackParams(content_elem, &content_feedback_params);
573
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000574 for (const buzz::XmlElement* payload_elem =
575 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
576 payload_elem != NULL;
577 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
578 AudioCodec codec;
579 if (ParseJingleAudioCodec(payload_elem, &codec)) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000580 AddFeedbackParams(content_feedback_params, &codec.feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581 audio->AddCodec(codec);
582 }
583 }
584
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000585 if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586 return false;
587 }
588
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000589 if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000590 return false;
591 }
592
593 audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
594
595 RtpHeaderExtensions hdrexts;
596 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
597 return false;
598 }
599 audio->set_rtp_header_extensions(hdrexts);
600
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000601 *content = audio.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 return true;
603}
604
605bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
606 ContentDescription** content,
607 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000608 talk_base::scoped_ptr<VideoContentDescription> video(
609 new VideoContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000610
wu@webrtc.org91053e72013-08-10 07:18:04 +0000611 FeedbackParams content_feedback_params;
612 ParseFeedbackParams(content_elem, &content_feedback_params);
613
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000614 for (const buzz::XmlElement* payload_elem =
615 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
616 payload_elem != NULL;
617 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
618 VideoCodec codec;
619 if (ParseJingleVideoCodec(payload_elem, &codec)) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000620 AddFeedbackParams(content_feedback_params, &codec.feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000621 video->AddCodec(codec);
622 }
623 }
624
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000625 if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000626 return false;
627 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000628 ParseBandwidth(content_elem, video.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000630 if (!ParseJingleEncryption(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 return false;
632 }
633
634 video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
635
636 RtpHeaderExtensions hdrexts;
637 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
638 return false;
639 }
640 video->set_rtp_header_extensions(hdrexts);
641
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000642 *content = video.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000643 return true;
644}
645
646bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
647 ContentDescription** content,
648 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000649 talk_base::scoped_ptr<DataContentDescription> data(
650 new DataContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000651 data->set_protocol(kMediaProtocolSctp);
652
653 for (const buzz::XmlElement* stream_elem =
654 content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
655 stream_elem != NULL;
656 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
657 StreamParams stream;
658 stream.groupid = stream_elem->Attr(QN_NICK);
659 stream.id = stream_elem->Attr(QN_NAME);
660 uint32 sid;
661 if (!talk_base::FromString(stream_elem->Attr(QN_SID), &sid)) {
662 return BadParse("Missing or invalid sid.", error);
663 }
664 if (sid > kMaxSctpSid) {
665 return BadParse("SID is greater than max value.", error);
666 }
667
668 stream.ssrcs.push_back(sid);
669 data->mutable_streams().push_back(stream);
670 }
671
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000672 *content = data.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673 return true;
674}
675
676bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
677 ContentDescription** content,
678 ParseError* error) {
679 DataContentDescription* data = new DataContentDescription();
680
wu@webrtc.org91053e72013-08-10 07:18:04 +0000681 FeedbackParams content_feedback_params;
682 ParseFeedbackParams(content_elem, &content_feedback_params);
683
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 for (const buzz::XmlElement* payload_elem =
685 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
686 payload_elem != NULL;
687 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
688 DataCodec codec;
689 if (ParseJingleDataCodec(payload_elem, &codec)) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000690 AddFeedbackParams(content_feedback_params, &codec.feedback_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000691 data->AddCodec(codec);
692 }
693 }
694
695 if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
696 return false;
697 }
698 ParseBandwidth(content_elem, data);
699
700 if (!ParseJingleEncryption(content_elem, data, error)) {
701 return false;
702 }
703
704 data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
705
706 *content = data;
707 return true;
708}
709
710bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
711 const buzz::XmlElement* content_elem,
712 ContentDescription** content,
713 ParseError* error) {
714 if (protocol == PROTOCOL_GINGLE) {
715 const std::string& content_type = content_elem->Name().Namespace();
716 if (NS_GINGLE_AUDIO == content_type) {
717 return ParseGingleAudioContent(content_elem, content, error);
718 } else if (NS_GINGLE_VIDEO == content_type) {
719 return ParseGingleVideoContent(content_elem, content, error);
720 } else {
721 return BadParse("Unknown content type: " + content_type, error);
722 }
723 } else {
724 const std::string& content_type = content_elem->Name().Namespace();
725 // We use the XMLNS of the <description> element to determine if
726 // it's RTP or SCTP.
727 if (content_type == NS_JINGLE_DRAFT_SCTP) {
728 return ParseJingleSctpDataContent(content_elem, content, error);
729 }
730
731 std::string media;
732 if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
733 return false;
734
735 if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
736 return ParseJingleAudioContent(content_elem, content, error);
737 } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
738 return ParseJingleVideoContent(content_elem, content, error);
739 } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
740 return ParseJingleRtpDataContent(content_elem, content, error);
741 } else {
742 return BadParse("Unknown media: " + media, error);
743 }
744 }
745}
746
747buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
748 buzz::XmlElement* payload_type =
749 new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
750 AddXmlAttr(payload_type, QN_ID, codec.id);
751 payload_type->AddAttr(QN_NAME, codec.name);
752 if (codec.clockrate > 0)
753 AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
754 if (codec.bitrate > 0)
755 AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
756 if (codec.channels > 1)
757 AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
758 return payload_type;
759}
760
761buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
762 buzz::XmlElement* payload_type =
763 new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
764 AddXmlAttr(payload_type, QN_ID, codec.id);
765 payload_type->AddAttr(QN_NAME, codec.name);
766 AddXmlAttr(payload_type, QN_WIDTH, codec.width);
767 AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
768 AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
769 return payload_type;
770}
771
772buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
773 buzz::XmlElement* elem = new buzz::XmlElement(name, true);
774 if (ssrc) {
775 SetXmlBody(elem, ssrc);
776 }
777 return elem;
778}
779
780buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
781 int kbps = bps / 1000;
782 buzz::XmlElement* elem = new buzz::XmlElement(name);
783 elem->AddAttr(buzz::QN_TYPE, "AS");
784 SetXmlBody(elem, kbps);
785 return elem;
786}
787
788// For Jingle, usage_qname is empty.
789buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
790 bool required) {
791 buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
792
793 if (required) {
794 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
795 }
796
797 for (CryptoParamsVec::const_iterator i = cryptos.begin();
798 i != cryptos.end();
799 ++i) {
800 buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
801
802 AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
803 crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
804 crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
805 if (!i->session_params.empty()) {
806 crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
807 }
808 encryption_elem->AddElement(crypto_elem);
809 }
810 return encryption_elem;
811}
812
813buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
814 const buzz::QName& usage_qname,
815 bool required) {
816 buzz::XmlElement* encryption_elem =
817 CreateJingleEncryptionElem(cryptos, required);
818
819 if (required) {
820 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
821 }
822
823 buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
824 encryption_elem->AddElement(usage_elem);
825
826 return encryption_elem;
827}
828
829buzz::XmlElement* CreateGingleAudioContentElem(
830 const AudioContentDescription* audio,
831 bool crypto_required) {
832 buzz::XmlElement* elem =
833 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
834
835 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
836 codec != audio->codecs().end(); ++codec) {
837 elem->AddElement(CreateGingleAudioCodecElem(*codec));
838 }
839 if (audio->has_ssrcs()) {
840 elem->AddElement(CreateGingleSsrcElem(
841 QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
842 }
843
844 const CryptoParamsVec& cryptos = audio->cryptos();
845 if (!cryptos.empty()) {
846 elem->AddElement(CreateGingleEncryptionElem(cryptos,
847 QN_GINGLE_AUDIO_CRYPTO_USAGE,
848 crypto_required));
849 }
850 return elem;
851}
852
853buzz::XmlElement* CreateGingleVideoContentElem(
854 const VideoContentDescription* video,
855 bool crypto_required) {
856 buzz::XmlElement* elem =
857 new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
858
859 for (VideoCodecs::const_iterator codec = video->codecs().begin();
860 codec != video->codecs().end(); ++codec) {
861 elem->AddElement(CreateGingleVideoCodecElem(*codec));
862 }
863 if (video->has_ssrcs()) {
864 elem->AddElement(CreateGingleSsrcElem(
865 QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
866 }
867 if (video->bandwidth() != kAutoBandwidth) {
868 elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
869 video->bandwidth()));
870 }
871
872 const CryptoParamsVec& cryptos = video->cryptos();
873 if (!cryptos.empty()) {
874 elem->AddElement(CreateGingleEncryptionElem(cryptos,
875 QN_GINGLE_VIDEO_CRYPTO_USAGE,
876 crypto_required));
877 }
878
879 return elem;
880}
881
882template <class T>
883buzz::XmlElement* CreatePayloadTypeParameterElem(
884 const std::string& name, T value) {
885 buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
886
887 elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
888 AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
889
890 return elem;
891}
892
wu@webrtc.org91053e72013-08-10 07:18:04 +0000893void AddRtcpFeedbackElem(buzz::XmlElement* elem,
894 const FeedbackParams& feedback_params) {
895 std::vector<FeedbackParam>::const_iterator it;
896 for (it = feedback_params.params().begin();
897 it != feedback_params.params().end(); ++it) {
898 buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB);
899 fb_elem->AddAttr(QN_TYPE, it->id());
900 fb_elem->AddAttr(QN_SUBTYPE, it->param());
901 elem->AddElement(fb_elem);
902 }
903}
904
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000905buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
906 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
907
908 AddXmlAttr(elem, QN_ID, codec.id);
909 elem->AddAttr(QN_NAME, codec.name);
910 if (codec.clockrate > 0) {
911 AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
912 }
913 if (codec.bitrate > 0) {
914 elem->AddElement(CreatePayloadTypeParameterElem(
915 PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
916 }
917 if (codec.channels > 1) {
918 AddXmlAttr(elem, QN_CHANNELS, codec.channels);
919 }
920
wu@webrtc.org91053e72013-08-10 07:18:04 +0000921 AddRtcpFeedbackElem(elem, codec.feedback_params);
922
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000923 return elem;
924}
925
926buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
927 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
928
929 AddXmlAttr(elem, QN_ID, codec.id);
930 elem->AddAttr(QN_NAME, codec.name);
931 elem->AddElement(CreatePayloadTypeParameterElem(
932 PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
933 elem->AddElement(CreatePayloadTypeParameterElem(
934 PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
935 elem->AddElement(CreatePayloadTypeParameterElem(
936 PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000937
938 AddRtcpFeedbackElem(elem, codec.feedback_params);
939
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000940 CodecParameterMap::const_iterator param_iter;
941 for (param_iter = codec.params.begin(); param_iter != codec.params.end();
942 ++param_iter) {
943 elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
944 param_iter->second));
945 }
946
947 return elem;
948}
949
950buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
951 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
952
953 AddXmlAttr(elem, QN_ID, codec.id);
954 elem->AddAttr(QN_NAME, codec.name);
955
wu@webrtc.org91053e72013-08-10 07:18:04 +0000956 AddRtcpFeedbackElem(elem, codec.feedback_params);
957
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000958 return elem;
959}
960
961void WriteLegacyJingleSsrc(const MediaContentDescription* media,
962 buzz::XmlElement* elem) {
963 if (media->has_ssrcs()) {
964 AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
965 }
966}
967
968void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
969 buzz::XmlElement* desc_elem) {
970 if (!media->multistream()) {
971 WriteLegacyJingleSsrc(media, desc_elem);
972 } else {
973 WriteJingleStreams(media->streams(), desc_elem);
974 }
975}
976
977buzz::XmlElement* CreateJingleAudioContentElem(
978 const AudioContentDescription* audio, bool crypto_required) {
979 buzz::XmlElement* elem =
980 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
981
982 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
983 WriteJingleStreamsOrLegacySsrc(audio, elem);
984
985 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
986 codec != audio->codecs().end(); ++codec) {
987 elem->AddElement(CreateJingleAudioCodecElem(*codec));
988 }
989
990 const CryptoParamsVec& cryptos = audio->cryptos();
991 if (!cryptos.empty()) {
992 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
993 }
994
995 if (audio->rtcp_mux()) {
996 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
997 }
998
999 WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
1000
1001 return elem;
1002}
1003
1004buzz::XmlElement* CreateJingleVideoContentElem(
1005 const VideoContentDescription* video, bool crypto_required) {
1006 buzz::XmlElement* elem =
1007 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1008
1009 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
1010 WriteJingleStreamsOrLegacySsrc(video, elem);
1011
1012 for (VideoCodecs::const_iterator codec = video->codecs().begin();
1013 codec != video->codecs().end(); ++codec) {
1014 elem->AddElement(CreateJingleVideoCodecElem(*codec));
1015 }
1016
1017 const CryptoParamsVec& cryptos = video->cryptos();
1018 if (!cryptos.empty()) {
1019 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1020 }
1021
1022 if (video->rtcp_mux()) {
1023 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1024 }
1025
1026 if (video->bandwidth() != kAutoBandwidth) {
1027 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1028 video->bandwidth()));
1029 }
1030
1031 WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
1032
1033 return elem;
1034}
1035
1036buzz::XmlElement* CreateJingleSctpDataContentElem(
1037 const DataContentDescription* data) {
1038 buzz::XmlElement* content_elem =
1039 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
1040 for (std::vector<StreamParams>::const_iterator
1041 stream = data->streams().begin();
1042 stream != data->streams().end(); ++stream) {
1043 buzz::XmlElement* stream_elem =
1044 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
1045 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
1046 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
1047 if (!stream->ssrcs.empty()) {
1048 AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
1049 }
1050 content_elem->AddElement(stream_elem);
1051 }
1052 return content_elem;;
1053}
1054
1055buzz::XmlElement* CreateJingleRtpDataContentElem(
1056 const DataContentDescription* data, bool crypto_required) {
1057
1058 buzz::XmlElement* elem =
1059 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1060
1061 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
1062 WriteJingleStreamsOrLegacySsrc(data, elem);
1063
1064 for (DataCodecs::const_iterator codec = data->codecs().begin();
1065 codec != data->codecs().end(); ++codec) {
1066 elem->AddElement(CreateJingleDataCodecElem(*codec));
1067 }
1068
1069 const CryptoParamsVec& cryptos = data->cryptos();
1070 if (!cryptos.empty()) {
1071 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1072 }
1073
1074 if (data->rtcp_mux()) {
1075 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1076 }
1077
1078 if (data->bandwidth() != kAutoBandwidth) {
1079 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1080 data->bandwidth()));
1081 }
1082
1083 return elem;
1084}
1085
1086bool IsSctp(const DataContentDescription* data) {
1087 return (data->protocol() == kMediaProtocolSctp ||
1088 data->protocol() == kMediaProtocolDtlsSctp);
1089}
1090
1091buzz::XmlElement* CreateJingleDataContentElem(
1092 const DataContentDescription* data, bool crypto_required) {
1093 if (IsSctp(data)) {
1094 return CreateJingleSctpDataContentElem(data);
1095 } else {
1096 return CreateJingleRtpDataContentElem(data, crypto_required);
1097 }
1098}
1099
1100bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
1101 const ContentDescription* content) {
1102 const MediaContentDescription* media =
1103 static_cast<const MediaContentDescription*>(content);
1104 if (protocol == PROTOCOL_GINGLE &&
1105 media->type() == MEDIA_TYPE_DATA) {
1106 return false;
1107 }
1108 return true;
1109}
1110
1111bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
1112 const ContentDescription* content,
1113 buzz::XmlElement** elem,
1114 WriteError* error) {
1115 const MediaContentDescription* media =
1116 static_cast<const MediaContentDescription*>(content);
1117 bool crypto_required = secure() == SEC_REQUIRED;
1118
1119 if (media->type() == MEDIA_TYPE_AUDIO) {
1120 const AudioContentDescription* audio =
1121 static_cast<const AudioContentDescription*>(media);
1122 if (protocol == PROTOCOL_GINGLE) {
1123 *elem = CreateGingleAudioContentElem(audio, crypto_required);
1124 } else {
1125 *elem = CreateJingleAudioContentElem(audio, crypto_required);
1126 }
1127 } else if (media->type() == MEDIA_TYPE_VIDEO) {
1128 const VideoContentDescription* video =
1129 static_cast<const VideoContentDescription*>(media);
1130 if (protocol == PROTOCOL_GINGLE) {
1131 *elem = CreateGingleVideoContentElem(video, crypto_required);
1132 } else {
1133 *elem = CreateJingleVideoContentElem(video, crypto_required);
1134 }
1135 } else if (media->type() == MEDIA_TYPE_DATA) {
1136 const DataContentDescription* data =
1137 static_cast<const DataContentDescription*>(media);
1138 if (protocol == PROTOCOL_GINGLE) {
1139 return BadWrite("Data channel not supported with Gingle.", error);
1140 } else {
1141 *elem = CreateJingleDataContentElem(data, crypto_required);
1142 }
1143 } else {
1144 return BadWrite("Unknown content type: " +
1145 talk_base::ToString<int>(media->type()), error);
1146 }
1147
1148 return true;
1149}
1150
1151} // namespace cricket