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