blob: 4d1fc29f7102bd8af68d2ab71d8ec03d012de2ae [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 "talk/session/media/mediasession.h"
29
30#include <functional>
31#include <map>
32#include <set>
33#include <utility>
34
35#include "talk/base/helpers.h"
36#include "talk/base/logging.h"
37#include "talk/base/scoped_ptr.h"
38#include "talk/base/stringutils.h"
39#include "talk/media/base/constants.h"
40#include "talk/media/base/cryptoparams.h"
41#include "talk/p2p/base/constants.h"
42#include "talk/session/media/channelmanager.h"
43#include "talk/session/media/srtpfilter.h"
44#include "talk/xmpp/constants.h"
45
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000046#ifdef HAVE_SCTP
47#include "talk/media/sctp/sctpdataengine.h"
48#else
wu@webrtc.org97077a32013-10-25 21:18:33 +000049static const uint32 kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000050#endif
51
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052namespace {
53const char kInline[] = "inline:";
54}
55
56namespace cricket {
57
58using talk_base::scoped_ptr;
59
60// RTP Profile names
61// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
62// RFC4585
63const char kMediaProtocolAvpf[] = "RTP/AVPF";
64// RFC5124
65const char kMediaProtocolSavpf[] = "RTP/SAVPF";
66
67const char kMediaProtocolRtpPrefix[] = "RTP/";
68
69const char kMediaProtocolSctp[] = "SCTP";
70const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
71
72static bool IsMediaContentOfType(const ContentInfo* content,
73 MediaType media_type) {
74 if (!IsMediaContent(content)) {
75 return false;
76 }
77
78 const MediaContentDescription* mdesc =
79 static_cast<const MediaContentDescription*>(content->description);
80 return mdesc && mdesc->type() == media_type;
81}
82
83static bool CreateCryptoParams(int tag, const std::string& cipher,
84 CryptoParams *out) {
85 std::string key;
86 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
87
88 if (!talk_base::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
89 return false;
90 }
91 out->tag = tag;
92 out->cipher_suite = cipher;
93 out->key_params = kInline;
94 out->key_params += key;
95 return true;
96}
97
98#ifdef HAVE_SRTP
99static bool AddCryptoParams(const std::string& cipher_suite,
100 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000101 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102
103 out->resize(size + 1);
104 return CreateCryptoParams(size, cipher_suite, &out->at(size));
105}
106
107void AddMediaCryptos(const CryptoParamsVec& cryptos,
108 MediaContentDescription* media) {
109 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
110 crypto != cryptos.end(); ++crypto) {
111 media->AddCrypto(*crypto);
112 }
113}
114
115bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
116 MediaContentDescription* media) {
117 CryptoParamsVec cryptos;
118 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
119 it != crypto_suites.end(); ++it) {
120 if (!AddCryptoParams(*it, &cryptos)) {
121 return false;
122 }
123 }
124 AddMediaCryptos(cryptos, media);
125 return true;
126}
127#endif
128
129const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
130 if (!media) {
131 return NULL;
132 }
133 return &media->cryptos();
134}
135
136bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
137 const CryptoParams& crypto,
138 CryptoParams* out) {
139 for (CryptoParamsVec::const_iterator it = cryptos.begin();
140 it != cryptos.end(); ++it) {
141 if (crypto.Matches(*it)) {
142 *out = *it;
143 return true;
144 }
145 }
146 return false;
147}
148
149// For audio, HMAC 32 is prefered because of the low overhead.
150void GetSupportedAudioCryptoSuites(
151 std::vector<std::string>* crypto_suites) {
152#ifdef HAVE_SRTP
153 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_32);
154 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
155#endif
156}
157
158void GetSupportedVideoCryptoSuites(
159 std::vector<std::string>* crypto_suites) {
160 GetSupportedDefaultCryptoSuites(crypto_suites);
161}
162
163void GetSupportedDataCryptoSuites(
164 std::vector<std::string>* crypto_suites) {
165 GetSupportedDefaultCryptoSuites(crypto_suites);
166}
167
168void GetSupportedDefaultCryptoSuites(
169 std::vector<std::string>* crypto_suites) {
170#ifdef HAVE_SRTP
171 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
172#endif
173}
174
175// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
176// tolerated unless bundle is enabled because it is low overhead. Pick the
177// crypto in the list that is supported.
178static bool SelectCrypto(const MediaContentDescription* offer,
179 bool bundle,
180 CryptoParams *crypto) {
181 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
182 const CryptoParamsVec& cryptos = offer->cryptos();
183
184 for (CryptoParamsVec::const_iterator i = cryptos.begin();
185 i != cryptos.end(); ++i) {
186 if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
187 (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && !bundle)) {
188 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
189 }
190 }
191 return false;
192}
193
194static const StreamParams* FindFirstStreamParamsByCname(
195 const StreamParamsVec& params_vec,
196 const std::string& cname) {
197 for (StreamParamsVec::const_iterator it = params_vec.begin();
198 it != params_vec.end(); ++it) {
199 if (cname == it->cname)
200 return &*it;
201 }
202 return NULL;
203}
204
205// Generates a new CNAME or the CNAME of an already existing StreamParams
206// if a StreamParams exist for another Stream in streams with sync_label
207// sync_label.
208static bool GenerateCname(const StreamParamsVec& params_vec,
209 const MediaSessionOptions::Streams& streams,
210 const std::string& synch_label,
211 std::string* cname) {
212 ASSERT(cname != NULL);
213 if (!cname)
214 return false;
215
216 // Check if a CNAME exist for any of the other synched streams.
217 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
218 stream_it != streams.end() ; ++stream_it) {
219 if (synch_label != stream_it->sync_label)
220 continue;
221
222 StreamParams param;
223 // groupid is empty for StreamParams generated using
224 // MediaSessionDescriptionFactory.
225 if (GetStreamByIds(params_vec, "", stream_it->id,
226 &param)) {
227 *cname = param.cname;
228 return true;
229 }
230 }
231 // No other stream seems to exist that we should sync with.
232 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
233 // This string is only used for synchronization, and therefore is opaque.
234 do {
235 if (!talk_base::CreateRandomString(16, cname)) {
236 ASSERT(false);
237 return false;
238 }
239 } while (FindFirstStreamParamsByCname(params_vec, *cname));
240
241 return true;
242}
243
244// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000245// The generated values are added to |ssrcs|.
246// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000248 int num_ssrcs,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 std::vector<uint32>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000250 for (int i = 0; i < num_ssrcs; i++) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251 uint32 candidate;
252 do {
253 candidate = talk_base::CreateRandomNonZeroId();
254 } while (GetStreamBySsrc(params_vec, candidate, NULL) ||
255 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
256 ssrcs->push_back(candidate);
257 }
258}
259
260// Returns false if we exhaust the range of SIDs.
261static bool GenerateSctpSid(const StreamParamsVec& params_vec,
262 uint32* sid) {
263 if (params_vec.size() > kMaxSctpSid) {
264 LOG(LS_WARNING) <<
265 "Could not generate an SCTP SID: too many SCTP streams.";
266 return false;
267 }
268 while (true) {
269 uint32 candidate = talk_base::CreateRandomNonZeroId() % kMaxSctpSid;
270 if (!GetStreamBySsrc(params_vec, candidate, NULL)) {
271 *sid = candidate;
272 return true;
273 }
274 }
275}
276
277static bool GenerateSctpSids(const StreamParamsVec& params_vec,
278 std::vector<uint32>* sids) {
279 uint32 sid;
280 if (!GenerateSctpSid(params_vec, &sid)) {
281 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
282 return false;
283 }
284 sids->push_back(sid);
285 return true;
286}
287
288// Finds all StreamParams of all media types and attach them to stream_params.
289static void GetCurrentStreamParams(const SessionDescription* sdesc,
290 StreamParamsVec* stream_params) {
291 if (!sdesc)
292 return;
293
294 const ContentInfos& contents = sdesc->contents();
295 for (ContentInfos::const_iterator content = contents.begin();
296 content != contents.end(); ++content) {
297 if (!IsMediaContent(&*content)) {
298 continue;
299 }
300 const MediaContentDescription* media =
301 static_cast<const MediaContentDescription*>(
302 content->description);
303 const StreamParamsVec& streams = media->streams();
304 for (StreamParamsVec::const_iterator it = streams.begin();
305 it != streams.end(); ++it) {
306 stream_params->push_back(*it);
307 }
308 }
309}
310
311template <typename IdStruct>
312class UsedIds {
313 public:
314 UsedIds(int min_allowed_id, int max_allowed_id)
315 : min_allowed_id_(min_allowed_id),
316 max_allowed_id_(max_allowed_id),
317 next_id_(max_allowed_id) {
318 }
319
320 // Loops through all Id in |ids| and changes its id if it is
321 // already in use by another IdStruct. Call this methods with all Id
322 // in a session description to make sure no duplicate ids exists.
323 // Note that typename Id must be a type of IdStruct.
324 template <typename Id>
325 void FindAndSetIdUsed(std::vector<Id>* ids) {
326 for (typename std::vector<Id>::iterator it = ids->begin();
327 it != ids->end(); ++it) {
328 FindAndSetIdUsed(&*it);
329 }
330 }
331
332 // Finds and sets an unused id if the |idstruct| id is already in use.
333 void FindAndSetIdUsed(IdStruct* idstruct) {
334 const int original_id = idstruct->id;
335 int new_id = idstruct->id;
336
337 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
338 // If the original id is not in range - this is an id that can't be
339 // dynamically changed.
340 return;
341 }
342
343 if (IsIdUsed(original_id)) {
344 new_id = FindUnusedId();
345 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
346 << " to " << new_id;
347 idstruct->id = new_id;
348 }
349 SetIdUsed(new_id);
350 }
351
352 private:
353 // Returns the first unused id in reverse order.
354 // This hopefully reduce the risk of more collisions. We want to change the
355 // default ids as little as possible.
356 int FindUnusedId() {
357 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
358 --next_id_;
359 }
360 ASSERT(next_id_ >= min_allowed_id_);
361 return next_id_;
362 }
363
364 bool IsIdUsed(int new_id) {
365 return id_set_.find(new_id) != id_set_.end();
366 }
367
368 void SetIdUsed(int new_id) {
369 id_set_.insert(new_id);
370 }
371
372 const int min_allowed_id_;
373 const int max_allowed_id_;
374 int next_id_;
375 std::set<int> id_set_;
376};
377
378// Helper class used for finding duplicate RTP payload types among audio, video
379// and data codecs. When bundle is used the payload types may not collide.
380class UsedPayloadTypes : public UsedIds<Codec> {
381 public:
382 UsedPayloadTypes()
383 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
384 }
385
386
387 private:
388 static const int kDynamicPayloadTypeMin = 96;
389 static const int kDynamicPayloadTypeMax = 127;
390};
391
392// Helper class used for finding duplicate RTP Header extension ids among
393// audio and video extensions.
394class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
395 public:
396 UsedRtpHeaderExtensionIds()
397 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
398 }
399
400 private:
401 // Min and Max local identifier as specified by RFC5285.
402 static const int kLocalIdMin = 1;
403 static const int kLocalIdMax = 255;
404};
405
406static bool IsSctp(const MediaContentDescription* desc) {
407 return ((desc->protocol() == kMediaProtocolSctp) ||
408 (desc->protocol() == kMediaProtocolDtlsSctp));
409}
410
411// Adds a StreamParams for each Stream in Streams with media type
412// media_type to content_description.
413// |current_params| - All currently known StreamParams of any media type.
414template <class C>
415static bool AddStreamParams(
416 MediaType media_type,
417 const MediaSessionOptions::Streams& streams,
418 StreamParamsVec* current_streams,
419 MediaContentDescriptionImpl<C>* content_description,
420 const bool add_legacy_stream) {
421 const bool include_rtx_stream =
422 ContainsRtxCodec(content_description->codecs());
423
424 if (streams.empty() && add_legacy_stream) {
425 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
426 std::vector<uint32> ssrcs;
427 if (IsSctp(content_description)) {
428 GenerateSctpSids(*current_streams, &ssrcs);
429 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000430 int num_ssrcs = include_rtx_stream ? 2 : 1;
431 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 }
433 if (include_rtx_stream) {
434 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
435 content_description->set_multistream(true);
436 } else {
437 content_description->AddLegacyStream(ssrcs[0]);
438 }
439 return true;
440 }
441
442 MediaSessionOptions::Streams::const_iterator stream_it;
443 for (stream_it = streams.begin();
444 stream_it != streams.end(); ++stream_it) {
445 if (stream_it->type != media_type)
446 continue; // Wrong media type.
447
448 StreamParams param;
449 // groupid is empty for StreamParams generated using
450 // MediaSessionDescriptionFactory.
451 if (!GetStreamByIds(*current_streams, "", stream_it->id,
452 &param)) {
453 // This is a new stream.
454 // Get a CNAME. Either new or same as one of the other synched streams.
455 std::string cname;
456 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
457 &cname)) {
458 return false;
459 }
460
461 std::vector<uint32> ssrcs;
462 if (IsSctp(content_description)) {
463 GenerateSctpSids(*current_streams, &ssrcs);
464 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000465 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466 }
467 StreamParams stream_param;
468 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000469 // Add the generated ssrc.
470 for (size_t i = 0; i < ssrcs.size(); ++i) {
471 stream_param.ssrcs.push_back(ssrcs[i]);
472 }
473 if (stream_it->num_sim_layers > 1) {
474 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
475 stream_param.ssrc_groups.push_back(group);
476 }
477 // Generate an extra ssrc for include_rtx_stream case.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000478 if (include_rtx_stream) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000479 std::vector<uint32> rtx_ssrc;
480 GenerateSsrcs(*current_streams, 1, &rtx_ssrc);
481 stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 content_description->set_multistream(true);
483 }
484 stream_param.cname = cname;
485 stream_param.sync_label = stream_it->sync_label;
486 content_description->AddStream(stream_param);
487
488 // Store the new StreamParams in current_streams.
489 // This is necessary so that we can use the CNAME for other media types.
490 current_streams->push_back(stream_param);
491 } else {
492 content_description->AddStream(param);
493 }
494 }
495 return true;
496}
497
498// Updates the transport infos of the |sdesc| according to the given
499// |bundle_group|. The transport infos of the content names within the
500// |bundle_group| should be updated to use the ufrag and pwd of the first
501// content within the |bundle_group|.
502static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
503 SessionDescription* sdesc) {
504 // The bundle should not be empty.
505 if (!sdesc || !bundle_group.FirstContentName()) {
506 return false;
507 }
508
509 // We should definitely have a transport for the first content.
510 std::string selected_content_name = *bundle_group.FirstContentName();
511 const TransportInfo* selected_transport_info =
512 sdesc->GetTransportInfoByName(selected_content_name);
513 if (!selected_transport_info) {
514 return false;
515 }
516
517 // Set the other contents to use the same ICE credentials.
518 const std::string selected_ufrag =
519 selected_transport_info->description.ice_ufrag;
520 const std::string selected_pwd =
521 selected_transport_info->description.ice_pwd;
522 for (TransportInfos::iterator it =
523 sdesc->transport_infos().begin();
524 it != sdesc->transport_infos().end(); ++it) {
525 if (bundle_group.HasContentName(it->content_name) &&
526 it->content_name != selected_content_name) {
527 it->description.ice_ufrag = selected_ufrag;
528 it->description.ice_pwd = selected_pwd;
529 }
530 }
531 return true;
532}
533
534// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
535// sets it to |cryptos|.
536static bool GetCryptosByName(const SessionDescription* sdesc,
537 const std::string& content_name,
538 CryptoParamsVec* cryptos) {
539 if (!sdesc || !cryptos) {
540 return false;
541 }
542
543 const ContentInfo* content = sdesc->GetContentByName(content_name);
544 if (!IsMediaContent(content) || !content->description) {
545 return false;
546 }
547
548 const MediaContentDescription* media_desc =
549 static_cast<const MediaContentDescription*>(content->description);
550 *cryptos = media_desc->cryptos();
551 return true;
552}
553
554// Predicate function used by the remove_if.
555// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
556static bool CryptoNotFound(const CryptoParams crypto,
557 const CryptoParamsVec* filter) {
558 if (filter == NULL) {
559 return true;
560 }
561 for (CryptoParamsVec::const_iterator it = filter->begin();
562 it != filter->end(); ++it) {
563 if (it->cipher_suite == crypto.cipher_suite) {
564 return false;
565 }
566 }
567 return true;
568}
569
570// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
571// which are not available in |filter|.
572static void PruneCryptos(const CryptoParamsVec& filter,
573 CryptoParamsVec* target_cryptos) {
574 if (!target_cryptos) {
575 return;
576 }
577 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
578 target_cryptos->end(),
579 bind2nd(ptr_fun(CryptoNotFound),
580 &filter)),
581 target_cryptos->end());
582}
583
584static bool IsRtpContent(SessionDescription* sdesc,
585 const std::string& content_name) {
586 bool is_rtp = false;
587 ContentInfo* content = sdesc->GetContentByName(content_name);
588 if (IsMediaContent(content)) {
589 MediaContentDescription* media_desc =
590 static_cast<MediaContentDescription*>(content->description);
591 if (!media_desc) {
592 return false;
593 }
594 is_rtp = media_desc->protocol().empty() ||
595 talk_base::starts_with(media_desc->protocol().data(),
596 kMediaProtocolRtpPrefix);
597 }
598 return is_rtp;
599}
600
601// Updates the crypto parameters of the |sdesc| according to the given
602// |bundle_group|. The crypto parameters of all the contents within the
603// |bundle_group| should be updated to use the common subset of the
604// available cryptos.
605static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
606 SessionDescription* sdesc) {
607 // The bundle should not be empty.
608 if (!sdesc || !bundle_group.FirstContentName()) {
609 return false;
610 }
611
wu@webrtc.org78187522013-10-07 23:32:02 +0000612 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 // Get the common cryptos.
614 const ContentNames& content_names = bundle_group.content_names();
615 CryptoParamsVec common_cryptos;
616 for (ContentNames::const_iterator it = content_names.begin();
617 it != content_names.end(); ++it) {
618 if (!IsRtpContent(sdesc, *it)) {
619 continue;
620 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000621 // The common cryptos are needed if any of the content does not have DTLS
622 // enabled.
623 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
624 common_cryptos_needed = true;
625 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000626 if (it == content_names.begin()) {
627 // Initial the common_cryptos with the first content in the bundle group.
628 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
629 return false;
630 }
631 if (common_cryptos.empty()) {
632 // If there's no crypto params, we should just return.
633 return true;
634 }
635 } else {
636 CryptoParamsVec cryptos;
637 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
638 return false;
639 }
640 PruneCryptos(cryptos, &common_cryptos);
641 }
642 }
643
wu@webrtc.org78187522013-10-07 23:32:02 +0000644 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000645 return false;
646 }
647
648 // Update to use the common cryptos.
649 for (ContentNames::const_iterator it = content_names.begin();
650 it != content_names.end(); ++it) {
651 if (!IsRtpContent(sdesc, *it)) {
652 continue;
653 }
654 ContentInfo* content = sdesc->GetContentByName(*it);
655 if (IsMediaContent(content)) {
656 MediaContentDescription* media_desc =
657 static_cast<MediaContentDescription*>(content->description);
658 if (!media_desc) {
659 return false;
660 }
661 media_desc->set_cryptos(common_cryptos);
662 }
663 }
664 return true;
665}
666
667template <class C>
668static bool ContainsRtxCodec(const std::vector<C>& codecs) {
669 typename std::vector<C>::const_iterator it;
670 for (it = codecs.begin(); it != codecs.end(); ++it) {
671 if (IsRtxCodec(*it)) {
672 return true;
673 }
674 }
675 return false;
676}
677
678template <class C>
679static bool IsRtxCodec(const C& codec) {
680 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
681}
682
683// Create a media content to be offered in a session-initiate,
684// according to the given options.rtcp_mux, options.is_muc,
685// options.streams, codecs, secure_transport, crypto, and streams. If we don't
686// currently have crypto (in current_cryptos) and it is enabled (in
687// secure_policy), crypto is created (according to crypto_suites). If
688// add_legacy_stream is true, and current_streams is empty, a legacy
689// stream is created. The created content is added to the offer.
690template <class C>
691static bool CreateMediaContentOffer(
692 const MediaSessionOptions& options,
693 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000694 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000695 const CryptoParamsVec* current_cryptos,
696 const std::vector<std::string>& crypto_suites,
697 const RtpHeaderExtensions& rtp_extensions,
698 bool add_legacy_stream,
699 StreamParamsVec* current_streams,
700 MediaContentDescriptionImpl<C>* offer) {
701 offer->AddCodecs(codecs);
702 offer->SortCodecs();
703
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000704 if (secure_policy == SEC_REQUIRED) {
705 offer->set_crypto_required(CT_SDES);
706 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000707 offer->set_rtcp_mux(options.rtcp_mux_enabled);
708 offer->set_multistream(options.is_muc);
709 offer->set_rtp_header_extensions(rtp_extensions);
710
711 if (!AddStreamParams(
712 offer->type(), options.streams, current_streams,
713 offer, add_legacy_stream)) {
714 return false;
715 }
716
717#ifdef HAVE_SRTP
718 if (secure_policy != SEC_DISABLED) {
719 if (current_cryptos) {
720 AddMediaCryptos(*current_cryptos, offer);
721 }
722 if (offer->cryptos().empty()) {
723 if (!CreateMediaCryptos(crypto_suites, offer)) {
724 return false;
725 }
726 }
727 }
728#endif
729
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000730 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731 return false;
732 }
733 return true;
734}
735
736template <class C>
737static void NegotiateCodecs(const std::vector<C>& local_codecs,
738 const std::vector<C>& offered_codecs,
739 std::vector<C>* negotiated_codecs) {
740 typename std::vector<C>::const_iterator ours;
741 for (ours = local_codecs.begin();
742 ours != local_codecs.end(); ++ours) {
743 typename std::vector<C>::const_iterator theirs;
744 for (theirs = offered_codecs.begin();
745 theirs != offered_codecs.end(); ++theirs) {
746 if (ours->Matches(*theirs)) {
747 C negotiated = *ours;
748 negotiated.IntersectFeedbackParams(*theirs);
749 if (IsRtxCodec(negotiated)) {
750 // Only negotiate RTX if kCodecParamAssociatedPayloadType has been
751 // set.
752 std::string apt_value;
753 if (!theirs->GetParam(kCodecParamAssociatedPayloadType, &apt_value)) {
754 LOG(LS_WARNING) << "RTX missing associated payload type.";
755 continue;
756 }
757 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_value);
758 }
759 negotiated.id = theirs->id;
760 negotiated_codecs->push_back(negotiated);
761 }
762 }
763 }
764}
765
766template <class C>
767static bool FindMatchingCodec(const std::vector<C>& codecs,
768 const C& codec_to_match,
769 C* found_codec) {
770 for (typename std::vector<C>::const_iterator it = codecs.begin();
771 it != codecs.end(); ++it) {
772 if (it->Matches(codec_to_match)) {
773 if (found_codec != NULL) {
774 *found_codec= *it;
775 }
776 return true;
777 }
778 }
779 return false;
780}
781
782// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
783// already exist in |offered_codecs| and ensure the payload types don't
784// collide.
785template <class C>
786static void FindCodecsToOffer(
787 const std::vector<C>& reference_codecs,
788 std::vector<C>* offered_codecs,
789 UsedPayloadTypes* used_pltypes) {
790
791 typedef std::map<int, C> RtxCodecReferences;
792 RtxCodecReferences new_rtx_codecs;
793
794 // Find all new RTX codecs.
795 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
796 it != reference_codecs.end(); ++it) {
797 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) {
798 C rtx_codec = *it;
799 int referenced_pl_type =
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000800 talk_base::FromString<int>(0,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000801 rtx_codec.params[kCodecParamAssociatedPayloadType]);
802 new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type,
803 rtx_codec));
804 }
805 }
806
807 // Add all new codecs that are not RTX codecs.
808 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
809 it != reference_codecs.end(); ++it) {
810 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) {
811 C codec = *it;
812 int original_payload_id = codec.id;
813 used_pltypes->FindAndSetIdUsed(&codec);
814 offered_codecs->push_back(codec);
815
816 // If this codec is referenced by a new RTX codec, update the reference
817 // in the RTX codec with the new payload type.
818 typename RtxCodecReferences::iterator rtx_it =
819 new_rtx_codecs.find(original_payload_id);
820 if (rtx_it != new_rtx_codecs.end()) {
821 C& rtx_codec = rtx_it->second;
822 rtx_codec.params[kCodecParamAssociatedPayloadType] =
823 talk_base::ToString(codec.id);
824 }
825 }
826 }
827
828 // Add all new RTX codecs.
829 for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin();
830 it != new_rtx_codecs.end(); ++it) {
831 C& rtx_codec = it->second;
832 used_pltypes->FindAndSetIdUsed(&rtx_codec);
833 offered_codecs->push_back(rtx_codec);
834 }
835}
836
837
838static bool FindByUri(const RtpHeaderExtensions& extensions,
839 const RtpHeaderExtension& ext_to_match,
840 RtpHeaderExtension* found_extension) {
841 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
842 it != extensions.end(); ++it) {
843 // We assume that all URIs are given in a canonical format.
844 if (it->uri == ext_to_match.uri) {
845 if (found_extension != NULL) {
846 *found_extension= *it;
847 }
848 return true;
849 }
850 }
851 return false;
852}
853
854static void FindAndSetRtpHdrExtUsed(
855 const RtpHeaderExtensions& reference_extensions,
856 RtpHeaderExtensions* offered_extensions,
857 UsedRtpHeaderExtensionIds* used_extensions) {
858 for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin();
859 it != reference_extensions.end(); ++it) {
860 if (!FindByUri(*offered_extensions, *it, NULL)) {
861 RtpHeaderExtension ext = *it;
862 used_extensions->FindAndSetIdUsed(&ext);
863 offered_extensions->push_back(ext);
864 }
865 }
866}
867
868static void NegotiateRtpHeaderExtensions(
869 const RtpHeaderExtensions& local_extensions,
870 const RtpHeaderExtensions& offered_extensions,
871 RtpHeaderExtensions* negotiated_extenstions) {
872 RtpHeaderExtensions::const_iterator ours;
873 for (ours = local_extensions.begin();
874 ours != local_extensions.end(); ++ours) {
875 RtpHeaderExtension theirs;
876 if (FindByUri(offered_extensions, *ours, &theirs)) {
877 // We respond with their RTP header extension id.
878 negotiated_extenstions->push_back(theirs);
879 }
880 }
881}
882
883static void StripCNCodecs(AudioCodecs* audio_codecs) {
884 AudioCodecs::iterator iter = audio_codecs->begin();
885 while (iter != audio_codecs->end()) {
886 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
887 iter = audio_codecs->erase(iter);
888 } else {
889 ++iter;
890 }
891 }
892}
893
894// Create a media content to be answered in a session-accept,
895// according to the given options.rtcp_mux, options.streams, codecs,
896// crypto, and streams. If we don't currently have crypto (in
897// current_cryptos) and it is enabled (in secure_policy), crypto is
898// created (according to crypto_suites). If add_legacy_stream is
899// true, and current_streams is empty, a legacy stream is created.
900// The codecs, rtcp_mux, and crypto are all negotiated with the offer
901// from the incoming session-initiate. If the negotiation fails, this
902// method returns false. The created content is added to the offer.
903template <class C>
904static bool CreateMediaContentAnswer(
905 const MediaContentDescriptionImpl<C>* offer,
906 const MediaSessionOptions& options,
907 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000908 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000909 const CryptoParamsVec* current_cryptos,
910 const RtpHeaderExtensions& local_rtp_extenstions,
911 StreamParamsVec* current_streams,
912 bool add_legacy_stream,
913 bool bundle_enabled,
914 MediaContentDescriptionImpl<C>* answer) {
915 std::vector<C> negotiated_codecs;
916 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
917 answer->AddCodecs(negotiated_codecs);
918 answer->SortCodecs();
919 answer->set_protocol(offer->protocol());
920 RtpHeaderExtensions negotiated_rtp_extensions;
921 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
922 offer->rtp_header_extensions(),
923 &negotiated_rtp_extensions);
924 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
925
926 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
927
928 if (sdes_policy != SEC_DISABLED) {
929 CryptoParams crypto;
930 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
931 if (current_cryptos) {
932 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
933 }
934 answer->AddCrypto(crypto);
935 }
936 }
937
938 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000939 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000940 return false;
941 }
942
943 if (!AddStreamParams(
944 answer->type(), options.streams, current_streams,
945 answer, add_legacy_stream)) {
946 return false; // Something went seriously wrong.
947 }
948
949 // Make sure the answer media content direction is per default set as
950 // described in RFC3264 section 6.1.
951 switch (offer->direction()) {
952 case MD_INACTIVE:
953 answer->set_direction(MD_INACTIVE);
954 break;
955 case MD_SENDONLY:
956 answer->set_direction(MD_RECVONLY);
957 break;
958 case MD_RECVONLY:
959 answer->set_direction(MD_SENDONLY);
960 break;
961 case MD_SENDRECV:
962 answer->set_direction(MD_SENDRECV);
963 break;
964 default:
965 break;
966 }
967
968 return true;
969}
970
971static bool IsMediaProtocolSupported(MediaType type,
972 const std::string& protocol) {
973 // Data channels can have a protocol of SCTP or SCTP/DTLS.
974 if (type == MEDIA_TYPE_DATA &&
975 (protocol == kMediaProtocolSctp ||
976 protocol == kMediaProtocolDtlsSctp)) {
977 return true;
978 }
979 // Since not all applications serialize and deserialize the media protocol,
980 // we will have to accept |protocol| to be empty.
981 return protocol == kMediaProtocolAvpf || protocol == kMediaProtocolSavpf ||
982 protocol.empty();
983}
984
985static void SetMediaProtocol(bool secure_transport,
986 MediaContentDescription* desc) {
987 if (!desc->cryptos().empty() || secure_transport)
988 desc->set_protocol(kMediaProtocolSavpf);
989 else
990 desc->set_protocol(kMediaProtocolAvpf);
991}
992
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000993// Gets the TransportInfo of the given |content_name| from the
994// |current_description|. If doesn't exist, returns a new one.
995static const TransportDescription* GetTransportDescription(
996 const std::string& content_name,
997 const SessionDescription* current_description) {
998 const TransportDescription* desc = NULL;
999 if (current_description) {
1000 const TransportInfo* info =
1001 current_description->GetTransportInfoByName(content_name);
1002 if (info) {
1003 desc = &info->description;
1004 }
1005 }
1006 return desc;
1007}
1008
1009// Gets the current DTLS state from the transport description.
1010static bool IsDtlsActive(
1011 const std::string& content_name,
1012 const SessionDescription* current_description) {
1013 if (!current_description)
1014 return false;
1015
1016 const ContentInfo* content =
1017 current_description->GetContentByName(content_name);
1018 if (!content)
1019 return false;
1020
1021 const TransportDescription* current_tdesc =
1022 GetTransportDescription(content_name, current_description);
1023 if (!current_tdesc)
1024 return false;
1025
1026 return current_tdesc->secure();
1027}
1028
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001029std::string MediaTypeToString(MediaType type) {
1030 std::string type_str;
1031 switch (type) {
1032 case MEDIA_TYPE_AUDIO:
1033 type_str = "audio";
1034 break;
1035 case MEDIA_TYPE_VIDEO:
1036 type_str = "video";
1037 break;
1038 case MEDIA_TYPE_DATA:
1039 type_str = "data";
1040 break;
1041 default:
1042 ASSERT(false);
1043 break;
1044 }
1045 return type_str;
1046}
1047
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048void MediaSessionOptions::AddStream(MediaType type,
1049 const std::string& id,
1050 const std::string& sync_label) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001051 AddStreamInternal(type, id, sync_label, 1);
1052}
1053
1054void MediaSessionOptions::AddVideoStream(
1055 const std::string& id,
1056 const std::string& sync_label,
1057 int num_sim_layers) {
1058 AddStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
1059}
1060
1061void MediaSessionOptions::AddStreamInternal(
1062 MediaType type,
1063 const std::string& id,
1064 const std::string& sync_label,
1065 int num_sim_layers) {
1066 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001067
1068 if (type == MEDIA_TYPE_VIDEO)
1069 has_video = true;
1070 else if (type == MEDIA_TYPE_AUDIO)
1071 has_audio = true;
1072 // If we haven't already set the data_channel_type, and we add a
1073 // stream, we assume it's an RTP data stream.
1074 else if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
1075 data_channel_type = DCT_RTP;
1076}
1077
1078void MediaSessionOptions::RemoveStream(MediaType type,
1079 const std::string& id) {
1080 Streams::iterator stream_it = streams.begin();
1081 for (; stream_it != streams.end(); ++stream_it) {
1082 if (stream_it->type == type && stream_it->id == id) {
1083 streams.erase(stream_it);
1084 return;
1085 }
1086 }
1087 ASSERT(false);
1088}
1089
1090MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1091 const TransportDescriptionFactory* transport_desc_factory)
1092 : secure_(SEC_DISABLED),
1093 add_legacy_(true),
1094 transport_desc_factory_(transport_desc_factory) {
1095}
1096
1097MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1098 ChannelManager* channel_manager,
1099 const TransportDescriptionFactory* transport_desc_factory)
1100 : secure_(SEC_DISABLED),
1101 add_legacy_(true),
1102 transport_desc_factory_(transport_desc_factory) {
1103 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1104 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1105 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1106 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1107 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1108}
1109
1110SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1111 const MediaSessionOptions& options,
1112 const SessionDescription* current_description) const {
1113 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1114
1115 scoped_ptr<SessionDescription> offer(new SessionDescription());
1116
1117 StreamParamsVec current_streams;
1118 GetCurrentStreamParams(current_description, &current_streams);
1119
1120 AudioCodecs audio_codecs;
1121 VideoCodecs video_codecs;
1122 DataCodecs data_codecs;
1123 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1124 &data_codecs);
1125
1126 if (!options.vad_enabled) {
1127 // If application doesn't want CN codecs in offer.
1128 StripCNCodecs(&audio_codecs);
1129 }
1130
1131 RtpHeaderExtensions audio_rtp_extensions;
1132 RtpHeaderExtensions video_rtp_extensions;
1133 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1134 &video_rtp_extensions);
1135
1136 // Handle m=audio.
1137 if (options.has_audio) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001138 cricket::SecurePolicy sdes_policy =
1139 IsDtlsActive(CN_AUDIO, current_description) ?
1140 cricket::SEC_DISABLED : secure();
1141
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001142 scoped_ptr<AudioContentDescription> audio(new AudioContentDescription());
1143 std::vector<std::string> crypto_suites;
1144 GetSupportedAudioCryptoSuites(&crypto_suites);
1145 if (!CreateMediaContentOffer(
1146 options,
1147 audio_codecs,
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001148 sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001149 GetCryptos(GetFirstAudioContentDescription(current_description)),
1150 crypto_suites,
1151 audio_rtp_extensions,
1152 add_legacy_,
1153 &current_streams,
1154 audio.get())) {
1155 return NULL;
1156 }
1157
1158 audio->set_lang(lang_);
1159 SetMediaProtocol(secure_transport, audio.get());
1160 offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio.release());
1161 if (!AddTransportOffer(CN_AUDIO, options.transport_options,
1162 current_description, offer.get())) {
1163 return NULL;
1164 }
1165 }
1166
1167 // Handle m=video.
1168 if (options.has_video) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001169 cricket::SecurePolicy sdes_policy =
1170 IsDtlsActive(CN_VIDEO, current_description) ?
1171 cricket::SEC_DISABLED : secure();
1172
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001173 scoped_ptr<VideoContentDescription> video(new VideoContentDescription());
1174 std::vector<std::string> crypto_suites;
1175 GetSupportedVideoCryptoSuites(&crypto_suites);
1176 if (!CreateMediaContentOffer(
1177 options,
1178 video_codecs,
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001179 sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001180 GetCryptos(GetFirstVideoContentDescription(current_description)),
1181 crypto_suites,
1182 video_rtp_extensions,
1183 add_legacy_,
1184 &current_streams,
1185 video.get())) {
1186 return NULL;
1187 }
1188
1189 video->set_bandwidth(options.video_bandwidth);
1190 SetMediaProtocol(secure_transport, video.get());
1191 offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video.release());
1192 if (!AddTransportOffer(CN_VIDEO, options.transport_options,
1193 current_description, offer.get())) {
1194 return NULL;
1195 }
1196 }
1197
1198 // Handle m=data.
1199 if (options.has_data()) {
1200 scoped_ptr<DataContentDescription> data(new DataContentDescription());
1201 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1202
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001203 cricket::SecurePolicy sdes_policy =
1204 IsDtlsActive(CN_DATA, current_description) ?
1205 cricket::SEC_DISABLED : secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001206 std::vector<std::string> crypto_suites;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207 if (is_sctp) {
1208 // SDES doesn't make sense for SCTP, so we disable it, and we only
1209 // get SDES crypto suites for RTP-based data channels.
1210 sdes_policy = cricket::SEC_DISABLED;
1211 // Unlike SetMediaProtocol below, we need to set the protocol
1212 // before we call CreateMediaContentOffer. Otherwise,
1213 // CreateMediaContentOffer won't know this is SCTP and will
1214 // generate SSRCs rather than SIDs.
1215 data->set_protocol(
1216 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1217 } else {
1218 GetSupportedDataCryptoSuites(&crypto_suites);
1219 }
1220
1221 if (!CreateMediaContentOffer(
1222 options,
1223 data_codecs,
1224 sdes_policy,
1225 GetCryptos(GetFirstDataContentDescription(current_description)),
1226 crypto_suites,
1227 RtpHeaderExtensions(),
1228 add_legacy_,
1229 &current_streams,
1230 data.get())) {
1231 return NULL;
1232 }
1233
1234 if (is_sctp) {
1235 offer->AddContent(CN_DATA, NS_JINGLE_DRAFT_SCTP, data.release());
1236 } else {
1237 data->set_bandwidth(options.data_bandwidth);
1238 SetMediaProtocol(secure_transport, data.get());
1239 offer->AddContent(CN_DATA, NS_JINGLE_RTP, data.release());
1240 }
1241 if (!AddTransportOffer(CN_DATA, options.transport_options,
1242 current_description, offer.get())) {
1243 return NULL;
1244 }
1245 }
1246
1247 // Bundle the contents together, if we've been asked to do so, and update any
1248 // parameters that need to be tweaked for BUNDLE.
1249 if (options.bundle_enabled) {
1250 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1251 for (ContentInfos::const_iterator content = offer->contents().begin();
1252 content != offer->contents().end(); ++content) {
1253 offer_bundle.AddContentName(content->name);
1254 }
1255 offer->AddGroup(offer_bundle);
1256 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1257 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1258 return NULL;
1259 }
1260 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1261 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1262 return NULL;
1263 }
1264 }
1265
1266 return offer.release();
1267}
1268
1269SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1270 const SessionDescription* offer, const MediaSessionOptions& options,
1271 const SessionDescription* current_description) const {
1272 // The answer contains the intersection of the codecs in the offer with the
1273 // codecs we support, ordered by our local preference. As indicated by
1274 // XEP-0167, we retain the same payload ids from the offer in the answer.
1275 scoped_ptr<SessionDescription> answer(new SessionDescription());
1276
1277 StreamParamsVec current_streams;
1278 GetCurrentStreamParams(current_description, &current_streams);
1279
1280 bool bundle_enabled =
1281 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1282
1283 // Handle m=audio.
1284 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1285 if (audio_content) {
1286 scoped_ptr<TransportDescription> audio_transport(
1287 CreateTransportAnswer(audio_content->name, offer,
1288 options.transport_options,
1289 current_description));
1290 if (!audio_transport) {
1291 return NULL;
1292 }
1293
1294 AudioCodecs audio_codecs = audio_codecs_;
1295 if (!options.vad_enabled) {
1296 StripCNCodecs(&audio_codecs);
1297 }
1298
1299 scoped_ptr<AudioContentDescription> audio_answer(
1300 new AudioContentDescription());
1301 // Do not require or create SDES cryptos if DTLS is used.
1302 cricket::SecurePolicy sdes_policy =
1303 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1304 if (!CreateMediaContentAnswer(
1305 static_cast<const AudioContentDescription*>(
1306 audio_content->description),
1307 options,
1308 audio_codecs,
1309 sdes_policy,
1310 GetCryptos(GetFirstAudioContentDescription(current_description)),
1311 audio_rtp_extensions_,
1312 &current_streams,
1313 add_legacy_,
1314 bundle_enabled,
1315 audio_answer.get())) {
1316 return NULL; // Fails the session setup.
1317 }
1318
1319 bool rejected = !options.has_audio || audio_content->rejected ||
1320 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1321 audio_answer->protocol());
1322 if (!rejected) {
1323 AddTransportAnswer(audio_content->name, *(audio_transport.get()),
1324 answer.get());
1325 } else {
1326 // RFC 3264
1327 // The answer MUST contain the same number of m-lines as the offer.
1328 LOG(LS_INFO) << "Audio is not supported in the answer.";
1329 }
1330
1331 answer->AddContent(audio_content->name, audio_content->type, rejected,
1332 audio_answer.release());
1333 } else {
1334 LOG(LS_INFO) << "Audio is not available in the offer.";
1335 }
1336
1337 // Handle m=video.
1338 const ContentInfo* video_content = GetFirstVideoContent(offer);
1339 if (video_content) {
1340 scoped_ptr<TransportDescription> video_transport(
1341 CreateTransportAnswer(video_content->name, offer,
1342 options.transport_options,
1343 current_description));
1344 if (!video_transport) {
1345 return NULL;
1346 }
1347
1348 scoped_ptr<VideoContentDescription> video_answer(
1349 new VideoContentDescription());
1350 // Do not require or create SDES cryptos if DTLS is used.
1351 cricket::SecurePolicy sdes_policy =
1352 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1353 if (!CreateMediaContentAnswer(
1354 static_cast<const VideoContentDescription*>(
1355 video_content->description),
1356 options,
1357 video_codecs_,
1358 sdes_policy,
1359 GetCryptos(GetFirstVideoContentDescription(current_description)),
1360 video_rtp_extensions_,
1361 &current_streams,
1362 add_legacy_,
1363 bundle_enabled,
1364 video_answer.get())) {
1365 return NULL;
1366 }
1367 bool rejected = !options.has_video || video_content->rejected ||
1368 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol());
1369 if (!rejected) {
1370 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1371 answer.get())) {
1372 return NULL;
1373 }
1374 video_answer->set_bandwidth(options.video_bandwidth);
1375 } else {
1376 // RFC 3264
1377 // The answer MUST contain the same number of m-lines as the offer.
1378 LOG(LS_INFO) << "Video is not supported in the answer.";
1379 }
1380 answer->AddContent(video_content->name, video_content->type, rejected,
1381 video_answer.release());
1382 } else {
1383 LOG(LS_INFO) << "Video is not available in the offer.";
1384 }
1385
1386 // Handle m=data.
1387 const ContentInfo* data_content = GetFirstDataContent(offer);
1388 if (data_content) {
1389 scoped_ptr<TransportDescription> data_transport(
1390 CreateTransportAnswer(data_content->name, offer,
1391 options.transport_options,
1392 current_description));
1393 if (!data_transport) {
1394 return NULL;
1395 }
1396 scoped_ptr<DataContentDescription> data_answer(
1397 new DataContentDescription());
1398 // Do not require or create SDES cryptos if DTLS is used.
1399 cricket::SecurePolicy sdes_policy =
1400 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1401 if (!CreateMediaContentAnswer(
1402 static_cast<const DataContentDescription*>(
1403 data_content->description),
1404 options,
1405 data_codecs_,
1406 sdes_policy,
1407 GetCryptos(GetFirstDataContentDescription(current_description)),
1408 RtpHeaderExtensions(),
1409 &current_streams,
1410 add_legacy_,
1411 bundle_enabled,
1412 data_answer.get())) {
1413 return NULL; // Fails the session setup.
1414 }
1415
1416 bool rejected = !options.has_data() || data_content->rejected ||
1417 !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol());
1418 if (!rejected) {
1419 data_answer->set_bandwidth(options.data_bandwidth);
1420 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1421 answer.get())) {
1422 return NULL;
1423 }
1424 } else {
1425 // RFC 3264
1426 // The answer MUST contain the same number of m-lines as the offer.
1427 LOG(LS_INFO) << "Data is not supported in the answer.";
1428 }
1429 answer->AddContent(data_content->name, data_content->type, rejected,
1430 data_answer.release());
1431 } else {
1432 LOG(LS_INFO) << "Data is not available in the offer.";
1433 }
1434
1435 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1436 // group in the answer with the appropriate content names.
1437 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1438 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1439 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1440 for (ContentInfos::const_iterator content = answer->contents().begin();
1441 content != answer->contents().end(); ++content) {
1442 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1443 answer_bundle.AddContentName(content->name);
1444 }
1445 }
1446 if (answer_bundle.FirstContentName()) {
1447 answer->AddGroup(answer_bundle);
1448
1449 // Share the same ICE credentials and crypto params across all contents,
1450 // as BUNDLE requires.
1451 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1452 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1453 return NULL;
1454 }
1455
1456 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1457 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1458 return NULL;
1459 }
1460 }
1461 }
1462
1463 return answer.release();
1464}
1465
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001466void MediaSessionDescriptionFactory::GetCodecsToOffer(
1467 const SessionDescription* current_description,
1468 AudioCodecs* audio_codecs,
1469 VideoCodecs* video_codecs,
1470 DataCodecs* data_codecs) const {
1471 UsedPayloadTypes used_pltypes;
1472 audio_codecs->clear();
1473 video_codecs->clear();
1474 data_codecs->clear();
1475
1476
1477 // First - get all codecs from the current description if the media type
1478 // is used.
1479 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1480 // type is added.
1481 if (current_description) {
1482 const AudioContentDescription* audio =
1483 GetFirstAudioContentDescription(current_description);
1484 if (audio) {
1485 *audio_codecs = audio->codecs();
1486 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1487 }
1488 const VideoContentDescription* video =
1489 GetFirstVideoContentDescription(current_description);
1490 if (video) {
1491 *video_codecs = video->codecs();
1492 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1493 }
1494 const DataContentDescription* data =
1495 GetFirstDataContentDescription(current_description);
1496 if (data) {
1497 *data_codecs = data->codecs();
1498 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1499 }
1500 }
1501
1502 // Add our codecs that are not in |current_description|.
1503 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1504 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1505 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1506}
1507
1508void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1509 const SessionDescription* current_description,
1510 RtpHeaderExtensions* audio_extensions,
1511 RtpHeaderExtensions* video_extensions) const {
1512 UsedRtpHeaderExtensionIds used_ids;
1513 audio_extensions->clear();
1514 video_extensions->clear();
1515
1516 // First - get all extensions from the current description if the media type
1517 // is used.
1518 // Add them to |used_ids| so the local ids are not reused if a new media
1519 // type is added.
1520 if (current_description) {
1521 const AudioContentDescription* audio =
1522 GetFirstAudioContentDescription(current_description);
1523 if (audio) {
1524 *audio_extensions = audio->rtp_header_extensions();
1525 used_ids.FindAndSetIdUsed(audio_extensions);
1526 }
1527 const VideoContentDescription* video =
1528 GetFirstVideoContentDescription(current_description);
1529 if (video) {
1530 *video_extensions = video->rtp_header_extensions();
1531 used_ids.FindAndSetIdUsed(video_extensions);
1532 }
1533 }
1534
1535 // Add our default RTP header extensions that are not in
1536 // |current_description|.
1537 FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions,
1538 &used_ids);
1539 FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions,
1540 &used_ids);
1541}
1542
1543bool MediaSessionDescriptionFactory::AddTransportOffer(
1544 const std::string& content_name,
1545 const TransportOptions& transport_options,
1546 const SessionDescription* current_desc,
1547 SessionDescription* offer_desc) const {
1548 if (!transport_desc_factory_)
1549 return false;
1550 const TransportDescription* current_tdesc =
1551 GetTransportDescription(content_name, current_desc);
1552 talk_base::scoped_ptr<TransportDescription> new_tdesc(
1553 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1554 bool ret = (new_tdesc.get() != NULL &&
1555 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1556 if (!ret) {
1557 LOG(LS_ERROR)
1558 << "Failed to AddTransportOffer, content name=" << content_name;
1559 }
1560 return ret;
1561}
1562
1563TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1564 const std::string& content_name,
1565 const SessionDescription* offer_desc,
1566 const TransportOptions& transport_options,
1567 const SessionDescription* current_desc) const {
1568 if (!transport_desc_factory_)
1569 return NULL;
1570 const TransportDescription* offer_tdesc =
1571 GetTransportDescription(content_name, offer_desc);
1572 const TransportDescription* current_tdesc =
1573 GetTransportDescription(content_name, current_desc);
1574 return
1575 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1576 current_tdesc);
1577}
1578
1579bool MediaSessionDescriptionFactory::AddTransportAnswer(
1580 const std::string& content_name,
1581 const TransportDescription& transport_desc,
1582 SessionDescription* answer_desc) const {
1583 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1584 transport_desc))) {
1585 LOG(LS_ERROR)
1586 << "Failed to AddTransportAnswer, content name=" << content_name;
1587 return false;
1588 }
1589 return true;
1590}
1591
1592bool IsMediaContent(const ContentInfo* content) {
1593 return (content &&
1594 (content->type == NS_JINGLE_RTP ||
1595 content->type == NS_JINGLE_DRAFT_SCTP));
1596}
1597
1598bool IsAudioContent(const ContentInfo* content) {
1599 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1600}
1601
1602bool IsVideoContent(const ContentInfo* content) {
1603 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1604}
1605
1606bool IsDataContent(const ContentInfo* content) {
1607 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1608}
1609
1610static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1611 MediaType media_type) {
1612 for (ContentInfos::const_iterator content = contents.begin();
1613 content != contents.end(); content++) {
1614 if (IsMediaContentOfType(&*content, media_type)) {
1615 return &*content;
1616 }
1617 }
1618 return NULL;
1619}
1620
1621const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1622 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1623}
1624
1625const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1626 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1627}
1628
1629const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1630 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1631}
1632
1633static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1634 MediaType media_type) {
1635 if (sdesc == NULL)
1636 return NULL;
1637
1638 return GetFirstMediaContent(sdesc->contents(), media_type);
1639}
1640
1641const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1642 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1643}
1644
1645const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1646 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1647}
1648
1649const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1650 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1651}
1652
1653const MediaContentDescription* GetFirstMediaContentDescription(
1654 const SessionDescription* sdesc, MediaType media_type) {
1655 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1656 const ContentDescription* description = content ? content->description : NULL;
1657 return static_cast<const MediaContentDescription*>(description);
1658}
1659
1660const AudioContentDescription* GetFirstAudioContentDescription(
1661 const SessionDescription* sdesc) {
1662 return static_cast<const AudioContentDescription*>(
1663 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1664}
1665
1666const VideoContentDescription* GetFirstVideoContentDescription(
1667 const SessionDescription* sdesc) {
1668 return static_cast<const VideoContentDescription*>(
1669 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1670}
1671
1672const DataContentDescription* GetFirstDataContentDescription(
1673 const SessionDescription* sdesc) {
1674 return static_cast<const DataContentDescription*>(
1675 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1676}
1677
1678bool GetMediaChannelNameFromComponent(
1679 int component, MediaType media_type, std::string* channel_name) {
1680 if (media_type == MEDIA_TYPE_AUDIO) {
1681 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1682 *channel_name = GICE_CHANNEL_NAME_RTP;
1683 return true;
1684 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1685 *channel_name = GICE_CHANNEL_NAME_RTCP;
1686 return true;
1687 }
1688 } else if (media_type == MEDIA_TYPE_VIDEO) {
1689 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1690 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTP;
1691 return true;
1692 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1693 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTCP;
1694 return true;
1695 }
1696 } else if (media_type == MEDIA_TYPE_DATA) {
1697 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1698 *channel_name = GICE_CHANNEL_NAME_DATA_RTP;
1699 return true;
1700 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1701 *channel_name = GICE_CHANNEL_NAME_DATA_RTCP;
1702 return true;
1703 }
1704 }
1705
1706 return false;
1707}
1708
1709bool GetMediaComponentFromChannelName(
1710 const std::string& channel_name, int* component) {
1711 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1712 channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1713 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1714 *component = ICE_CANDIDATE_COMPONENT_RTP;
1715 return true;
1716 } else if (channel_name == GICE_CHANNEL_NAME_RTCP ||
1717 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP ||
1718 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1719 *component = ICE_CANDIDATE_COMPONENT_RTCP;
1720 return true;
1721 }
1722
1723 return false;
1724}
1725
1726bool GetMediaTypeFromChannelName(
1727 const std::string& channel_name, MediaType* media_type) {
1728 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1729 channel_name == GICE_CHANNEL_NAME_RTCP) {
1730 *media_type = MEDIA_TYPE_AUDIO;
1731 return true;
1732 } else if (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1733 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP) {
1734 *media_type = MEDIA_TYPE_VIDEO;
1735 return true;
1736 } else if (channel_name == GICE_CHANNEL_NAME_DATA_RTP ||
1737 channel_name == GICE_CHANNEL_NAME_DATA_RTCP) {
1738 *media_type = MEDIA_TYPE_DATA;
1739 return true;
1740 }
1741
1742 return false;
1743}
1744
1745} // namespace cricket