blob: 265906f4780af2e90f0df8243d457a5bed5e93da [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
443int FindWithDefault(const std::map<std::string, std::string>& map,
444 const std::string& key, const int def) {
445 std::map<std::string, std::string>::const_iterator iter = map.find(key);
446 return (iter == map.end()) ? def : atoi(iter->second.c_str());
447}
448
449
450// Parse the first encryption element found.
451// Return false if there was an encryption element and it could not be
452// parsed.
453bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
454 MediaContentDescription* media,
455 ParseError* error) {
456 const buzz::XmlElement* encryption =
457 content_elem->FirstNamed(QN_ENCRYPTION);
458 if (encryption == NULL) {
459 return true;
460 }
461
462 media->set_crypto_required(
463 GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
464
465 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
466 crypto != NULL;
467 crypto = crypto->NextNamed(QN_CRYPTO)) {
468 CryptoParams params;
469 if (!ParseCryptoParams(crypto, &params, error)) {
470 return false;
471 }
472 media->AddCrypto(params);
473 }
474 return true;
475}
476
477bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
478 int id = GetXmlAttr(elem, QN_ID, -1);
479 if (id < 0)
480 return false;
481
482 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
483 int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
484 int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
485
486 std::map<std::string, std::string> paramap;
487 ParsePayloadTypeParameters(elem, &paramap);
488 int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
489
490 *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
491 return true;
492}
493
494bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
495 int id = GetXmlAttr(elem, QN_ID, -1);
496 if (id < 0)
497 return false;
498
499 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
500
501 std::map<std::string, std::string> paramap;
502 ParsePayloadTypeParameters(elem, &paramap);
503 int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
504 int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
505 int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
506
507 *codec = VideoCodec(id, name, width, height, framerate, 0);
508 codec->params = paramap;
509 return true;
510}
511
512bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
513 int id = GetXmlAttr(elem, QN_ID, -1);
514 if (id < 0)
515 return false;
516
517 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
518
519 *codec = DataCodec(id, name, 0);
520 return true;
521}
522
523bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
524 MediaContentDescription* media,
525 ParseError* error) {
526 if (HasJingleStreams(desc_elem)) {
527 if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
528 return false;
529 }
530 } else {
531 const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
532 if (!ParseSsrcAsLegacyStream(
533 ssrc_str, &(media->mutable_streams()), error)) {
534 return false;
535 }
536 }
537 return true;
538}
539
540bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
541 ContentDescription** content,
542 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000543 talk_base::scoped_ptr<AudioContentDescription> audio(
544 new AudioContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000545
546 for (const buzz::XmlElement* payload_elem =
547 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
548 payload_elem != NULL;
549 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
550 AudioCodec codec;
551 if (ParseJingleAudioCodec(payload_elem, &codec)) {
552 audio->AddCodec(codec);
553 }
554 }
555
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000556 if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000557 return false;
558 }
559
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000560 if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000561 return false;
562 }
563
564 audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
565
566 RtpHeaderExtensions hdrexts;
567 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
568 return false;
569 }
570 audio->set_rtp_header_extensions(hdrexts);
571
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000572 *content = audio.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000573 return true;
574}
575
576bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
577 ContentDescription** content,
578 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000579 talk_base::scoped_ptr<VideoContentDescription> video(
580 new VideoContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581
582 for (const buzz::XmlElement* payload_elem =
583 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
584 payload_elem != NULL;
585 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
586 VideoCodec codec;
587 if (ParseJingleVideoCodec(payload_elem, &codec)) {
588 video->AddCodec(codec);
589 }
590 }
591
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000592 if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 return false;
594 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000595 ParseBandwidth(content_elem, video.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000596
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000597 if (!ParseJingleEncryption(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 return false;
599 }
600
601 video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
602
603 RtpHeaderExtensions hdrexts;
604 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
605 return false;
606 }
607 video->set_rtp_header_extensions(hdrexts);
608
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000609 *content = video.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000610 return true;
611}
612
613bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
614 ContentDescription** content,
615 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000616 talk_base::scoped_ptr<DataContentDescription> data(
617 new DataContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618 data->set_protocol(kMediaProtocolSctp);
619
620 for (const buzz::XmlElement* stream_elem =
621 content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
622 stream_elem != NULL;
623 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
624 StreamParams stream;
625 stream.groupid = stream_elem->Attr(QN_NICK);
626 stream.id = stream_elem->Attr(QN_NAME);
627 uint32 sid;
628 if (!talk_base::FromString(stream_elem->Attr(QN_SID), &sid)) {
629 return BadParse("Missing or invalid sid.", error);
630 }
631 if (sid > kMaxSctpSid) {
632 return BadParse("SID is greater than max value.", error);
633 }
634
635 stream.ssrcs.push_back(sid);
636 data->mutable_streams().push_back(stream);
637 }
638
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000639 *content = data.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000640 return true;
641}
642
643bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
644 ContentDescription** content,
645 ParseError* error) {
646 DataContentDescription* data = new DataContentDescription();
647
648 for (const buzz::XmlElement* payload_elem =
649 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
650 payload_elem != NULL;
651 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
652 DataCodec codec;
653 if (ParseJingleDataCodec(payload_elem, &codec)) {
654 data->AddCodec(codec);
655 }
656 }
657
658 if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
659 return false;
660 }
661 ParseBandwidth(content_elem, data);
662
663 if (!ParseJingleEncryption(content_elem, data, error)) {
664 return false;
665 }
666
667 data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
668
669 *content = data;
670 return true;
671}
672
673bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
674 const buzz::XmlElement* content_elem,
675 ContentDescription** content,
676 ParseError* error) {
677 if (protocol == PROTOCOL_GINGLE) {
678 const std::string& content_type = content_elem->Name().Namespace();
679 if (NS_GINGLE_AUDIO == content_type) {
680 return ParseGingleAudioContent(content_elem, content, error);
681 } else if (NS_GINGLE_VIDEO == content_type) {
682 return ParseGingleVideoContent(content_elem, content, error);
683 } else {
684 return BadParse("Unknown content type: " + content_type, error);
685 }
686 } else {
687 const std::string& content_type = content_elem->Name().Namespace();
688 // We use the XMLNS of the <description> element to determine if
689 // it's RTP or SCTP.
690 if (content_type == NS_JINGLE_DRAFT_SCTP) {
691 return ParseJingleSctpDataContent(content_elem, content, error);
692 }
693
694 std::string media;
695 if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
696 return false;
697
698 if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
699 return ParseJingleAudioContent(content_elem, content, error);
700 } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
701 return ParseJingleVideoContent(content_elem, content, error);
702 } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
703 return ParseJingleRtpDataContent(content_elem, content, error);
704 } else {
705 return BadParse("Unknown media: " + media, error);
706 }
707 }
708}
709
710buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
711 buzz::XmlElement* payload_type =
712 new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
713 AddXmlAttr(payload_type, QN_ID, codec.id);
714 payload_type->AddAttr(QN_NAME, codec.name);
715 if (codec.clockrate > 0)
716 AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
717 if (codec.bitrate > 0)
718 AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
719 if (codec.channels > 1)
720 AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
721 return payload_type;
722}
723
724buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
725 buzz::XmlElement* payload_type =
726 new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
727 AddXmlAttr(payload_type, QN_ID, codec.id);
728 payload_type->AddAttr(QN_NAME, codec.name);
729 AddXmlAttr(payload_type, QN_WIDTH, codec.width);
730 AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
731 AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
732 return payload_type;
733}
734
735buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
736 buzz::XmlElement* elem = new buzz::XmlElement(name, true);
737 if (ssrc) {
738 SetXmlBody(elem, ssrc);
739 }
740 return elem;
741}
742
743buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
744 int kbps = bps / 1000;
745 buzz::XmlElement* elem = new buzz::XmlElement(name);
746 elem->AddAttr(buzz::QN_TYPE, "AS");
747 SetXmlBody(elem, kbps);
748 return elem;
749}
750
751// For Jingle, usage_qname is empty.
752buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
753 bool required) {
754 buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
755
756 if (required) {
757 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
758 }
759
760 for (CryptoParamsVec::const_iterator i = cryptos.begin();
761 i != cryptos.end();
762 ++i) {
763 buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
764
765 AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
766 crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
767 crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
768 if (!i->session_params.empty()) {
769 crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
770 }
771 encryption_elem->AddElement(crypto_elem);
772 }
773 return encryption_elem;
774}
775
776buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
777 const buzz::QName& usage_qname,
778 bool required) {
779 buzz::XmlElement* encryption_elem =
780 CreateJingleEncryptionElem(cryptos, required);
781
782 if (required) {
783 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
784 }
785
786 buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
787 encryption_elem->AddElement(usage_elem);
788
789 return encryption_elem;
790}
791
792buzz::XmlElement* CreateGingleAudioContentElem(
793 const AudioContentDescription* audio,
794 bool crypto_required) {
795 buzz::XmlElement* elem =
796 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
797
798 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
799 codec != audio->codecs().end(); ++codec) {
800 elem->AddElement(CreateGingleAudioCodecElem(*codec));
801 }
802 if (audio->has_ssrcs()) {
803 elem->AddElement(CreateGingleSsrcElem(
804 QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
805 }
806
807 const CryptoParamsVec& cryptos = audio->cryptos();
808 if (!cryptos.empty()) {
809 elem->AddElement(CreateGingleEncryptionElem(cryptos,
810 QN_GINGLE_AUDIO_CRYPTO_USAGE,
811 crypto_required));
812 }
813 return elem;
814}
815
816buzz::XmlElement* CreateGingleVideoContentElem(
817 const VideoContentDescription* video,
818 bool crypto_required) {
819 buzz::XmlElement* elem =
820 new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
821
822 for (VideoCodecs::const_iterator codec = video->codecs().begin();
823 codec != video->codecs().end(); ++codec) {
824 elem->AddElement(CreateGingleVideoCodecElem(*codec));
825 }
826 if (video->has_ssrcs()) {
827 elem->AddElement(CreateGingleSsrcElem(
828 QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
829 }
830 if (video->bandwidth() != kAutoBandwidth) {
831 elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
832 video->bandwidth()));
833 }
834
835 const CryptoParamsVec& cryptos = video->cryptos();
836 if (!cryptos.empty()) {
837 elem->AddElement(CreateGingleEncryptionElem(cryptos,
838 QN_GINGLE_VIDEO_CRYPTO_USAGE,
839 crypto_required));
840 }
841
842 return elem;
843}
844
845template <class T>
846buzz::XmlElement* CreatePayloadTypeParameterElem(
847 const std::string& name, T value) {
848 buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
849
850 elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
851 AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
852
853 return elem;
854}
855
856buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
857 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
858
859 AddXmlAttr(elem, QN_ID, codec.id);
860 elem->AddAttr(QN_NAME, codec.name);
861 if (codec.clockrate > 0) {
862 AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
863 }
864 if (codec.bitrate > 0) {
865 elem->AddElement(CreatePayloadTypeParameterElem(
866 PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
867 }
868 if (codec.channels > 1) {
869 AddXmlAttr(elem, QN_CHANNELS, codec.channels);
870 }
871
872 return elem;
873}
874
875buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
876 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
877
878 AddXmlAttr(elem, QN_ID, codec.id);
879 elem->AddAttr(QN_NAME, codec.name);
880 elem->AddElement(CreatePayloadTypeParameterElem(
881 PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
882 elem->AddElement(CreatePayloadTypeParameterElem(
883 PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
884 elem->AddElement(CreatePayloadTypeParameterElem(
885 PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
886 CodecParameterMap::const_iterator param_iter;
887 for (param_iter = codec.params.begin(); param_iter != codec.params.end();
888 ++param_iter) {
889 elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
890 param_iter->second));
891 }
892
893 return elem;
894}
895
896buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
897 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
898
899 AddXmlAttr(elem, QN_ID, codec.id);
900 elem->AddAttr(QN_NAME, codec.name);
901
902 return elem;
903}
904
905void WriteLegacyJingleSsrc(const MediaContentDescription* media,
906 buzz::XmlElement* elem) {
907 if (media->has_ssrcs()) {
908 AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
909 }
910}
911
912void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
913 buzz::XmlElement* desc_elem) {
914 if (!media->multistream()) {
915 WriteLegacyJingleSsrc(media, desc_elem);
916 } else {
917 WriteJingleStreams(media->streams(), desc_elem);
918 }
919}
920
921buzz::XmlElement* CreateJingleAudioContentElem(
922 const AudioContentDescription* audio, bool crypto_required) {
923 buzz::XmlElement* elem =
924 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
925
926 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
927 WriteJingleStreamsOrLegacySsrc(audio, elem);
928
929 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
930 codec != audio->codecs().end(); ++codec) {
931 elem->AddElement(CreateJingleAudioCodecElem(*codec));
932 }
933
934 const CryptoParamsVec& cryptos = audio->cryptos();
935 if (!cryptos.empty()) {
936 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
937 }
938
939 if (audio->rtcp_mux()) {
940 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
941 }
942
943 WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
944
945 return elem;
946}
947
948buzz::XmlElement* CreateJingleVideoContentElem(
949 const VideoContentDescription* video, bool crypto_required) {
950 buzz::XmlElement* elem =
951 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
952
953 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
954 WriteJingleStreamsOrLegacySsrc(video, elem);
955
956 for (VideoCodecs::const_iterator codec = video->codecs().begin();
957 codec != video->codecs().end(); ++codec) {
958 elem->AddElement(CreateJingleVideoCodecElem(*codec));
959 }
960
961 const CryptoParamsVec& cryptos = video->cryptos();
962 if (!cryptos.empty()) {
963 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
964 }
965
966 if (video->rtcp_mux()) {
967 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
968 }
969
970 if (video->bandwidth() != kAutoBandwidth) {
971 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
972 video->bandwidth()));
973 }
974
975 WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
976
977 return elem;
978}
979
980buzz::XmlElement* CreateJingleSctpDataContentElem(
981 const DataContentDescription* data) {
982 buzz::XmlElement* content_elem =
983 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
984 for (std::vector<StreamParams>::const_iterator
985 stream = data->streams().begin();
986 stream != data->streams().end(); ++stream) {
987 buzz::XmlElement* stream_elem =
988 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
989 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
990 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
991 if (!stream->ssrcs.empty()) {
992 AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
993 }
994 content_elem->AddElement(stream_elem);
995 }
996 return content_elem;;
997}
998
999buzz::XmlElement* CreateJingleRtpDataContentElem(
1000 const DataContentDescription* data, bool crypto_required) {
1001
1002 buzz::XmlElement* elem =
1003 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
1004
1005 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
1006 WriteJingleStreamsOrLegacySsrc(data, elem);
1007
1008 for (DataCodecs::const_iterator codec = data->codecs().begin();
1009 codec != data->codecs().end(); ++codec) {
1010 elem->AddElement(CreateJingleDataCodecElem(*codec));
1011 }
1012
1013 const CryptoParamsVec& cryptos = data->cryptos();
1014 if (!cryptos.empty()) {
1015 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1016 }
1017
1018 if (data->rtcp_mux()) {
1019 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1020 }
1021
1022 if (data->bandwidth() != kAutoBandwidth) {
1023 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1024 data->bandwidth()));
1025 }
1026
1027 return elem;
1028}
1029
1030bool IsSctp(const DataContentDescription* data) {
1031 return (data->protocol() == kMediaProtocolSctp ||
1032 data->protocol() == kMediaProtocolDtlsSctp);
1033}
1034
1035buzz::XmlElement* CreateJingleDataContentElem(
1036 const DataContentDescription* data, bool crypto_required) {
1037 if (IsSctp(data)) {
1038 return CreateJingleSctpDataContentElem(data);
1039 } else {
1040 return CreateJingleRtpDataContentElem(data, crypto_required);
1041 }
1042}
1043
1044bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
1045 const ContentDescription* content) {
1046 const MediaContentDescription* media =
1047 static_cast<const MediaContentDescription*>(content);
1048 if (protocol == PROTOCOL_GINGLE &&
1049 media->type() == MEDIA_TYPE_DATA) {
1050 return false;
1051 }
1052 return true;
1053}
1054
1055bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
1056 const ContentDescription* content,
1057 buzz::XmlElement** elem,
1058 WriteError* error) {
1059 const MediaContentDescription* media =
1060 static_cast<const MediaContentDescription*>(content);
1061 bool crypto_required = secure() == SEC_REQUIRED;
1062
1063 if (media->type() == MEDIA_TYPE_AUDIO) {
1064 const AudioContentDescription* audio =
1065 static_cast<const AudioContentDescription*>(media);
1066 if (protocol == PROTOCOL_GINGLE) {
1067 *elem = CreateGingleAudioContentElem(audio, crypto_required);
1068 } else {
1069 *elem = CreateJingleAudioContentElem(audio, crypto_required);
1070 }
1071 } else if (media->type() == MEDIA_TYPE_VIDEO) {
1072 const VideoContentDescription* video =
1073 static_cast<const VideoContentDescription*>(media);
1074 if (protocol == PROTOCOL_GINGLE) {
1075 *elem = CreateGingleVideoContentElem(video, crypto_required);
1076 } else {
1077 *elem = CreateJingleVideoContentElem(video, crypto_required);
1078 }
1079 } else if (media->type() == MEDIA_TYPE_DATA) {
1080 const DataContentDescription* data =
1081 static_cast<const DataContentDescription*>(media);
1082 if (protocol == PROTOCOL_GINGLE) {
1083 return BadWrite("Data channel not supported with Gingle.", error);
1084 } else {
1085 *elem = CreateJingleDataContentElem(data, crypto_required);
1086 }
1087 } else {
1088 return BadWrite("Unknown content type: " +
1089 talk_base::ToString<int>(media->type()), error);
1090 }
1091
1092 return true;
1093}
1094
1095} // namespace cricket