blob: 584c342058f4434400fd8eaab5f45fdf4e9c232a [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) {
223 const std::string& type = NS_JINGLE_RTP;
224 Session *session = session_manager_->CreateSession(jid().Str(), type);
225 session_map_[session->id()] = call;
226 return session;
227}
228
229Call *MediaSessionClient::FindCallByRemoteName(const std::string &remote_name) {
230 SessionMap::const_iterator call;
231 for (call = session_map_.begin(); call != session_map_.end(); ++call) {
232 std::vector<Session *> sessions = call->second->sessions();
233 std::vector<Session *>::const_iterator session;
234 for (session = sessions.begin(); session != sessions.end(); ++session) {
235 if (remote_name == (*session)->remote_name()) {
236 return call->second;
237 }
238 }
239 }
240
241 return NULL;
242}
243
244// TODO(pthatcher): Move all of the parsing and writing functions into
245// mediamessages.cc, with unit tests.
246bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
247 int id = GetXmlAttr(element, QN_ID, -1);
248 if (id < 0)
249 return false;
250
251 std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
252 int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
253 int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
254 int channels = GetXmlAttr(element, QN_CHANNELS, 1);
255 *out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
256 return true;
257}
258
259bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
260 int id = GetXmlAttr(element, QN_ID, -1);
261 if (id < 0)
262 return false;
263
264 std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
265 int width = GetXmlAttr(element, QN_WIDTH, 0);
266 int height = GetXmlAttr(element, QN_HEIGHT, 0);
267 int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
268
269 *out = VideoCodec(id, name, width, height, framerate, 0);
270 return true;
271}
272
273// Parses an ssrc string as a legacy stream. If it fails, returns
274// false and fills an error message.
275bool ParseSsrcAsLegacyStream(const std::string& ssrc_str,
276 std::vector<StreamParams>* streams,
277 ParseError* error) {
278 if (!ssrc_str.empty()) {
279 uint32 ssrc;
280 if (!talk_base::FromString(ssrc_str, &ssrc)) {
281 return BadParse("Missing or invalid ssrc.", error);
282 }
283
284 streams->push_back(StreamParams::CreateLegacy(ssrc));
285 }
286 return true;
287}
288
289void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
290 const buzz::QName& name,
291 MediaContentDescription* media) {
292 const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
293 if (ssrc_elem) {
294 ParseError error;
295 ParseSsrcAsLegacyStream(
296 ssrc_elem->BodyText(), &(media->mutable_streams()), &error);
297 }
298}
299
300bool ParseCryptoParams(const buzz::XmlElement* element,
301 CryptoParams* out,
302 ParseError* error) {
303 if (!element->HasAttr(QN_CRYPTO_SUITE)) {
304 return BadParse("crypto: crypto-suite attribute missing ", error);
305 } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
306 return BadParse("crypto: key-params attribute missing ", error);
307 } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
308 return BadParse("crypto: tag attribute missing ", error);
309 }
310
311 const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
312 const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
313 const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
314 const std::string& session_params =
315 element->Attr(QN_CRYPTO_SESSION_PARAMS); // Optional.
316
317 *out = CryptoParams(tag, crypto_suite, key_params, session_params);
318 return true;
319}
320
321
322// Parse the first encryption element found with a matching 'usage'
323// element.
324// <usage/> is specific to Gingle. In Jingle, <crypto/> is already
325// scoped to a content.
326// Return false if there was an encryption element and it could not be
327// parsed.
328bool ParseGingleEncryption(const buzz::XmlElement* desc,
329 const buzz::QName& usage,
330 MediaContentDescription* media,
331 ParseError* error) {
332 for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
333 encryption != NULL;
334 encryption = encryption->NextNamed(QN_ENCRYPTION)) {
335 if (encryption->FirstNamed(usage) != NULL) {
336 media->set_crypto_required(
337 GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
338 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
339 crypto != NULL;
340 crypto = crypto->NextNamed(QN_CRYPTO)) {
341 CryptoParams params;
342 if (!ParseCryptoParams(crypto, &params, error)) {
343 return false;
344 }
345 media->AddCrypto(params);
346 }
347 break;
348 }
349 }
350 return true;
351}
352
353void ParseBandwidth(const buzz::XmlElement* parent_elem,
354 MediaContentDescription* media) {
355 const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
356 int bandwidth_kbps = -1;
357 if (bw_elem && talk_base::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
358 if (bandwidth_kbps >= 0) {
359 media->set_bandwidth(bandwidth_kbps * 1000);
360 }
361 }
362}
363
364bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
365 ContentDescription** content,
366 ParseError* error) {
367 AudioContentDescription* audio = new AudioContentDescription();
368
369 if (content_elem->FirstElement()) {
370 for (const buzz::XmlElement* codec_elem =
371 content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
372 codec_elem != NULL;
373 codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
374 AudioCodec codec;
375 if (ParseGingleAudioCodec(codec_elem, &codec)) {
376 audio->AddCodec(codec);
377 }
378 }
379 } else {
380 // For backward compatibility, we can assume the other client is
381 // an old version of Talk if it has no audio payload types at all.
382 audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
383 audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
384 }
385
386 ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
387
388 if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
389 audio, error)) {
390 return false;
391 }
392
393 *content = audio;
394 return true;
395}
396
397bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
398 ContentDescription** content,
399 ParseError* error) {
400 VideoContentDescription* video = new VideoContentDescription();
401
402 for (const buzz::XmlElement* codec_elem =
403 content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
404 codec_elem != NULL;
405 codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
406 VideoCodec codec;
407 if (ParseGingleVideoCodec(codec_elem, &codec)) {
408 video->AddCodec(codec);
409 }
410 }
411
412 ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
413 ParseBandwidth(content_elem, video);
414
415 if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
416 video, error)) {
417 return false;
418 }
419
420 *content = video;
421 return true;
422}
423
424void ParsePayloadTypeParameters(const buzz::XmlElement* element,
425 std::map<std::string, std::string>* paramap) {
426 for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
427 param != NULL; param = param->NextNamed(QN_PARAMETER)) {
428 std::string name = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
429 buzz::STR_EMPTY);
430 std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
431 buzz::STR_EMPTY);
432 if (!name.empty() && !value.empty()) {
433 paramap->insert(make_pair(name, value));
434 }
435 }
436}
437
438int FindWithDefault(const std::map<std::string, std::string>& map,
439 const std::string& key, const int def) {
440 std::map<std::string, std::string>::const_iterator iter = map.find(key);
441 return (iter == map.end()) ? def : atoi(iter->second.c_str());
442}
443
444
445// Parse the first encryption element found.
446// Return false if there was an encryption element and it could not be
447// parsed.
448bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
449 MediaContentDescription* media,
450 ParseError* error) {
451 const buzz::XmlElement* encryption =
452 content_elem->FirstNamed(QN_ENCRYPTION);
453 if (encryption == NULL) {
454 return true;
455 }
456
457 media->set_crypto_required(
458 GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
459
460 for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
461 crypto != NULL;
462 crypto = crypto->NextNamed(QN_CRYPTO)) {
463 CryptoParams params;
464 if (!ParseCryptoParams(crypto, &params, error)) {
465 return false;
466 }
467 media->AddCrypto(params);
468 }
469 return true;
470}
471
472bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
473 int id = GetXmlAttr(elem, QN_ID, -1);
474 if (id < 0)
475 return false;
476
477 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
478 int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
479 int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
480
481 std::map<std::string, std::string> paramap;
482 ParsePayloadTypeParameters(elem, &paramap);
483 int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
484
485 *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
486 return true;
487}
488
489bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
490 int id = GetXmlAttr(elem, QN_ID, -1);
491 if (id < 0)
492 return false;
493
494 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
495
496 std::map<std::string, std::string> paramap;
497 ParsePayloadTypeParameters(elem, &paramap);
498 int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
499 int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
500 int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
501
502 *codec = VideoCodec(id, name, width, height, framerate, 0);
503 codec->params = paramap;
504 return true;
505}
506
507bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
508 int id = GetXmlAttr(elem, QN_ID, -1);
509 if (id < 0)
510 return false;
511
512 std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
513
514 *codec = DataCodec(id, name, 0);
515 return true;
516}
517
518bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
519 MediaContentDescription* media,
520 ParseError* error) {
521 if (HasJingleStreams(desc_elem)) {
522 if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
523 return false;
524 }
525 } else {
526 const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
527 if (!ParseSsrcAsLegacyStream(
528 ssrc_str, &(media->mutable_streams()), error)) {
529 return false;
530 }
531 }
532 return true;
533}
534
535bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
536 ContentDescription** content,
537 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000538 talk_base::scoped_ptr<AudioContentDescription> audio(
539 new AudioContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000540
541 for (const buzz::XmlElement* payload_elem =
542 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
543 payload_elem != NULL;
544 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
545 AudioCodec codec;
546 if (ParseJingleAudioCodec(payload_elem, &codec)) {
547 audio->AddCodec(codec);
548 }
549 }
550
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000551 if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000552 return false;
553 }
554
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000555 if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000556 return false;
557 }
558
559 audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
560
561 RtpHeaderExtensions hdrexts;
562 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
563 return false;
564 }
565 audio->set_rtp_header_extensions(hdrexts);
566
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000567 *content = audio.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568 return true;
569}
570
571bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
572 ContentDescription** content,
573 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000574 talk_base::scoped_ptr<VideoContentDescription> video(
575 new VideoContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000576
577 for (const buzz::XmlElement* payload_elem =
578 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
579 payload_elem != NULL;
580 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
581 VideoCodec codec;
582 if (ParseJingleVideoCodec(payload_elem, &codec)) {
583 video->AddCodec(codec);
584 }
585 }
586
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000587 if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588 return false;
589 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000590 ParseBandwidth(content_elem, video.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000591
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000592 if (!ParseJingleEncryption(content_elem, video.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 return false;
594 }
595
596 video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
597
598 RtpHeaderExtensions hdrexts;
599 if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
600 return false;
601 }
602 video->set_rtp_header_extensions(hdrexts);
603
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000604 *content = video.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 return true;
606}
607
608bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
609 ContentDescription** content,
610 ParseError* error) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000611 talk_base::scoped_ptr<DataContentDescription> data(
612 new DataContentDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 data->set_protocol(kMediaProtocolSctp);
614
615 for (const buzz::XmlElement* stream_elem =
616 content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
617 stream_elem != NULL;
618 stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
619 StreamParams stream;
620 stream.groupid = stream_elem->Attr(QN_NICK);
621 stream.id = stream_elem->Attr(QN_NAME);
622 uint32 sid;
623 if (!talk_base::FromString(stream_elem->Attr(QN_SID), &sid)) {
624 return BadParse("Missing or invalid sid.", error);
625 }
626 if (sid > kMaxSctpSid) {
627 return BadParse("SID is greater than max value.", error);
628 }
629
630 stream.ssrcs.push_back(sid);
631 data->mutable_streams().push_back(stream);
632 }
633
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000634 *content = data.release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000635 return true;
636}
637
638bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
639 ContentDescription** content,
640 ParseError* error) {
641 DataContentDescription* data = new DataContentDescription();
642
643 for (const buzz::XmlElement* payload_elem =
644 content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
645 payload_elem != NULL;
646 payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
647 DataCodec codec;
648 if (ParseJingleDataCodec(payload_elem, &codec)) {
649 data->AddCodec(codec);
650 }
651 }
652
653 if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
654 return false;
655 }
656 ParseBandwidth(content_elem, data);
657
658 if (!ParseJingleEncryption(content_elem, data, error)) {
659 return false;
660 }
661
662 data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
663
664 *content = data;
665 return true;
666}
667
668bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
669 const buzz::XmlElement* content_elem,
670 ContentDescription** content,
671 ParseError* error) {
672 if (protocol == PROTOCOL_GINGLE) {
673 const std::string& content_type = content_elem->Name().Namespace();
674 if (NS_GINGLE_AUDIO == content_type) {
675 return ParseGingleAudioContent(content_elem, content, error);
676 } else if (NS_GINGLE_VIDEO == content_type) {
677 return ParseGingleVideoContent(content_elem, content, error);
678 } else {
679 return BadParse("Unknown content type: " + content_type, error);
680 }
681 } else {
682 const std::string& content_type = content_elem->Name().Namespace();
683 // We use the XMLNS of the <description> element to determine if
684 // it's RTP or SCTP.
685 if (content_type == NS_JINGLE_DRAFT_SCTP) {
686 return ParseJingleSctpDataContent(content_elem, content, error);
687 }
688
689 std::string media;
690 if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
691 return false;
692
693 if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
694 return ParseJingleAudioContent(content_elem, content, error);
695 } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
696 return ParseJingleVideoContent(content_elem, content, error);
697 } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
698 return ParseJingleRtpDataContent(content_elem, content, error);
699 } else {
700 return BadParse("Unknown media: " + media, error);
701 }
702 }
703}
704
705buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
706 buzz::XmlElement* payload_type =
707 new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
708 AddXmlAttr(payload_type, QN_ID, codec.id);
709 payload_type->AddAttr(QN_NAME, codec.name);
710 if (codec.clockrate > 0)
711 AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
712 if (codec.bitrate > 0)
713 AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
714 if (codec.channels > 1)
715 AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
716 return payload_type;
717}
718
719buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
720 buzz::XmlElement* payload_type =
721 new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
722 AddXmlAttr(payload_type, QN_ID, codec.id);
723 payload_type->AddAttr(QN_NAME, codec.name);
724 AddXmlAttr(payload_type, QN_WIDTH, codec.width);
725 AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
726 AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
727 return payload_type;
728}
729
730buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
731 buzz::XmlElement* elem = new buzz::XmlElement(name, true);
732 if (ssrc) {
733 SetXmlBody(elem, ssrc);
734 }
735 return elem;
736}
737
738buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
739 int kbps = bps / 1000;
740 buzz::XmlElement* elem = new buzz::XmlElement(name);
741 elem->AddAttr(buzz::QN_TYPE, "AS");
742 SetXmlBody(elem, kbps);
743 return elem;
744}
745
746// For Jingle, usage_qname is empty.
747buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
748 bool required) {
749 buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
750
751 if (required) {
752 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
753 }
754
755 for (CryptoParamsVec::const_iterator i = cryptos.begin();
756 i != cryptos.end();
757 ++i) {
758 buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
759
760 AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
761 crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
762 crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
763 if (!i->session_params.empty()) {
764 crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
765 }
766 encryption_elem->AddElement(crypto_elem);
767 }
768 return encryption_elem;
769}
770
771buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
772 const buzz::QName& usage_qname,
773 bool required) {
774 buzz::XmlElement* encryption_elem =
775 CreateJingleEncryptionElem(cryptos, required);
776
777 if (required) {
778 encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
779 }
780
781 buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
782 encryption_elem->AddElement(usage_elem);
783
784 return encryption_elem;
785}
786
787buzz::XmlElement* CreateGingleAudioContentElem(
788 const AudioContentDescription* audio,
789 bool crypto_required) {
790 buzz::XmlElement* elem =
791 new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
792
793 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
794 codec != audio->codecs().end(); ++codec) {
795 elem->AddElement(CreateGingleAudioCodecElem(*codec));
796 }
797 if (audio->has_ssrcs()) {
798 elem->AddElement(CreateGingleSsrcElem(
799 QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
800 }
801
802 const CryptoParamsVec& cryptos = audio->cryptos();
803 if (!cryptos.empty()) {
804 elem->AddElement(CreateGingleEncryptionElem(cryptos,
805 QN_GINGLE_AUDIO_CRYPTO_USAGE,
806 crypto_required));
807 }
808 return elem;
809}
810
811buzz::XmlElement* CreateGingleVideoContentElem(
812 const VideoContentDescription* video,
813 bool crypto_required) {
814 buzz::XmlElement* elem =
815 new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
816
817 for (VideoCodecs::const_iterator codec = video->codecs().begin();
818 codec != video->codecs().end(); ++codec) {
819 elem->AddElement(CreateGingleVideoCodecElem(*codec));
820 }
821 if (video->has_ssrcs()) {
822 elem->AddElement(CreateGingleSsrcElem(
823 QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
824 }
825 if (video->bandwidth() != kAutoBandwidth) {
826 elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
827 video->bandwidth()));
828 }
829
830 const CryptoParamsVec& cryptos = video->cryptos();
831 if (!cryptos.empty()) {
832 elem->AddElement(CreateGingleEncryptionElem(cryptos,
833 QN_GINGLE_VIDEO_CRYPTO_USAGE,
834 crypto_required));
835 }
836
837 return elem;
838}
839
840template <class T>
841buzz::XmlElement* CreatePayloadTypeParameterElem(
842 const std::string& name, T value) {
843 buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
844
845 elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
846 AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
847
848 return elem;
849}
850
851buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
852 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
853
854 AddXmlAttr(elem, QN_ID, codec.id);
855 elem->AddAttr(QN_NAME, codec.name);
856 if (codec.clockrate > 0) {
857 AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
858 }
859 if (codec.bitrate > 0) {
860 elem->AddElement(CreatePayloadTypeParameterElem(
861 PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
862 }
863 if (codec.channels > 1) {
864 AddXmlAttr(elem, QN_CHANNELS, codec.channels);
865 }
866
867 return elem;
868}
869
870buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
871 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
872
873 AddXmlAttr(elem, QN_ID, codec.id);
874 elem->AddAttr(QN_NAME, codec.name);
875 elem->AddElement(CreatePayloadTypeParameterElem(
876 PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
877 elem->AddElement(CreatePayloadTypeParameterElem(
878 PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
879 elem->AddElement(CreatePayloadTypeParameterElem(
880 PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
881 CodecParameterMap::const_iterator param_iter;
882 for (param_iter = codec.params.begin(); param_iter != codec.params.end();
883 ++param_iter) {
884 elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
885 param_iter->second));
886 }
887
888 return elem;
889}
890
891buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
892 buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
893
894 AddXmlAttr(elem, QN_ID, codec.id);
895 elem->AddAttr(QN_NAME, codec.name);
896
897 return elem;
898}
899
900void WriteLegacyJingleSsrc(const MediaContentDescription* media,
901 buzz::XmlElement* elem) {
902 if (media->has_ssrcs()) {
903 AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
904 }
905}
906
907void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
908 buzz::XmlElement* desc_elem) {
909 if (!media->multistream()) {
910 WriteLegacyJingleSsrc(media, desc_elem);
911 } else {
912 WriteJingleStreams(media->streams(), desc_elem);
913 }
914}
915
916buzz::XmlElement* CreateJingleAudioContentElem(
917 const AudioContentDescription* audio, bool crypto_required) {
918 buzz::XmlElement* elem =
919 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
920
921 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
922 WriteJingleStreamsOrLegacySsrc(audio, elem);
923
924 for (AudioCodecs::const_iterator codec = audio->codecs().begin();
925 codec != audio->codecs().end(); ++codec) {
926 elem->AddElement(CreateJingleAudioCodecElem(*codec));
927 }
928
929 const CryptoParamsVec& cryptos = audio->cryptos();
930 if (!cryptos.empty()) {
931 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
932 }
933
934 if (audio->rtcp_mux()) {
935 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
936 }
937
938 WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
939
940 return elem;
941}
942
943buzz::XmlElement* CreateJingleVideoContentElem(
944 const VideoContentDescription* video, bool crypto_required) {
945 buzz::XmlElement* elem =
946 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
947
948 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
949 WriteJingleStreamsOrLegacySsrc(video, elem);
950
951 for (VideoCodecs::const_iterator codec = video->codecs().begin();
952 codec != video->codecs().end(); ++codec) {
953 elem->AddElement(CreateJingleVideoCodecElem(*codec));
954 }
955
956 const CryptoParamsVec& cryptos = video->cryptos();
957 if (!cryptos.empty()) {
958 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
959 }
960
961 if (video->rtcp_mux()) {
962 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
963 }
964
965 if (video->bandwidth() != kAutoBandwidth) {
966 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
967 video->bandwidth()));
968 }
969
970 WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
971
972 return elem;
973}
974
975buzz::XmlElement* CreateJingleSctpDataContentElem(
976 const DataContentDescription* data) {
977 buzz::XmlElement* content_elem =
978 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
979 for (std::vector<StreamParams>::const_iterator
980 stream = data->streams().begin();
981 stream != data->streams().end(); ++stream) {
982 buzz::XmlElement* stream_elem =
983 new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
984 AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
985 AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
986 if (!stream->ssrcs.empty()) {
987 AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
988 }
989 content_elem->AddElement(stream_elem);
990 }
991 return content_elem;;
992}
993
994buzz::XmlElement* CreateJingleRtpDataContentElem(
995 const DataContentDescription* data, bool crypto_required) {
996
997 buzz::XmlElement* elem =
998 new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
999
1000 elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
1001 WriteJingleStreamsOrLegacySsrc(data, elem);
1002
1003 for (DataCodecs::const_iterator codec = data->codecs().begin();
1004 codec != data->codecs().end(); ++codec) {
1005 elem->AddElement(CreateJingleDataCodecElem(*codec));
1006 }
1007
1008 const CryptoParamsVec& cryptos = data->cryptos();
1009 if (!cryptos.empty()) {
1010 elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
1011 }
1012
1013 if (data->rtcp_mux()) {
1014 elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
1015 }
1016
1017 if (data->bandwidth() != kAutoBandwidth) {
1018 elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
1019 data->bandwidth()));
1020 }
1021
1022 return elem;
1023}
1024
1025bool IsSctp(const DataContentDescription* data) {
1026 return (data->protocol() == kMediaProtocolSctp ||
1027 data->protocol() == kMediaProtocolDtlsSctp);
1028}
1029
1030buzz::XmlElement* CreateJingleDataContentElem(
1031 const DataContentDescription* data, bool crypto_required) {
1032 if (IsSctp(data)) {
1033 return CreateJingleSctpDataContentElem(data);
1034 } else {
1035 return CreateJingleRtpDataContentElem(data, crypto_required);
1036 }
1037}
1038
1039bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
1040 const ContentDescription* content) {
1041 const MediaContentDescription* media =
1042 static_cast<const MediaContentDescription*>(content);
1043 if (protocol == PROTOCOL_GINGLE &&
1044 media->type() == MEDIA_TYPE_DATA) {
1045 return false;
1046 }
1047 return true;
1048}
1049
1050bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
1051 const ContentDescription* content,
1052 buzz::XmlElement** elem,
1053 WriteError* error) {
1054 const MediaContentDescription* media =
1055 static_cast<const MediaContentDescription*>(content);
1056 bool crypto_required = secure() == SEC_REQUIRED;
1057
1058 if (media->type() == MEDIA_TYPE_AUDIO) {
1059 const AudioContentDescription* audio =
1060 static_cast<const AudioContentDescription*>(media);
1061 if (protocol == PROTOCOL_GINGLE) {
1062 *elem = CreateGingleAudioContentElem(audio, crypto_required);
1063 } else {
1064 *elem = CreateJingleAudioContentElem(audio, crypto_required);
1065 }
1066 } else if (media->type() == MEDIA_TYPE_VIDEO) {
1067 const VideoContentDescription* video =
1068 static_cast<const VideoContentDescription*>(media);
1069 if (protocol == PROTOCOL_GINGLE) {
1070 *elem = CreateGingleVideoContentElem(video, crypto_required);
1071 } else {
1072 *elem = CreateJingleVideoContentElem(video, crypto_required);
1073 }
1074 } else if (media->type() == MEDIA_TYPE_DATA) {
1075 const DataContentDescription* data =
1076 static_cast<const DataContentDescription*>(media);
1077 if (protocol == PROTOCOL_GINGLE) {
1078 return BadWrite("Data channel not supported with Gingle.", error);
1079 } else {
1080 *elem = CreateJingleDataContentElem(data, crypto_required);
1081 }
1082 } else {
1083 return BadWrite("Unknown content type: " +
1084 talk_base::ToString<int>(media->type()), error);
1085 }
1086
1087 return true;
1088}
1089
1090} // namespace cricket