blob: b728517961307b667f2c5417a31137cecf625634 [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
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035#include "talk/media/base/constants.h"
36#include "talk/media/base/cryptoparams.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037#include "talk/session/media/channelmanager.h"
38#include "talk/session/media/srtpfilter.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000039#include "webrtc/base/helpers.h"
40#include "webrtc/base/logging.h"
41#include "webrtc/base/scoped_ptr.h"
42#include "webrtc/base/stringutils.h"
pthatcher@webrtc.org5ad41782014-12-23 22:14:15 +000043#include "webrtc/p2p/base/constants.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000045#ifdef HAVE_SCTP
46#include "talk/media/sctp/sctpdataengine.h"
47#else
wu@webrtc.org97077a32013-10-25 21:18:33 +000048static const uint32 kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000049#endif
50
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051namespace {
52const char kInline[] = "inline:";
53}
54
55namespace cricket {
56
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000057using rtc::scoped_ptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058
59// RTP Profile names
60// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
61// RFC4585
62const char kMediaProtocolAvpf[] = "RTP/AVPF";
63// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000064const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
65
66// This should be replaced by "UDP/TLS/RTP/SAVPF", but we need to support it for
67// now to be compatible with previous Chrome versions.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068const char kMediaProtocolSavpf[] = "RTP/SAVPF";
69
70const char kMediaProtocolRtpPrefix[] = "RTP/";
71
72const char kMediaProtocolSctp[] = "SCTP";
73const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000074const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000075const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076
77static bool IsMediaContentOfType(const ContentInfo* content,
78 MediaType media_type) {
79 if (!IsMediaContent(content)) {
80 return false;
81 }
82
83 const MediaContentDescription* mdesc =
84 static_cast<const MediaContentDescription*>(content->description);
85 return mdesc && mdesc->type() == media_type;
86}
87
88static bool CreateCryptoParams(int tag, const std::string& cipher,
89 CryptoParams *out) {
90 std::string key;
91 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
92
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000093 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000094 return false;
95 }
96 out->tag = tag;
97 out->cipher_suite = cipher;
98 out->key_params = kInline;
99 out->key_params += key;
100 return true;
101}
102
103#ifdef HAVE_SRTP
104static bool AddCryptoParams(const std::string& cipher_suite,
105 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000106 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107
108 out->resize(size + 1);
109 return CreateCryptoParams(size, cipher_suite, &out->at(size));
110}
111
112void AddMediaCryptos(const CryptoParamsVec& cryptos,
113 MediaContentDescription* media) {
114 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
115 crypto != cryptos.end(); ++crypto) {
116 media->AddCrypto(*crypto);
117 }
118}
119
120bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
121 MediaContentDescription* media) {
122 CryptoParamsVec cryptos;
123 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
124 it != crypto_suites.end(); ++it) {
125 if (!AddCryptoParams(*it, &cryptos)) {
126 return false;
127 }
128 }
129 AddMediaCryptos(cryptos, media);
130 return true;
131}
132#endif
133
134const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
135 if (!media) {
136 return NULL;
137 }
138 return &media->cryptos();
139}
140
141bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
142 const CryptoParams& crypto,
143 CryptoParams* out) {
144 for (CryptoParamsVec::const_iterator it = cryptos.begin();
145 it != cryptos.end(); ++it) {
146 if (crypto.Matches(*it)) {
147 *out = *it;
148 return true;
149 }
150 }
151 return false;
152}
153
154// For audio, HMAC 32 is prefered because of the low overhead.
155void GetSupportedAudioCryptoSuites(
156 std::vector<std::string>* crypto_suites) {
157#ifdef HAVE_SRTP
158 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_32);
159 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
160#endif
161}
162
163void GetSupportedVideoCryptoSuites(
164 std::vector<std::string>* crypto_suites) {
165 GetSupportedDefaultCryptoSuites(crypto_suites);
166}
167
168void GetSupportedDataCryptoSuites(
169 std::vector<std::string>* crypto_suites) {
170 GetSupportedDefaultCryptoSuites(crypto_suites);
171}
172
173void GetSupportedDefaultCryptoSuites(
174 std::vector<std::string>* crypto_suites) {
175#ifdef HAVE_SRTP
176 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80);
177#endif
178}
179
180// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
181// tolerated unless bundle is enabled because it is low overhead. Pick the
182// crypto in the list that is supported.
183static bool SelectCrypto(const MediaContentDescription* offer,
184 bool bundle,
185 CryptoParams *crypto) {
186 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
187 const CryptoParamsVec& cryptos = offer->cryptos();
188
189 for (CryptoParamsVec::const_iterator i = cryptos.begin();
190 i != cryptos.end(); ++i) {
191 if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
192 (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && !bundle)) {
193 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
194 }
195 }
196 return false;
197}
198
199static const StreamParams* FindFirstStreamParamsByCname(
200 const StreamParamsVec& params_vec,
201 const std::string& cname) {
202 for (StreamParamsVec::const_iterator it = params_vec.begin();
203 it != params_vec.end(); ++it) {
204 if (cname == it->cname)
205 return &*it;
206 }
207 return NULL;
208}
209
210// Generates a new CNAME or the CNAME of an already existing StreamParams
211// if a StreamParams exist for another Stream in streams with sync_label
212// sync_label.
213static bool GenerateCname(const StreamParamsVec& params_vec,
214 const MediaSessionOptions::Streams& streams,
215 const std::string& synch_label,
216 std::string* cname) {
217 ASSERT(cname != NULL);
218 if (!cname)
219 return false;
220
221 // Check if a CNAME exist for any of the other synched streams.
222 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
223 stream_it != streams.end() ; ++stream_it) {
224 if (synch_label != stream_it->sync_label)
225 continue;
226
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227 // groupid is empty for StreamParams generated using
228 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000229 const StreamParams* param = GetStreamByIds(params_vec, "", stream_it->id);
230 if (param) {
231 *cname = param->cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232 return true;
233 }
234 }
235 // No other stream seems to exist that we should sync with.
236 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
237 // This string is only used for synchronization, and therefore is opaque.
238 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000239 if (!rtc::CreateRandomString(16, cname)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240 ASSERT(false);
241 return false;
242 }
243 } while (FindFirstStreamParamsByCname(params_vec, *cname));
244
245 return true;
246}
247
248// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000249// The generated values are added to |ssrcs|.
250// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000252 int num_ssrcs,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 std::vector<uint32>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000254 for (int i = 0; i < num_ssrcs; i++) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255 uint32 candidate;
256 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000257 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000258 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
260 ssrcs->push_back(candidate);
261 }
262}
263
264// Returns false if we exhaust the range of SIDs.
265static bool GenerateSctpSid(const StreamParamsVec& params_vec,
266 uint32* sid) {
267 if (params_vec.size() > kMaxSctpSid) {
268 LOG(LS_WARNING) <<
269 "Could not generate an SCTP SID: too many SCTP streams.";
270 return false;
271 }
272 while (true) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000273 uint32 candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000274 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275 *sid = candidate;
276 return true;
277 }
278 }
279}
280
281static bool GenerateSctpSids(const StreamParamsVec& params_vec,
282 std::vector<uint32>* sids) {
283 uint32 sid;
284 if (!GenerateSctpSid(params_vec, &sid)) {
285 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
286 return false;
287 }
288 sids->push_back(sid);
289 return true;
290}
291
292// Finds all StreamParams of all media types and attach them to stream_params.
293static void GetCurrentStreamParams(const SessionDescription* sdesc,
294 StreamParamsVec* stream_params) {
295 if (!sdesc)
296 return;
297
298 const ContentInfos& contents = sdesc->contents();
299 for (ContentInfos::const_iterator content = contents.begin();
300 content != contents.end(); ++content) {
301 if (!IsMediaContent(&*content)) {
302 continue;
303 }
304 const MediaContentDescription* media =
305 static_cast<const MediaContentDescription*>(
306 content->description);
307 const StreamParamsVec& streams = media->streams();
308 for (StreamParamsVec::const_iterator it = streams.begin();
309 it != streams.end(); ++it) {
310 stream_params->push_back(*it);
311 }
312 }
313}
314
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000315// Filters the data codecs for the data channel type.
316void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
317 // Filter RTP codec for SCTP and vice versa.
318 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
319 for (std::vector<DataCodec>::iterator iter = codecs->begin();
320 iter != codecs->end();) {
321 if (iter->id == codec_id) {
322 iter = codecs->erase(iter);
323 } else {
324 ++iter;
325 }
326 }
327}
328
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000329template <typename IdStruct>
330class UsedIds {
331 public:
332 UsedIds(int min_allowed_id, int max_allowed_id)
333 : min_allowed_id_(min_allowed_id),
334 max_allowed_id_(max_allowed_id),
335 next_id_(max_allowed_id) {
336 }
337
338 // Loops through all Id in |ids| and changes its id if it is
339 // already in use by another IdStruct. Call this methods with all Id
340 // in a session description to make sure no duplicate ids exists.
341 // Note that typename Id must be a type of IdStruct.
342 template <typename Id>
343 void FindAndSetIdUsed(std::vector<Id>* ids) {
344 for (typename std::vector<Id>::iterator it = ids->begin();
345 it != ids->end(); ++it) {
346 FindAndSetIdUsed(&*it);
347 }
348 }
349
350 // Finds and sets an unused id if the |idstruct| id is already in use.
351 void FindAndSetIdUsed(IdStruct* idstruct) {
352 const int original_id = idstruct->id;
353 int new_id = idstruct->id;
354
355 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
356 // If the original id is not in range - this is an id that can't be
357 // dynamically changed.
358 return;
359 }
360
361 if (IsIdUsed(original_id)) {
362 new_id = FindUnusedId();
363 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
364 << " to " << new_id;
365 idstruct->id = new_id;
366 }
367 SetIdUsed(new_id);
368 }
369
370 private:
371 // Returns the first unused id in reverse order.
372 // This hopefully reduce the risk of more collisions. We want to change the
373 // default ids as little as possible.
374 int FindUnusedId() {
375 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
376 --next_id_;
377 }
378 ASSERT(next_id_ >= min_allowed_id_);
379 return next_id_;
380 }
381
382 bool IsIdUsed(int new_id) {
383 return id_set_.find(new_id) != id_set_.end();
384 }
385
386 void SetIdUsed(int new_id) {
387 id_set_.insert(new_id);
388 }
389
390 const int min_allowed_id_;
391 const int max_allowed_id_;
392 int next_id_;
393 std::set<int> id_set_;
394};
395
396// Helper class used for finding duplicate RTP payload types among audio, video
397// and data codecs. When bundle is used the payload types may not collide.
398class UsedPayloadTypes : public UsedIds<Codec> {
399 public:
400 UsedPayloadTypes()
401 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
402 }
403
404
405 private:
406 static const int kDynamicPayloadTypeMin = 96;
407 static const int kDynamicPayloadTypeMax = 127;
408};
409
410// Helper class used for finding duplicate RTP Header extension ids among
411// audio and video extensions.
412class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
413 public:
414 UsedRtpHeaderExtensionIds()
415 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
416 }
417
418 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000419 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000420 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000421 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000422};
423
424static bool IsSctp(const MediaContentDescription* desc) {
425 return ((desc->protocol() == kMediaProtocolSctp) ||
426 (desc->protocol() == kMediaProtocolDtlsSctp));
427}
428
429// Adds a StreamParams for each Stream in Streams with media type
430// media_type to content_description.
431// |current_params| - All currently known StreamParams of any media type.
432template <class C>
433static bool AddStreamParams(
434 MediaType media_type,
435 const MediaSessionOptions::Streams& streams,
436 StreamParamsVec* current_streams,
437 MediaContentDescriptionImpl<C>* content_description,
438 const bool add_legacy_stream) {
439 const bool include_rtx_stream =
440 ContainsRtxCodec(content_description->codecs());
441
442 if (streams.empty() && add_legacy_stream) {
443 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
444 std::vector<uint32> ssrcs;
445 if (IsSctp(content_description)) {
446 GenerateSctpSids(*current_streams, &ssrcs);
447 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000448 int num_ssrcs = include_rtx_stream ? 2 : 1;
449 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450 }
451 if (include_rtx_stream) {
452 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
453 content_description->set_multistream(true);
454 } else {
455 content_description->AddLegacyStream(ssrcs[0]);
456 }
457 return true;
458 }
459
460 MediaSessionOptions::Streams::const_iterator stream_it;
461 for (stream_it = streams.begin();
462 stream_it != streams.end(); ++stream_it) {
463 if (stream_it->type != media_type)
464 continue; // Wrong media type.
465
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000466 const StreamParams* param =
467 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000468 // groupid is empty for StreamParams generated using
469 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000470 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471 // This is a new stream.
472 // Get a CNAME. Either new or same as one of the other synched streams.
473 std::string cname;
474 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
475 &cname)) {
476 return false;
477 }
478
479 std::vector<uint32> ssrcs;
480 if (IsSctp(content_description)) {
481 GenerateSctpSids(*current_streams, &ssrcs);
482 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000483 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484 }
485 StreamParams stream_param;
486 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000487 // Add the generated ssrc.
488 for (size_t i = 0; i < ssrcs.size(); ++i) {
489 stream_param.ssrcs.push_back(ssrcs[i]);
490 }
491 if (stream_it->num_sim_layers > 1) {
492 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
493 stream_param.ssrc_groups.push_back(group);
494 }
495 // Generate an extra ssrc for include_rtx_stream case.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000496 if (include_rtx_stream) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000497 std::vector<uint32> rtx_ssrc;
498 GenerateSsrcs(*current_streams, 1, &rtx_ssrc);
499 stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000500 content_description->set_multistream(true);
501 }
502 stream_param.cname = cname;
503 stream_param.sync_label = stream_it->sync_label;
504 content_description->AddStream(stream_param);
505
506 // Store the new StreamParams in current_streams.
507 // This is necessary so that we can use the CNAME for other media types.
508 current_streams->push_back(stream_param);
509 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000510 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 }
512 }
513 return true;
514}
515
516// Updates the transport infos of the |sdesc| according to the given
517// |bundle_group|. The transport infos of the content names within the
518// |bundle_group| should be updated to use the ufrag and pwd of the first
519// content within the |bundle_group|.
520static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
521 SessionDescription* sdesc) {
522 // The bundle should not be empty.
523 if (!sdesc || !bundle_group.FirstContentName()) {
524 return false;
525 }
526
527 // We should definitely have a transport for the first content.
528 std::string selected_content_name = *bundle_group.FirstContentName();
529 const TransportInfo* selected_transport_info =
530 sdesc->GetTransportInfoByName(selected_content_name);
531 if (!selected_transport_info) {
532 return false;
533 }
534
535 // Set the other contents to use the same ICE credentials.
536 const std::string selected_ufrag =
537 selected_transport_info->description.ice_ufrag;
538 const std::string selected_pwd =
539 selected_transport_info->description.ice_pwd;
540 for (TransportInfos::iterator it =
541 sdesc->transport_infos().begin();
542 it != sdesc->transport_infos().end(); ++it) {
543 if (bundle_group.HasContentName(it->content_name) &&
544 it->content_name != selected_content_name) {
545 it->description.ice_ufrag = selected_ufrag;
546 it->description.ice_pwd = selected_pwd;
547 }
548 }
549 return true;
550}
551
552// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
553// sets it to |cryptos|.
554static bool GetCryptosByName(const SessionDescription* sdesc,
555 const std::string& content_name,
556 CryptoParamsVec* cryptos) {
557 if (!sdesc || !cryptos) {
558 return false;
559 }
560
561 const ContentInfo* content = sdesc->GetContentByName(content_name);
562 if (!IsMediaContent(content) || !content->description) {
563 return false;
564 }
565
566 const MediaContentDescription* media_desc =
567 static_cast<const MediaContentDescription*>(content->description);
568 *cryptos = media_desc->cryptos();
569 return true;
570}
571
572// Predicate function used by the remove_if.
573// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
574static bool CryptoNotFound(const CryptoParams crypto,
575 const CryptoParamsVec* filter) {
576 if (filter == NULL) {
577 return true;
578 }
579 for (CryptoParamsVec::const_iterator it = filter->begin();
580 it != filter->end(); ++it) {
581 if (it->cipher_suite == crypto.cipher_suite) {
582 return false;
583 }
584 }
585 return true;
586}
587
588// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
589// which are not available in |filter|.
590static void PruneCryptos(const CryptoParamsVec& filter,
591 CryptoParamsVec* target_cryptos) {
592 if (!target_cryptos) {
593 return;
594 }
595 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
596 target_cryptos->end(),
597 bind2nd(ptr_fun(CryptoNotFound),
598 &filter)),
599 target_cryptos->end());
600}
601
602static bool IsRtpContent(SessionDescription* sdesc,
603 const std::string& content_name) {
604 bool is_rtp = false;
605 ContentInfo* content = sdesc->GetContentByName(content_name);
606 if (IsMediaContent(content)) {
607 MediaContentDescription* media_desc =
608 static_cast<MediaContentDescription*>(content->description);
609 if (!media_desc) {
610 return false;
611 }
612 is_rtp = media_desc->protocol().empty() ||
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000613 rtc::starts_with(media_desc->protocol().data(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000614 kMediaProtocolRtpPrefix);
615 }
616 return is_rtp;
617}
618
619// Updates the crypto parameters of the |sdesc| according to the given
620// |bundle_group|. The crypto parameters of all the contents within the
621// |bundle_group| should be updated to use the common subset of the
622// available cryptos.
623static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
624 SessionDescription* sdesc) {
625 // The bundle should not be empty.
626 if (!sdesc || !bundle_group.FirstContentName()) {
627 return false;
628 }
629
wu@webrtc.org78187522013-10-07 23:32:02 +0000630 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 // Get the common cryptos.
632 const ContentNames& content_names = bundle_group.content_names();
633 CryptoParamsVec common_cryptos;
634 for (ContentNames::const_iterator it = content_names.begin();
635 it != content_names.end(); ++it) {
636 if (!IsRtpContent(sdesc, *it)) {
637 continue;
638 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000639 // The common cryptos are needed if any of the content does not have DTLS
640 // enabled.
641 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
642 common_cryptos_needed = true;
643 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000644 if (it == content_names.begin()) {
645 // Initial the common_cryptos with the first content in the bundle group.
646 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
647 return false;
648 }
649 if (common_cryptos.empty()) {
650 // If there's no crypto params, we should just return.
651 return true;
652 }
653 } else {
654 CryptoParamsVec cryptos;
655 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
656 return false;
657 }
658 PruneCryptos(cryptos, &common_cryptos);
659 }
660 }
661
wu@webrtc.org78187522013-10-07 23:32:02 +0000662 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000663 return false;
664 }
665
666 // Update to use the common cryptos.
667 for (ContentNames::const_iterator it = content_names.begin();
668 it != content_names.end(); ++it) {
669 if (!IsRtpContent(sdesc, *it)) {
670 continue;
671 }
672 ContentInfo* content = sdesc->GetContentByName(*it);
673 if (IsMediaContent(content)) {
674 MediaContentDescription* media_desc =
675 static_cast<MediaContentDescription*>(content->description);
676 if (!media_desc) {
677 return false;
678 }
679 media_desc->set_cryptos(common_cryptos);
680 }
681 }
682 return true;
683}
684
685template <class C>
686static bool ContainsRtxCodec(const std::vector<C>& codecs) {
687 typename std::vector<C>::const_iterator it;
688 for (it = codecs.begin(); it != codecs.end(); ++it) {
689 if (IsRtxCodec(*it)) {
690 return true;
691 }
692 }
693 return false;
694}
695
696template <class C>
697static bool IsRtxCodec(const C& codec) {
698 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
699}
700
701// Create a media content to be offered in a session-initiate,
702// according to the given options.rtcp_mux, options.is_muc,
703// options.streams, codecs, secure_transport, crypto, and streams. If we don't
704// currently have crypto (in current_cryptos) and it is enabled (in
705// secure_policy), crypto is created (according to crypto_suites). If
706// add_legacy_stream is true, and current_streams is empty, a legacy
707// stream is created. The created content is added to the offer.
708template <class C>
709static bool CreateMediaContentOffer(
710 const MediaSessionOptions& options,
711 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000712 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000713 const CryptoParamsVec* current_cryptos,
714 const std::vector<std::string>& crypto_suites,
715 const RtpHeaderExtensions& rtp_extensions,
716 bool add_legacy_stream,
717 StreamParamsVec* current_streams,
718 MediaContentDescriptionImpl<C>* offer) {
719 offer->AddCodecs(codecs);
720 offer->SortCodecs();
721
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000722 if (secure_policy == SEC_REQUIRED) {
723 offer->set_crypto_required(CT_SDES);
724 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 offer->set_rtcp_mux(options.rtcp_mux_enabled);
726 offer->set_multistream(options.is_muc);
727 offer->set_rtp_header_extensions(rtp_extensions);
728
729 if (!AddStreamParams(
730 offer->type(), options.streams, current_streams,
731 offer, add_legacy_stream)) {
732 return false;
733 }
734
735#ifdef HAVE_SRTP
736 if (secure_policy != SEC_DISABLED) {
737 if (current_cryptos) {
738 AddMediaCryptos(*current_cryptos, offer);
739 }
740 if (offer->cryptos().empty()) {
741 if (!CreateMediaCryptos(crypto_suites, offer)) {
742 return false;
743 }
744 }
745 }
746#endif
747
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000748 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000749 return false;
750 }
751 return true;
752}
753
754template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000755static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
756 const std::string& codec1_id_str,
757 const std::vector<C>& codecs2,
758 const std::string& codec2_id_str) {
759 int codec1_id;
760 int codec2_id;
761 C codec1;
762 C codec2;
763 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
764 !rtc::FromString(codec2_id_str, &codec2_id) ||
765 !FindCodecById(codecs1, codec1_id, &codec1) ||
766 !FindCodecById(codecs2, codec2_id, &codec2)) {
767 return false;
768 }
769 return codec1.Matches(codec2);
770}
771
772template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000773static void NegotiateCodecs(const std::vector<C>& local_codecs,
774 const std::vector<C>& offered_codecs,
775 std::vector<C>* negotiated_codecs) {
776 typename std::vector<C>::const_iterator ours;
777 for (ours = local_codecs.begin();
778 ours != local_codecs.end(); ++ours) {
779 typename std::vector<C>::const_iterator theirs;
780 for (theirs = offered_codecs.begin();
781 theirs != offered_codecs.end(); ++theirs) {
782 if (ours->Matches(*theirs)) {
783 C negotiated = *ours;
784 negotiated.IntersectFeedbackParams(*theirs);
785 if (IsRtxCodec(negotiated)) {
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000786 std::string offered_apt_value;
787 std::string local_apt_value;
788 if (!ours->GetParam(kCodecParamAssociatedPayloadType,
789 &local_apt_value) ||
790 !theirs->GetParam(kCodecParamAssociatedPayloadType,
791 &offered_apt_value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000792 LOG(LS_WARNING) << "RTX missing associated payload type.";
793 continue;
794 }
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000795 // Only negotiate RTX if kCodecParamAssociatedPayloadType has been
796 // set in local and remote codecs, and they match.
797 if (!ReferencedCodecsMatch(local_codecs, local_apt_value,
798 offered_codecs, offered_apt_value)) {
799 LOG(LS_WARNING) << "RTX associated codecs don't match.";
800 continue;
801 }
802 negotiated.SetParam(kCodecParamAssociatedPayloadType,
803 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000804 }
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000805
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000806 negotiated.id = theirs->id;
wu@webrtc.orgff1b1bf2014-06-20 20:57:42 +0000807 // RFC3264: Although the answerer MAY list the formats in their desired
808 // order of preference, it is RECOMMENDED that unless there is a
809 // specific reason, the answerer list formats in the same relative order
810 // they were present in the offer.
811 negotiated.preference = theirs->preference;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000812 negotiated_codecs->push_back(negotiated);
813 }
814 }
815 }
816}
817
818template <class C>
819static bool FindMatchingCodec(const std::vector<C>& codecs,
820 const C& codec_to_match,
821 C* found_codec) {
822 for (typename std::vector<C>::const_iterator it = codecs.begin();
823 it != codecs.end(); ++it) {
824 if (it->Matches(codec_to_match)) {
825 if (found_codec != NULL) {
826 *found_codec= *it;
827 }
828 return true;
829 }
830 }
831 return false;
832}
833
834// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
835// already exist in |offered_codecs| and ensure the payload types don't
836// collide.
837template <class C>
838static void FindCodecsToOffer(
839 const std::vector<C>& reference_codecs,
840 std::vector<C>* offered_codecs,
841 UsedPayloadTypes* used_pltypes) {
842
843 typedef std::map<int, C> RtxCodecReferences;
844 RtxCodecReferences new_rtx_codecs;
845
846 // Find all new RTX codecs.
847 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
848 it != reference_codecs.end(); ++it) {
849 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) {
850 C rtx_codec = *it;
851 int referenced_pl_type =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000852 rtc::FromString<int>(0,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000853 rtx_codec.params[kCodecParamAssociatedPayloadType]);
854 new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type,
855 rtx_codec));
856 }
857 }
858
859 // Add all new codecs that are not RTX codecs.
860 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
861 it != reference_codecs.end(); ++it) {
862 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) {
863 C codec = *it;
864 int original_payload_id = codec.id;
865 used_pltypes->FindAndSetIdUsed(&codec);
866 offered_codecs->push_back(codec);
867
868 // If this codec is referenced by a new RTX codec, update the reference
869 // in the RTX codec with the new payload type.
870 typename RtxCodecReferences::iterator rtx_it =
871 new_rtx_codecs.find(original_payload_id);
872 if (rtx_it != new_rtx_codecs.end()) {
873 C& rtx_codec = rtx_it->second;
874 rtx_codec.params[kCodecParamAssociatedPayloadType] =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000875 rtc::ToString(codec.id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000876 }
877 }
878 }
879
880 // Add all new RTX codecs.
881 for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin();
882 it != new_rtx_codecs.end(); ++it) {
883 C& rtx_codec = it->second;
884 used_pltypes->FindAndSetIdUsed(&rtx_codec);
885 offered_codecs->push_back(rtx_codec);
886 }
887}
888
889
890static bool FindByUri(const RtpHeaderExtensions& extensions,
891 const RtpHeaderExtension& ext_to_match,
892 RtpHeaderExtension* found_extension) {
893 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
894 it != extensions.end(); ++it) {
895 // We assume that all URIs are given in a canonical format.
896 if (it->uri == ext_to_match.uri) {
897 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000898 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000899 }
900 return true;
901 }
902 }
903 return false;
904}
905
906static void FindAndSetRtpHdrExtUsed(
907 const RtpHeaderExtensions& reference_extensions,
908 RtpHeaderExtensions* offered_extensions,
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000909 const RtpHeaderExtensions& other_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 UsedRtpHeaderExtensionIds* used_extensions) {
911 for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin();
912 it != reference_extensions.end(); ++it) {
913 if (!FindByUri(*offered_extensions, *it, NULL)) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000914 RtpHeaderExtension ext;
915 if (!FindByUri(other_extensions, *it, &ext)) {
916 ext = *it;
917 used_extensions->FindAndSetIdUsed(&ext);
918 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000919 offered_extensions->push_back(ext);
920 }
921 }
922}
923
924static void NegotiateRtpHeaderExtensions(
925 const RtpHeaderExtensions& local_extensions,
926 const RtpHeaderExtensions& offered_extensions,
927 RtpHeaderExtensions* negotiated_extenstions) {
928 RtpHeaderExtensions::const_iterator ours;
929 for (ours = local_extensions.begin();
930 ours != local_extensions.end(); ++ours) {
931 RtpHeaderExtension theirs;
932 if (FindByUri(offered_extensions, *ours, &theirs)) {
933 // We respond with their RTP header extension id.
934 negotiated_extenstions->push_back(theirs);
935 }
936 }
937}
938
939static void StripCNCodecs(AudioCodecs* audio_codecs) {
940 AudioCodecs::iterator iter = audio_codecs->begin();
941 while (iter != audio_codecs->end()) {
942 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
943 iter = audio_codecs->erase(iter);
944 } else {
945 ++iter;
946 }
947 }
948}
949
950// Create a media content to be answered in a session-accept,
951// according to the given options.rtcp_mux, options.streams, codecs,
952// crypto, and streams. If we don't currently have crypto (in
953// current_cryptos) and it is enabled (in secure_policy), crypto is
954// created (according to crypto_suites). If add_legacy_stream is
955// true, and current_streams is empty, a legacy stream is created.
956// The codecs, rtcp_mux, and crypto are all negotiated with the offer
957// from the incoming session-initiate. If the negotiation fails, this
958// method returns false. The created content is added to the offer.
959template <class C>
960static bool CreateMediaContentAnswer(
961 const MediaContentDescriptionImpl<C>* offer,
962 const MediaSessionOptions& options,
963 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000964 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000965 const CryptoParamsVec* current_cryptos,
966 const RtpHeaderExtensions& local_rtp_extenstions,
967 StreamParamsVec* current_streams,
968 bool add_legacy_stream,
969 bool bundle_enabled,
970 MediaContentDescriptionImpl<C>* answer) {
971 std::vector<C> negotiated_codecs;
972 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
973 answer->AddCodecs(negotiated_codecs);
974 answer->SortCodecs();
975 answer->set_protocol(offer->protocol());
976 RtpHeaderExtensions negotiated_rtp_extensions;
977 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
978 offer->rtp_header_extensions(),
979 &negotiated_rtp_extensions);
980 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
981
982 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
983
984 if (sdes_policy != SEC_DISABLED) {
985 CryptoParams crypto;
986 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
987 if (current_cryptos) {
988 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
989 }
990 answer->AddCrypto(crypto);
991 }
992 }
993
994 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000995 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000996 return false;
997 }
998
999 if (!AddStreamParams(
1000 answer->type(), options.streams, current_streams,
1001 answer, add_legacy_stream)) {
1002 return false; // Something went seriously wrong.
1003 }
1004
1005 // Make sure the answer media content direction is per default set as
1006 // described in RFC3264 section 6.1.
1007 switch (offer->direction()) {
1008 case MD_INACTIVE:
1009 answer->set_direction(MD_INACTIVE);
1010 break;
1011 case MD_SENDONLY:
1012 answer->set_direction(MD_RECVONLY);
1013 break;
1014 case MD_RECVONLY:
1015 answer->set_direction(MD_SENDONLY);
1016 break;
1017 case MD_SENDRECV:
1018 answer->set_direction(MD_SENDRECV);
1019 break;
1020 default:
1021 break;
1022 }
1023
1024 return true;
1025}
1026
1027static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001028 const std::string& protocol,
1029 bool secure_transport) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001030 // Data channels can have a protocol of SCTP or SCTP/DTLS.
1031 if (type == MEDIA_TYPE_DATA &&
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001032 ((protocol == kMediaProtocolSctp && !secure_transport)||
1033 (protocol == kMediaProtocolDtlsSctp && secure_transport))) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001034 return true;
1035 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001036
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001037 // Since not all applications serialize and deserialize the media protocol,
1038 // we will have to accept |protocol| to be empty.
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001039 return protocol == kMediaProtocolAvpf || protocol.empty() ||
1040 protocol == kMediaProtocolSavpf ||
1041 (protocol == kMediaProtocolDtlsSavpf && secure_transport);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001042}
1043
1044static void SetMediaProtocol(bool secure_transport,
1045 MediaContentDescription* desc) {
1046 if (!desc->cryptos().empty() || secure_transport)
1047 desc->set_protocol(kMediaProtocolSavpf);
1048 else
1049 desc->set_protocol(kMediaProtocolAvpf);
1050}
1051
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001052// Gets the TransportInfo of the given |content_name| from the
1053// |current_description|. If doesn't exist, returns a new one.
1054static const TransportDescription* GetTransportDescription(
1055 const std::string& content_name,
1056 const SessionDescription* current_description) {
1057 const TransportDescription* desc = NULL;
1058 if (current_description) {
1059 const TransportInfo* info =
1060 current_description->GetTransportInfoByName(content_name);
1061 if (info) {
1062 desc = &info->description;
1063 }
1064 }
1065 return desc;
1066}
1067
1068// Gets the current DTLS state from the transport description.
1069static bool IsDtlsActive(
1070 const std::string& content_name,
1071 const SessionDescription* current_description) {
1072 if (!current_description)
1073 return false;
1074
1075 const ContentInfo* content =
1076 current_description->GetContentByName(content_name);
1077 if (!content)
1078 return false;
1079
1080 const TransportDescription* current_tdesc =
1081 GetTransportDescription(content_name, current_description);
1082 if (!current_tdesc)
1083 return false;
1084
1085 return current_tdesc->secure();
1086}
1087
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001088std::string MediaTypeToString(MediaType type) {
1089 std::string type_str;
1090 switch (type) {
1091 case MEDIA_TYPE_AUDIO:
1092 type_str = "audio";
1093 break;
1094 case MEDIA_TYPE_VIDEO:
1095 type_str = "video";
1096 break;
1097 case MEDIA_TYPE_DATA:
1098 type_str = "data";
1099 break;
1100 default:
1101 ASSERT(false);
1102 break;
1103 }
1104 return type_str;
1105}
1106
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001107void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 const std::string& id,
1109 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001110 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001111}
1112
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001113void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001114 const std::string& id,
1115 const std::string& sync_label,
1116 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001117 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001118}
1119
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001120void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001121 MediaType type,
1122 const std::string& id,
1123 const std::string& sync_label,
1124 int num_sim_layers) {
1125 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001126
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001127 // If we haven't already set the data_channel_type, and we add a
1128 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001129 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001130 data_channel_type = DCT_RTP;
1131}
1132
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001133void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001134 const std::string& id) {
1135 Streams::iterator stream_it = streams.begin();
1136 for (; stream_it != streams.end(); ++stream_it) {
1137 if (stream_it->type == type && stream_it->id == id) {
1138 streams.erase(stream_it);
1139 return;
1140 }
1141 }
1142 ASSERT(false);
1143}
1144
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001145bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1146 Streams::const_iterator stream_it = streams.begin();
1147 for (; stream_it != streams.end(); ++stream_it) {
1148 if (stream_it->type == type) {
1149 return true;
1150 }
1151 }
1152 return false;
1153}
1154
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001155MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1156 const TransportDescriptionFactory* transport_desc_factory)
1157 : secure_(SEC_DISABLED),
1158 add_legacy_(true),
1159 transport_desc_factory_(transport_desc_factory) {
1160}
1161
1162MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1163 ChannelManager* channel_manager,
1164 const TransportDescriptionFactory* transport_desc_factory)
1165 : secure_(SEC_DISABLED),
1166 add_legacy_(true),
1167 transport_desc_factory_(transport_desc_factory) {
1168 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1169 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1170 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1171 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1172 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1173}
1174
1175SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1176 const MediaSessionOptions& options,
1177 const SessionDescription* current_description) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001178 scoped_ptr<SessionDescription> offer(new SessionDescription());
1179
1180 StreamParamsVec current_streams;
1181 GetCurrentStreamParams(current_description, &current_streams);
1182
1183 AudioCodecs audio_codecs;
1184 VideoCodecs video_codecs;
1185 DataCodecs data_codecs;
1186 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1187 &data_codecs);
1188
1189 if (!options.vad_enabled) {
1190 // If application doesn't want CN codecs in offer.
1191 StripCNCodecs(&audio_codecs);
1192 }
1193
1194 RtpHeaderExtensions audio_rtp_extensions;
1195 RtpHeaderExtensions video_rtp_extensions;
1196 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1197 &video_rtp_extensions);
1198
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001199 bool audio_added = false;
1200 bool video_added = false;
1201 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001202
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001203 // Iterate through the contents of |current_description| to maintain the order
1204 // of the m-lines in the new offer.
1205 if (current_description) {
1206 ContentInfos::const_iterator it = current_description->contents().begin();
1207 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001208 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001209 if (!AddAudioContentForOffer(options, current_description,
1210 audio_rtp_extensions, audio_codecs,
1211 &current_streams, offer.get())) {
1212 return NULL;
1213 }
1214 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001215 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001216 if (!AddVideoContentForOffer(options, current_description,
1217 video_rtp_extensions, video_codecs,
1218 &current_streams, offer.get())) {
1219 return NULL;
1220 }
1221 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001222 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001223 MediaSessionOptions options_copy(options);
1224 if (IsSctp(static_cast<const MediaContentDescription*>(
1225 it->description))) {
1226 options_copy.data_channel_type = DCT_SCTP;
1227 }
1228 if (!AddDataContentForOffer(options_copy, current_description,
1229 &data_codecs, &current_streams,
1230 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001231 return NULL;
1232 }
1233 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001234 } else {
1235 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001236 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001237 }
1238 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001239
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001240 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001241 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001242 !AddAudioContentForOffer(options, current_description,
1243 audio_rtp_extensions, audio_codecs,
1244 &current_streams, offer.get())) {
1245 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001246 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001247 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001248 !AddVideoContentForOffer(options, current_description,
1249 video_rtp_extensions, video_codecs,
1250 &current_streams, offer.get())) {
1251 return NULL;
1252 }
1253 if (!data_added && options.has_data() &&
1254 !AddDataContentForOffer(options, current_description, &data_codecs,
1255 &current_streams, offer.get())) {
1256 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001257 }
1258
1259 // Bundle the contents together, if we've been asked to do so, and update any
1260 // parameters that need to be tweaked for BUNDLE.
1261 if (options.bundle_enabled) {
1262 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1263 for (ContentInfos::const_iterator content = offer->contents().begin();
1264 content != offer->contents().end(); ++content) {
1265 offer_bundle.AddContentName(content->name);
1266 }
1267 offer->AddGroup(offer_bundle);
1268 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1269 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1270 return NULL;
1271 }
1272 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1273 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1274 return NULL;
1275 }
1276 }
1277
1278 return offer.release();
1279}
1280
1281SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1282 const SessionDescription* offer, const MediaSessionOptions& options,
1283 const SessionDescription* current_description) const {
1284 // The answer contains the intersection of the codecs in the offer with the
1285 // codecs we support, ordered by our local preference. As indicated by
1286 // XEP-0167, we retain the same payload ids from the offer in the answer.
1287 scoped_ptr<SessionDescription> answer(new SessionDescription());
1288
1289 StreamParamsVec current_streams;
1290 GetCurrentStreamParams(current_description, &current_streams);
1291
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001292 if (offer) {
1293 ContentInfos::const_iterator it = offer->contents().begin();
1294 for (; it != offer->contents().end(); ++it) {
1295 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1296 if (!AddAudioContentForAnswer(offer, options, current_description,
1297 &current_streams, answer.get())) {
1298 return NULL;
1299 }
1300 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1301 if (!AddVideoContentForAnswer(offer, options, current_description,
1302 &current_streams, answer.get())) {
1303 return NULL;
1304 }
1305 } else {
1306 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1307 if (!AddDataContentForAnswer(offer, options, current_description,
1308 &current_streams, answer.get())) {
1309 return NULL;
1310 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001311 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001312 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001313 }
1314
1315 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1316 // group in the answer with the appropriate content names.
1317 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1318 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1319 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1320 for (ContentInfos::const_iterator content = answer->contents().begin();
1321 content != answer->contents().end(); ++content) {
1322 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1323 answer_bundle.AddContentName(content->name);
1324 }
1325 }
1326 if (answer_bundle.FirstContentName()) {
1327 answer->AddGroup(answer_bundle);
1328
1329 // Share the same ICE credentials and crypto params across all contents,
1330 // as BUNDLE requires.
1331 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1332 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1333 return NULL;
1334 }
1335
1336 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1337 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1338 return NULL;
1339 }
1340 }
1341 }
1342
1343 return answer.release();
1344}
1345
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001346void MediaSessionDescriptionFactory::GetCodecsToOffer(
1347 const SessionDescription* current_description,
1348 AudioCodecs* audio_codecs,
1349 VideoCodecs* video_codecs,
1350 DataCodecs* data_codecs) const {
1351 UsedPayloadTypes used_pltypes;
1352 audio_codecs->clear();
1353 video_codecs->clear();
1354 data_codecs->clear();
1355
1356
1357 // First - get all codecs from the current description if the media type
1358 // is used.
1359 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1360 // type is added.
1361 if (current_description) {
1362 const AudioContentDescription* audio =
1363 GetFirstAudioContentDescription(current_description);
1364 if (audio) {
1365 *audio_codecs = audio->codecs();
1366 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1367 }
1368 const VideoContentDescription* video =
1369 GetFirstVideoContentDescription(current_description);
1370 if (video) {
1371 *video_codecs = video->codecs();
1372 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1373 }
1374 const DataContentDescription* data =
1375 GetFirstDataContentDescription(current_description);
1376 if (data) {
1377 *data_codecs = data->codecs();
1378 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1379 }
1380 }
1381
1382 // Add our codecs that are not in |current_description|.
1383 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1384 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1385 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1386}
1387
1388void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1389 const SessionDescription* current_description,
1390 RtpHeaderExtensions* audio_extensions,
1391 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001392 // All header extensions allocated from the same range to avoid potential
1393 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001394 UsedRtpHeaderExtensionIds used_ids;
1395 audio_extensions->clear();
1396 video_extensions->clear();
1397
1398 // First - get all extensions from the current description if the media type
1399 // is used.
1400 // Add them to |used_ids| so the local ids are not reused if a new media
1401 // type is added.
1402 if (current_description) {
1403 const AudioContentDescription* audio =
1404 GetFirstAudioContentDescription(current_description);
1405 if (audio) {
1406 *audio_extensions = audio->rtp_header_extensions();
1407 used_ids.FindAndSetIdUsed(audio_extensions);
1408 }
1409 const VideoContentDescription* video =
1410 GetFirstVideoContentDescription(current_description);
1411 if (video) {
1412 *video_extensions = video->rtp_header_extensions();
1413 used_ids.FindAndSetIdUsed(video_extensions);
1414 }
1415 }
1416
1417 // Add our default RTP header extensions that are not in
1418 // |current_description|.
1419 FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions,
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001420 *video_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001421 FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions,
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001422 *audio_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001423}
1424
1425bool MediaSessionDescriptionFactory::AddTransportOffer(
1426 const std::string& content_name,
1427 const TransportOptions& transport_options,
1428 const SessionDescription* current_desc,
1429 SessionDescription* offer_desc) const {
1430 if (!transport_desc_factory_)
1431 return false;
1432 const TransportDescription* current_tdesc =
1433 GetTransportDescription(content_name, current_desc);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001434 rtc::scoped_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001435 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1436 bool ret = (new_tdesc.get() != NULL &&
1437 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1438 if (!ret) {
1439 LOG(LS_ERROR)
1440 << "Failed to AddTransportOffer, content name=" << content_name;
1441 }
1442 return ret;
1443}
1444
1445TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1446 const std::string& content_name,
1447 const SessionDescription* offer_desc,
1448 const TransportOptions& transport_options,
1449 const SessionDescription* current_desc) const {
1450 if (!transport_desc_factory_)
1451 return NULL;
1452 const TransportDescription* offer_tdesc =
1453 GetTransportDescription(content_name, offer_desc);
1454 const TransportDescription* current_tdesc =
1455 GetTransportDescription(content_name, current_desc);
1456 return
1457 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1458 current_tdesc);
1459}
1460
1461bool MediaSessionDescriptionFactory::AddTransportAnswer(
1462 const std::string& content_name,
1463 const TransportDescription& transport_desc,
1464 SessionDescription* answer_desc) const {
1465 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1466 transport_desc))) {
1467 LOG(LS_ERROR)
1468 << "Failed to AddTransportAnswer, content name=" << content_name;
1469 return false;
1470 }
1471 return true;
1472}
1473
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001474bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1475 const MediaSessionOptions& options,
1476 const SessionDescription* current_description,
1477 const RtpHeaderExtensions& audio_rtp_extensions,
1478 const AudioCodecs& audio_codecs,
1479 StreamParamsVec* current_streams,
1480 SessionDescription* desc) const {
1481 cricket::SecurePolicy sdes_policy =
1482 IsDtlsActive(CN_AUDIO, current_description) ?
1483 cricket::SEC_DISABLED : secure();
1484
1485 scoped_ptr<AudioContentDescription> audio(new AudioContentDescription());
1486 std::vector<std::string> crypto_suites;
1487 GetSupportedAudioCryptoSuites(&crypto_suites);
1488 if (!CreateMediaContentOffer(
1489 options,
1490 audio_codecs,
1491 sdes_policy,
1492 GetCryptos(GetFirstAudioContentDescription(current_description)),
1493 crypto_suites,
1494 audio_rtp_extensions,
1495 add_legacy_,
1496 current_streams,
1497 audio.get())) {
1498 return false;
1499 }
1500 audio->set_lang(lang_);
1501
1502 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1503 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001504
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001505 if (!options.recv_audio) {
1506 audio->set_direction(MD_SENDONLY);
1507 }
1508
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001509 desc->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio.release());
1510 if (!AddTransportOffer(CN_AUDIO, options.transport_options,
1511 current_description, desc)) {
1512 return false;
1513 }
1514
1515 return true;
1516}
1517
1518bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1519 const MediaSessionOptions& options,
1520 const SessionDescription* current_description,
1521 const RtpHeaderExtensions& video_rtp_extensions,
1522 const VideoCodecs& video_codecs,
1523 StreamParamsVec* current_streams,
1524 SessionDescription* desc) const {
1525 cricket::SecurePolicy sdes_policy =
1526 IsDtlsActive(CN_VIDEO, current_description) ?
1527 cricket::SEC_DISABLED : secure();
1528
1529 scoped_ptr<VideoContentDescription> video(new VideoContentDescription());
1530 std::vector<std::string> crypto_suites;
1531 GetSupportedVideoCryptoSuites(&crypto_suites);
1532 if (!CreateMediaContentOffer(
1533 options,
1534 video_codecs,
1535 sdes_policy,
1536 GetCryptos(GetFirstVideoContentDescription(current_description)),
1537 crypto_suites,
1538 video_rtp_extensions,
1539 add_legacy_,
1540 current_streams,
1541 video.get())) {
1542 return false;
1543 }
1544
1545 video->set_bandwidth(options.video_bandwidth);
1546
1547 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1548 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001549
1550 if (!options.recv_video) {
1551 video->set_direction(MD_SENDONLY);
1552 }
1553
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001554 desc->AddContent(CN_VIDEO, NS_JINGLE_RTP, video.release());
1555 if (!AddTransportOffer(CN_VIDEO, options.transport_options,
1556 current_description, desc)) {
1557 return false;
1558 }
1559
1560 return true;
1561}
1562
1563bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1564 const MediaSessionOptions& options,
1565 const SessionDescription* current_description,
1566 DataCodecs* data_codecs,
1567 StreamParamsVec* current_streams,
1568 SessionDescription* desc) const {
1569 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1570
1571 scoped_ptr<DataContentDescription> data(new DataContentDescription());
1572 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1573
1574 FilterDataCodecs(data_codecs, is_sctp);
1575
1576 cricket::SecurePolicy sdes_policy =
1577 IsDtlsActive(CN_DATA, current_description) ?
1578 cricket::SEC_DISABLED : secure();
1579 std::vector<std::string> crypto_suites;
1580 if (is_sctp) {
1581 // SDES doesn't make sense for SCTP, so we disable it, and we only
1582 // get SDES crypto suites for RTP-based data channels.
1583 sdes_policy = cricket::SEC_DISABLED;
1584 // Unlike SetMediaProtocol below, we need to set the protocol
1585 // before we call CreateMediaContentOffer. Otherwise,
1586 // CreateMediaContentOffer won't know this is SCTP and will
1587 // generate SSRCs rather than SIDs.
1588 data->set_protocol(
1589 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1590 } else {
1591 GetSupportedDataCryptoSuites(&crypto_suites);
1592 }
1593
1594 if (!CreateMediaContentOffer(
1595 options,
1596 *data_codecs,
1597 sdes_policy,
1598 GetCryptos(GetFirstDataContentDescription(current_description)),
1599 crypto_suites,
1600 RtpHeaderExtensions(),
1601 add_legacy_,
1602 current_streams,
1603 data.get())) {
1604 return false;
1605 }
1606
1607 if (is_sctp) {
1608 desc->AddContent(CN_DATA, NS_JINGLE_DRAFT_SCTP, data.release());
1609 } else {
1610 data->set_bandwidth(options.data_bandwidth);
1611 SetMediaProtocol(secure_transport, data.get());
1612 desc->AddContent(CN_DATA, NS_JINGLE_RTP, data.release());
1613 }
1614 if (!AddTransportOffer(CN_DATA, options.transport_options,
1615 current_description, desc)) {
1616 return false;
1617 }
1618 return true;
1619}
1620
1621bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1622 const SessionDescription* offer,
1623 const MediaSessionOptions& options,
1624 const SessionDescription* current_description,
1625 StreamParamsVec* current_streams,
1626 SessionDescription* answer) const {
1627 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1628
1629 scoped_ptr<TransportDescription> audio_transport(
1630 CreateTransportAnswer(audio_content->name, offer,
1631 options.transport_options,
1632 current_description));
1633 if (!audio_transport) {
1634 return false;
1635 }
1636
1637 AudioCodecs audio_codecs = audio_codecs_;
1638 if (!options.vad_enabled) {
1639 StripCNCodecs(&audio_codecs);
1640 }
1641
1642 bool bundle_enabled =
1643 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1644 scoped_ptr<AudioContentDescription> audio_answer(
1645 new AudioContentDescription());
1646 // Do not require or create SDES cryptos if DTLS is used.
1647 cricket::SecurePolicy sdes_policy =
1648 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1649 if (!CreateMediaContentAnswer(
1650 static_cast<const AudioContentDescription*>(
1651 audio_content->description),
1652 options,
1653 audio_codecs,
1654 sdes_policy,
1655 GetCryptos(GetFirstAudioContentDescription(current_description)),
1656 audio_rtp_extensions_,
1657 current_streams,
1658 add_legacy_,
1659 bundle_enabled,
1660 audio_answer.get())) {
1661 return false; // Fails the session setup.
1662 }
1663
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001664 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001665 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1666 audio_answer->protocol(),
1667 audio_transport->secure());
1668 if (!rejected) {
1669 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1670 } else {
1671 // RFC 3264
1672 // The answer MUST contain the same number of m-lines as the offer.
1673 LOG(LS_INFO) << "Audio is not supported in the answer.";
1674 }
1675
1676 answer->AddContent(audio_content->name, audio_content->type, rejected,
1677 audio_answer.release());
1678 return true;
1679}
1680
1681bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1682 const SessionDescription* offer,
1683 const MediaSessionOptions& options,
1684 const SessionDescription* current_description,
1685 StreamParamsVec* current_streams,
1686 SessionDescription* answer) const {
1687 const ContentInfo* video_content = GetFirstVideoContent(offer);
1688 scoped_ptr<TransportDescription> video_transport(
1689 CreateTransportAnswer(video_content->name, offer,
1690 options.transport_options,
1691 current_description));
1692 if (!video_transport) {
1693 return false;
1694 }
1695
1696 scoped_ptr<VideoContentDescription> video_answer(
1697 new VideoContentDescription());
1698 // Do not require or create SDES cryptos if DTLS is used.
1699 cricket::SecurePolicy sdes_policy =
1700 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1701 bool bundle_enabled =
1702 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1703 if (!CreateMediaContentAnswer(
1704 static_cast<const VideoContentDescription*>(
1705 video_content->description),
1706 options,
1707 video_codecs_,
1708 sdes_policy,
1709 GetCryptos(GetFirstVideoContentDescription(current_description)),
1710 video_rtp_extensions_,
1711 current_streams,
1712 add_legacy_,
1713 bundle_enabled,
1714 video_answer.get())) {
1715 return false;
1716 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001717 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001718 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1719 video_answer->protocol(),
1720 video_transport->secure());
1721 if (!rejected) {
1722 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1723 answer)) {
1724 return false;
1725 }
1726 video_answer->set_bandwidth(options.video_bandwidth);
1727 } else {
1728 // RFC 3264
1729 // The answer MUST contain the same number of m-lines as the offer.
1730 LOG(LS_INFO) << "Video is not supported in the answer.";
1731 }
1732 answer->AddContent(video_content->name, video_content->type, rejected,
1733 video_answer.release());
1734 return true;
1735}
1736
1737bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1738 const SessionDescription* offer,
1739 const MediaSessionOptions& options,
1740 const SessionDescription* current_description,
1741 StreamParamsVec* current_streams,
1742 SessionDescription* answer) const {
1743 const ContentInfo* data_content = GetFirstDataContent(offer);
1744 scoped_ptr<TransportDescription> data_transport(
1745 CreateTransportAnswer(data_content->name, offer,
1746 options.transport_options,
1747 current_description));
1748 if (!data_transport) {
1749 return false;
1750 }
1751 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1752 std::vector<DataCodec> data_codecs(data_codecs_);
1753 FilterDataCodecs(&data_codecs, is_sctp);
1754
1755 scoped_ptr<DataContentDescription> data_answer(
1756 new DataContentDescription());
1757 // Do not require or create SDES cryptos if DTLS is used.
1758 cricket::SecurePolicy sdes_policy =
1759 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1760 bool bundle_enabled =
1761 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1762 if (!CreateMediaContentAnswer(
1763 static_cast<const DataContentDescription*>(
1764 data_content->description),
1765 options,
1766 data_codecs_,
1767 sdes_policy,
1768 GetCryptos(GetFirstDataContentDescription(current_description)),
1769 RtpHeaderExtensions(),
1770 current_streams,
1771 add_legacy_,
1772 bundle_enabled,
1773 data_answer.get())) {
1774 return false; // Fails the session setup.
1775 }
1776
1777 bool rejected = !options.has_data() || data_content->rejected ||
1778 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1779 data_answer->protocol(),
1780 data_transport->secure());
1781 if (!rejected) {
1782 data_answer->set_bandwidth(options.data_bandwidth);
1783 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1784 answer)) {
1785 return false;
1786 }
1787 } else {
1788 // RFC 3264
1789 // The answer MUST contain the same number of m-lines as the offer.
1790 LOG(LS_INFO) << "Data is not supported in the answer.";
1791 }
1792 answer->AddContent(data_content->name, data_content->type, rejected,
1793 data_answer.release());
1794 return true;
1795}
1796
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001797bool IsMediaContent(const ContentInfo* content) {
1798 return (content &&
1799 (content->type == NS_JINGLE_RTP ||
1800 content->type == NS_JINGLE_DRAFT_SCTP));
1801}
1802
1803bool IsAudioContent(const ContentInfo* content) {
1804 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1805}
1806
1807bool IsVideoContent(const ContentInfo* content) {
1808 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1809}
1810
1811bool IsDataContent(const ContentInfo* content) {
1812 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1813}
1814
1815static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1816 MediaType media_type) {
1817 for (ContentInfos::const_iterator content = contents.begin();
1818 content != contents.end(); content++) {
1819 if (IsMediaContentOfType(&*content, media_type)) {
1820 return &*content;
1821 }
1822 }
1823 return NULL;
1824}
1825
1826const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1827 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1828}
1829
1830const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1831 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1832}
1833
1834const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1835 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1836}
1837
1838static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1839 MediaType media_type) {
1840 if (sdesc == NULL)
1841 return NULL;
1842
1843 return GetFirstMediaContent(sdesc->contents(), media_type);
1844}
1845
1846const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1847 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1848}
1849
1850const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1851 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1852}
1853
1854const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1855 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1856}
1857
1858const MediaContentDescription* GetFirstMediaContentDescription(
1859 const SessionDescription* sdesc, MediaType media_type) {
1860 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1861 const ContentDescription* description = content ? content->description : NULL;
1862 return static_cast<const MediaContentDescription*>(description);
1863}
1864
1865const AudioContentDescription* GetFirstAudioContentDescription(
1866 const SessionDescription* sdesc) {
1867 return static_cast<const AudioContentDescription*>(
1868 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1869}
1870
1871const VideoContentDescription* GetFirstVideoContentDescription(
1872 const SessionDescription* sdesc) {
1873 return static_cast<const VideoContentDescription*>(
1874 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1875}
1876
1877const DataContentDescription* GetFirstDataContentDescription(
1878 const SessionDescription* sdesc) {
1879 return static_cast<const DataContentDescription*>(
1880 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1881}
1882
1883bool GetMediaChannelNameFromComponent(
1884 int component, MediaType media_type, std::string* channel_name) {
1885 if (media_type == MEDIA_TYPE_AUDIO) {
1886 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1887 *channel_name = GICE_CHANNEL_NAME_RTP;
1888 return true;
1889 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1890 *channel_name = GICE_CHANNEL_NAME_RTCP;
1891 return true;
1892 }
1893 } else if (media_type == MEDIA_TYPE_VIDEO) {
1894 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1895 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTP;
1896 return true;
1897 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1898 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTCP;
1899 return true;
1900 }
1901 } else if (media_type == MEDIA_TYPE_DATA) {
1902 if (component == ICE_CANDIDATE_COMPONENT_RTP) {
1903 *channel_name = GICE_CHANNEL_NAME_DATA_RTP;
1904 return true;
1905 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) {
1906 *channel_name = GICE_CHANNEL_NAME_DATA_RTCP;
1907 return true;
1908 }
1909 }
1910
1911 return false;
1912}
1913
1914bool GetMediaComponentFromChannelName(
1915 const std::string& channel_name, int* component) {
1916 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1917 channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1918 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1919 *component = ICE_CANDIDATE_COMPONENT_RTP;
1920 return true;
1921 } else if (channel_name == GICE_CHANNEL_NAME_RTCP ||
1922 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP ||
1923 channel_name == GICE_CHANNEL_NAME_DATA_RTP) {
1924 *component = ICE_CANDIDATE_COMPONENT_RTCP;
1925 return true;
1926 }
1927
1928 return false;
1929}
1930
1931bool GetMediaTypeFromChannelName(
1932 const std::string& channel_name, MediaType* media_type) {
1933 if (channel_name == GICE_CHANNEL_NAME_RTP ||
1934 channel_name == GICE_CHANNEL_NAME_RTCP) {
1935 *media_type = MEDIA_TYPE_AUDIO;
1936 return true;
1937 } else if (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP ||
1938 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP) {
1939 *media_type = MEDIA_TYPE_VIDEO;
1940 return true;
1941 } else if (channel_name == GICE_CHANNEL_NAME_DATA_RTP ||
1942 channel_name == GICE_CHANNEL_NAME_DATA_RTCP) {
1943 *media_type = MEDIA_TYPE_DATA;
1944 return true;
1945 }
1946
1947 return false;
1948}
1949
1950} // namespace cricket