blob: 6a98f7d07681d2a2c0c2d19f1c969595487f8883 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010011#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -080013#include <algorithm> // For std::find_if.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <functional>
15#include <map>
kwiberg31022942016-03-11 14:18:21 -080016#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <set>
18#include <utility>
19
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000020#include "webrtc/base/helpers.h"
21#include "webrtc/base/logging.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000022#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080023#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080024#include "webrtc/media/base/mediaconstants.h"
25#include "webrtc/p2p/base/p2pconstants.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010026#include "webrtc/pc/channelmanager.h"
27#include "webrtc/pc/srtpfilter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000028
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000029#ifdef HAVE_SCTP
kjellandera96e2d72016-02-04 23:52:28 -080030#include "webrtc/media/sctp/sctpdataengine.h"
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000031#else
Peter Boström0c4e06b2015-10-07 12:23:21 +020032static const uint32_t kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000033#endif
34
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035namespace {
36const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080037
38void GetSupportedCryptoSuiteNames(void (*func)(std::vector<int>*),
39 std::vector<std::string>* names) {
40#ifdef HAVE_SRTP
41 std::vector<int> crypto_suites;
42 func(&crypto_suites);
43 for (const auto crypto : crypto_suites) {
44 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
45 }
46#endif
47}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048}
49
50namespace cricket {
51
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052
53// RTP Profile names
54// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
55// RFC4585
56const char kMediaProtocolAvpf[] = "RTP/AVPF";
57// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000058const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
59
deadbeeff3938292015-07-15 12:20:53 -070060// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
61// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062const char kMediaProtocolSavpf[] = "RTP/SAVPF";
63
64const char kMediaProtocolRtpPrefix[] = "RTP/";
65
66const char kMediaProtocolSctp[] = "SCTP";
67const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000068const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000069const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070
71static bool IsMediaContentOfType(const ContentInfo* content,
72 MediaType media_type) {
73 if (!IsMediaContent(content)) {
74 return false;
75 }
76
77 const MediaContentDescription* mdesc =
78 static_cast<const MediaContentDescription*>(content->description);
79 return mdesc && mdesc->type() == media_type;
80}
81
82static bool CreateCryptoParams(int tag, const std::string& cipher,
83 CryptoParams *out) {
84 std::string key;
85 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
86
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000087 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088 return false;
89 }
90 out->tag = tag;
91 out->cipher_suite = cipher;
92 out->key_params = kInline;
93 out->key_params += key;
94 return true;
95}
96
97#ifdef HAVE_SRTP
98static bool AddCryptoParams(const std::string& cipher_suite,
99 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000100 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101
102 out->resize(size + 1);
103 return CreateCryptoParams(size, cipher_suite, &out->at(size));
104}
105
106void AddMediaCryptos(const CryptoParamsVec& cryptos,
107 MediaContentDescription* media) {
108 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
109 crypto != cryptos.end(); ++crypto) {
110 media->AddCrypto(*crypto);
111 }
112}
113
114bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
115 MediaContentDescription* media) {
116 CryptoParamsVec cryptos;
117 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
118 it != crypto_suites.end(); ++it) {
119 if (!AddCryptoParams(*it, &cryptos)) {
120 return false;
121 }
122 }
123 AddMediaCryptos(cryptos, media);
124 return true;
125}
126#endif
127
128const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
129 if (!media) {
130 return NULL;
131 }
132 return &media->cryptos();
133}
134
135bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
136 const CryptoParams& crypto,
137 CryptoParams* out) {
138 for (CryptoParamsVec::const_iterator it = cryptos.begin();
139 it != cryptos.end(); ++it) {
140 if (crypto.Matches(*it)) {
141 *out = *it;
142 return true;
143 }
144 }
145 return false;
146}
147
148// For audio, HMAC 32 is prefered because of the low overhead.
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800149void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800151 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
152 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153#endif
154}
155
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800156void GetSupportedAudioCryptoSuiteNames(
157 std::vector<std::string>* crypto_suite_names) {
158 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
159 crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000160}
161
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800162void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) {
163 GetDefaultSrtpCryptoSuites(crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164}
165
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800166void GetSupportedVideoCryptoSuiteNames(
167 std::vector<std::string>* crypto_suite_names) {
168 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
169 crypto_suite_names);
170}
171
172void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) {
173 GetDefaultSrtpCryptoSuites(crypto_suites);
174}
175
176void GetSupportedDataCryptoSuiteNames(
177 std::vector<std::string>* crypto_suite_names) {
178 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
179 crypto_suite_names);
180}
181
182void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800184 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185#endif
186}
187
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800188void GetDefaultSrtpCryptoSuiteNames(
189 std::vector<std::string>* crypto_suite_names) {
190 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names);
191}
192
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000193// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
194// tolerated unless bundle is enabled because it is low overhead. Pick the
195// crypto in the list that is supported.
196static bool SelectCrypto(const MediaContentDescription* offer,
197 bool bundle,
198 CryptoParams *crypto) {
199 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
200 const CryptoParamsVec& cryptos = offer->cryptos();
201
202 for (CryptoParamsVec::const_iterator i = cryptos.begin();
203 i != cryptos.end(); ++i) {
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700204 if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
205 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
206 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000207 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
208 }
209 }
210 return false;
211}
212
213static const StreamParams* FindFirstStreamParamsByCname(
214 const StreamParamsVec& params_vec,
215 const std::string& cname) {
216 for (StreamParamsVec::const_iterator it = params_vec.begin();
217 it != params_vec.end(); ++it) {
218 if (cname == it->cname)
219 return &*it;
220 }
221 return NULL;
222}
223
224// Generates a new CNAME or the CNAME of an already existing StreamParams
225// if a StreamParams exist for another Stream in streams with sync_label
226// sync_label.
227static bool GenerateCname(const StreamParamsVec& params_vec,
228 const MediaSessionOptions::Streams& streams,
229 const std::string& synch_label,
230 std::string* cname) {
231 ASSERT(cname != NULL);
232 if (!cname)
233 return false;
234
235 // Check if a CNAME exist for any of the other synched streams.
236 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
237 stream_it != streams.end() ; ++stream_it) {
238 if (synch_label != stream_it->sync_label)
239 continue;
240
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000241 // groupid is empty for StreamParams generated using
242 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000243 const StreamParams* param = GetStreamByIds(params_vec, "", stream_it->id);
244 if (param) {
245 *cname = param->cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246 return true;
247 }
248 }
249 // No other stream seems to exist that we should sync with.
250 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
251 // This string is only used for synchronization, and therefore is opaque.
252 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000253 if (!rtc::CreateRandomString(16, cname)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 ASSERT(false);
255 return false;
256 }
257 } while (FindFirstStreamParamsByCname(params_vec, *cname));
258
259 return true;
260}
261
262// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000263// The generated values are added to |ssrcs|.
264// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000266 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200267 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000268 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200269 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000271 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000272 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000273 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
274 ssrcs->push_back(candidate);
275 }
276}
277
278// Returns false if we exhaust the range of SIDs.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200279static bool GenerateSctpSid(const StreamParamsVec& params_vec, uint32_t* sid) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 if (params_vec.size() > kMaxSctpSid) {
281 LOG(LS_WARNING) <<
282 "Could not generate an SCTP SID: too many SCTP streams.";
283 return false;
284 }
285 while (true) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200286 uint32_t candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000287 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288 *sid = candidate;
289 return true;
290 }
291 }
292}
293
294static bool GenerateSctpSids(const StreamParamsVec& params_vec,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200295 std::vector<uint32_t>* sids) {
296 uint32_t sid;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 if (!GenerateSctpSid(params_vec, &sid)) {
298 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
299 return false;
300 }
301 sids->push_back(sid);
302 return true;
303}
304
305// Finds all StreamParams of all media types and attach them to stream_params.
306static void GetCurrentStreamParams(const SessionDescription* sdesc,
307 StreamParamsVec* stream_params) {
308 if (!sdesc)
309 return;
310
311 const ContentInfos& contents = sdesc->contents();
312 for (ContentInfos::const_iterator content = contents.begin();
313 content != contents.end(); ++content) {
314 if (!IsMediaContent(&*content)) {
315 continue;
316 }
317 const MediaContentDescription* media =
318 static_cast<const MediaContentDescription*>(
319 content->description);
320 const StreamParamsVec& streams = media->streams();
321 for (StreamParamsVec::const_iterator it = streams.begin();
322 it != streams.end(); ++it) {
323 stream_params->push_back(*it);
324 }
325 }
326}
327
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000328// Filters the data codecs for the data channel type.
329void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
330 // Filter RTP codec for SCTP and vice versa.
331 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
332 for (std::vector<DataCodec>::iterator iter = codecs->begin();
333 iter != codecs->end();) {
334 if (iter->id == codec_id) {
335 iter = codecs->erase(iter);
336 } else {
337 ++iter;
338 }
339 }
340}
341
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342template <typename IdStruct>
343class UsedIds {
344 public:
345 UsedIds(int min_allowed_id, int max_allowed_id)
346 : min_allowed_id_(min_allowed_id),
347 max_allowed_id_(max_allowed_id),
348 next_id_(max_allowed_id) {
349 }
350
351 // Loops through all Id in |ids| and changes its id if it is
352 // already in use by another IdStruct. Call this methods with all Id
353 // in a session description to make sure no duplicate ids exists.
354 // Note that typename Id must be a type of IdStruct.
355 template <typename Id>
356 void FindAndSetIdUsed(std::vector<Id>* ids) {
357 for (typename std::vector<Id>::iterator it = ids->begin();
358 it != ids->end(); ++it) {
359 FindAndSetIdUsed(&*it);
360 }
361 }
362
363 // Finds and sets an unused id if the |idstruct| id is already in use.
364 void FindAndSetIdUsed(IdStruct* idstruct) {
365 const int original_id = idstruct->id;
366 int new_id = idstruct->id;
367
368 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
369 // If the original id is not in range - this is an id that can't be
370 // dynamically changed.
371 return;
372 }
373
374 if (IsIdUsed(original_id)) {
375 new_id = FindUnusedId();
376 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
377 << " to " << new_id;
378 idstruct->id = new_id;
379 }
380 SetIdUsed(new_id);
381 }
382
383 private:
384 // Returns the first unused id in reverse order.
385 // This hopefully reduce the risk of more collisions. We want to change the
386 // default ids as little as possible.
387 int FindUnusedId() {
388 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
389 --next_id_;
390 }
391 ASSERT(next_id_ >= min_allowed_id_);
392 return next_id_;
393 }
394
395 bool IsIdUsed(int new_id) {
396 return id_set_.find(new_id) != id_set_.end();
397 }
398
399 void SetIdUsed(int new_id) {
400 id_set_.insert(new_id);
401 }
402
403 const int min_allowed_id_;
404 const int max_allowed_id_;
405 int next_id_;
406 std::set<int> id_set_;
407};
408
409// Helper class used for finding duplicate RTP payload types among audio, video
410// and data codecs. When bundle is used the payload types may not collide.
411class UsedPayloadTypes : public UsedIds<Codec> {
412 public:
413 UsedPayloadTypes()
414 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
415 }
416
417
418 private:
419 static const int kDynamicPayloadTypeMin = 96;
420 static const int kDynamicPayloadTypeMax = 127;
421};
422
423// Helper class used for finding duplicate RTP Header extension ids among
424// audio and video extensions.
425class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
426 public:
427 UsedRtpHeaderExtensionIds()
428 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
429 }
430
431 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000432 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000434 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435};
436
437static bool IsSctp(const MediaContentDescription* desc) {
438 return ((desc->protocol() == kMediaProtocolSctp) ||
439 (desc->protocol() == kMediaProtocolDtlsSctp));
440}
441
442// Adds a StreamParams for each Stream in Streams with media type
443// media_type to content_description.
444// |current_params| - All currently known StreamParams of any media type.
445template <class C>
446static bool AddStreamParams(
447 MediaType media_type,
448 const MediaSessionOptions::Streams& streams,
449 StreamParamsVec* current_streams,
450 MediaContentDescriptionImpl<C>* content_description,
451 const bool add_legacy_stream) {
Noah Richards2e7a0982015-05-18 14:02:54 -0700452 const bool include_rtx_streams =
453 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000454
455 if (streams.empty() && add_legacy_stream) {
456 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200457 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000458 if (IsSctp(content_description)) {
459 GenerateSctpSids(*current_streams, &ssrcs);
460 } else {
Noah Richards2e7a0982015-05-18 14:02:54 -0700461 int num_ssrcs = include_rtx_streams ? 2 : 1;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000462 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700464 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
466 content_description->set_multistream(true);
467 } else {
468 content_description->AddLegacyStream(ssrcs[0]);
469 }
470 return true;
471 }
472
473 MediaSessionOptions::Streams::const_iterator stream_it;
474 for (stream_it = streams.begin();
475 stream_it != streams.end(); ++stream_it) {
476 if (stream_it->type != media_type)
477 continue; // Wrong media type.
478
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000479 const StreamParams* param =
480 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000481 // groupid is empty for StreamParams generated using
482 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000483 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484 // This is a new stream.
485 // Get a CNAME. Either new or same as one of the other synched streams.
486 std::string cname;
487 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
488 &cname)) {
489 return false;
490 }
491
Peter Boström0c4e06b2015-10-07 12:23:21 +0200492 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 if (IsSctp(content_description)) {
494 GenerateSctpSids(*current_streams, &ssrcs);
495 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000496 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000497 }
498 StreamParams stream_param;
499 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000500 // Add the generated ssrc.
501 for (size_t i = 0; i < ssrcs.size(); ++i) {
502 stream_param.ssrcs.push_back(ssrcs[i]);
503 }
504 if (stream_it->num_sim_layers > 1) {
505 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
506 stream_param.ssrc_groups.push_back(group);
507 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700508 // Generate extra ssrcs for include_rtx_streams case.
509 if (include_rtx_streams) {
510 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200511 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700512 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
513 &rtx_ssrcs);
514 for (size_t i = 0; i < ssrcs.size(); ++i) {
515 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
516 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 content_description->set_multistream(true);
518 }
519 stream_param.cname = cname;
520 stream_param.sync_label = stream_it->sync_label;
521 content_description->AddStream(stream_param);
522
523 // Store the new StreamParams in current_streams.
524 // This is necessary so that we can use the CNAME for other media types.
525 current_streams->push_back(stream_param);
526 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000527 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000528 }
529 }
530 return true;
531}
532
533// Updates the transport infos of the |sdesc| according to the given
534// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800535// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
536// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
538 SessionDescription* sdesc) {
539 // The bundle should not be empty.
540 if (!sdesc || !bundle_group.FirstContentName()) {
541 return false;
542 }
543
544 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700545 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546 const TransportInfo* selected_transport_info =
547 sdesc->GetTransportInfoByName(selected_content_name);
548 if (!selected_transport_info) {
549 return false;
550 }
551
552 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700553 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000554 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700555 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000556 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800557 ConnectionRole selected_connection_role =
558 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000559 for (TransportInfos::iterator it =
560 sdesc->transport_infos().begin();
561 it != sdesc->transport_infos().end(); ++it) {
562 if (bundle_group.HasContentName(it->content_name) &&
563 it->content_name != selected_content_name) {
564 it->description.ice_ufrag = selected_ufrag;
565 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800566 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000567 }
568 }
569 return true;
570}
571
572// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
573// sets it to |cryptos|.
574static bool GetCryptosByName(const SessionDescription* sdesc,
575 const std::string& content_name,
576 CryptoParamsVec* cryptos) {
577 if (!sdesc || !cryptos) {
578 return false;
579 }
580
581 const ContentInfo* content = sdesc->GetContentByName(content_name);
582 if (!IsMediaContent(content) || !content->description) {
583 return false;
584 }
585
586 const MediaContentDescription* media_desc =
587 static_cast<const MediaContentDescription*>(content->description);
588 *cryptos = media_desc->cryptos();
589 return true;
590}
591
592// Predicate function used by the remove_if.
593// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
594static bool CryptoNotFound(const CryptoParams crypto,
595 const CryptoParamsVec* filter) {
596 if (filter == NULL) {
597 return true;
598 }
599 for (CryptoParamsVec::const_iterator it = filter->begin();
600 it != filter->end(); ++it) {
601 if (it->cipher_suite == crypto.cipher_suite) {
602 return false;
603 }
604 }
605 return true;
606}
607
608// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
609// which are not available in |filter|.
610static void PruneCryptos(const CryptoParamsVec& filter,
611 CryptoParamsVec* target_cryptos) {
612 if (!target_cryptos) {
613 return;
614 }
615 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
616 target_cryptos->end(),
617 bind2nd(ptr_fun(CryptoNotFound),
618 &filter)),
619 target_cryptos->end());
620}
621
deadbeefb5cb19b2015-11-23 16:39:12 -0800622static bool IsRtpProtocol(const std::string& protocol) {
623 return protocol.empty() ||
624 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
625}
626
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000627static bool IsRtpContent(SessionDescription* sdesc,
628 const std::string& content_name) {
629 bool is_rtp = false;
630 ContentInfo* content = sdesc->GetContentByName(content_name);
631 if (IsMediaContent(content)) {
632 MediaContentDescription* media_desc =
633 static_cast<MediaContentDescription*>(content->description);
634 if (!media_desc) {
635 return false;
636 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800637 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000638 }
639 return is_rtp;
640}
641
642// Updates the crypto parameters of the |sdesc| according to the given
643// |bundle_group|. The crypto parameters of all the contents within the
644// |bundle_group| should be updated to use the common subset of the
645// available cryptos.
646static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
647 SessionDescription* sdesc) {
648 // The bundle should not be empty.
649 if (!sdesc || !bundle_group.FirstContentName()) {
650 return false;
651 }
652
wu@webrtc.org78187522013-10-07 23:32:02 +0000653 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000654 // Get the common cryptos.
655 const ContentNames& content_names = bundle_group.content_names();
656 CryptoParamsVec common_cryptos;
657 for (ContentNames::const_iterator it = content_names.begin();
658 it != content_names.end(); ++it) {
659 if (!IsRtpContent(sdesc, *it)) {
660 continue;
661 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000662 // The common cryptos are needed if any of the content does not have DTLS
663 // enabled.
664 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
665 common_cryptos_needed = true;
666 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000667 if (it == content_names.begin()) {
668 // Initial the common_cryptos with the first content in the bundle group.
669 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
670 return false;
671 }
672 if (common_cryptos.empty()) {
673 // If there's no crypto params, we should just return.
674 return true;
675 }
676 } else {
677 CryptoParamsVec cryptos;
678 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
679 return false;
680 }
681 PruneCryptos(cryptos, &common_cryptos);
682 }
683 }
684
wu@webrtc.org78187522013-10-07 23:32:02 +0000685 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000686 return false;
687 }
688
689 // Update to use the common cryptos.
690 for (ContentNames::const_iterator it = content_names.begin();
691 it != content_names.end(); ++it) {
692 if (!IsRtpContent(sdesc, *it)) {
693 continue;
694 }
695 ContentInfo* content = sdesc->GetContentByName(*it);
696 if (IsMediaContent(content)) {
697 MediaContentDescription* media_desc =
698 static_cast<MediaContentDescription*>(content->description);
699 if (!media_desc) {
700 return false;
701 }
702 media_desc->set_cryptos(common_cryptos);
703 }
704 }
705 return true;
706}
707
708template <class C>
709static bool ContainsRtxCodec(const std::vector<C>& codecs) {
710 typename std::vector<C>::const_iterator it;
711 for (it = codecs.begin(); it != codecs.end(); ++it) {
712 if (IsRtxCodec(*it)) {
713 return true;
714 }
715 }
716 return false;
717}
718
719template <class C>
720static bool IsRtxCodec(const C& codec) {
721 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
722}
723
deadbeef0ed85b22016-02-23 17:24:52 -0800724static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
725 const std::string& content_name) {
726 auto it = options.transport_options.find(content_name);
727 if (it == options.transport_options.end()) {
728 return TransportOptions();
729 }
730 return it->second;
731}
732
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733// Create a media content to be offered in a session-initiate,
734// according to the given options.rtcp_mux, options.is_muc,
735// options.streams, codecs, secure_transport, crypto, and streams. If we don't
736// currently have crypto (in current_cryptos) and it is enabled (in
737// secure_policy), crypto is created (according to crypto_suites). If
738// add_legacy_stream is true, and current_streams is empty, a legacy
739// stream is created. The created content is added to the offer.
740template <class C>
741static bool CreateMediaContentOffer(
742 const MediaSessionOptions& options,
743 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000744 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745 const CryptoParamsVec* current_cryptos,
746 const std::vector<std::string>& crypto_suites,
747 const RtpHeaderExtensions& rtp_extensions,
748 bool add_legacy_stream,
749 StreamParamsVec* current_streams,
750 MediaContentDescriptionImpl<C>* offer) {
751 offer->AddCodecs(codecs);
752 offer->SortCodecs();
753
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000754 if (secure_policy == SEC_REQUIRED) {
755 offer->set_crypto_required(CT_SDES);
756 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757 offer->set_rtcp_mux(options.rtcp_mux_enabled);
deadbeef13871492015-12-09 12:37:51 -0800758 // TODO(deadbeef): Once we're sure this works correctly, enable it in
759 // CreateOffer.
760 // if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
761 // offer->set_rtcp_reduced_size(true);
762 // }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000763 offer->set_multistream(options.is_muc);
764 offer->set_rtp_header_extensions(rtp_extensions);
765
766 if (!AddStreamParams(
767 offer->type(), options.streams, current_streams,
768 offer, add_legacy_stream)) {
769 return false;
770 }
771
772#ifdef HAVE_SRTP
773 if (secure_policy != SEC_DISABLED) {
774 if (current_cryptos) {
775 AddMediaCryptos(*current_cryptos, offer);
776 }
777 if (offer->cryptos().empty()) {
778 if (!CreateMediaCryptos(crypto_suites, offer)) {
779 return false;
780 }
781 }
782 }
783#endif
784
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000785 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000786 return false;
787 }
788 return true;
789}
790
791template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000792static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
793 const std::string& codec1_id_str,
794 const std::vector<C>& codecs2,
795 const std::string& codec2_id_str) {
796 int codec1_id;
797 int codec2_id;
798 C codec1;
799 C codec2;
800 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
801 !rtc::FromString(codec2_id_str, &codec2_id) ||
802 !FindCodecById(codecs1, codec1_id, &codec1) ||
803 !FindCodecById(codecs2, codec2_id, &codec2)) {
804 return false;
805 }
806 return codec1.Matches(codec2);
807}
808
809template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000810static void NegotiateCodecs(const std::vector<C>& local_codecs,
811 const std::vector<C>& offered_codecs,
812 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800813 for (const C& ours : local_codecs) {
814 C theirs;
815 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
816 C negotiated = ours;
817 negotiated.IntersectFeedbackParams(theirs);
818 if (IsRtxCodec(negotiated)) {
819 std::string offered_apt_value;
820 theirs.GetParam(kCodecParamAssociatedPayloadType, &offered_apt_value);
821 // FindMatchingCodec shouldn't return something with no apt value.
822 RTC_DCHECK(!offered_apt_value.empty());
823 negotiated.SetParam(kCodecParamAssociatedPayloadType,
824 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800826 negotiated.id = theirs.id;
827 // RFC3264: Although the answerer MAY list the formats in their desired
828 // order of preference, it is RECOMMENDED that unless there is a
829 // specific reason, the answerer list formats in the same relative order
830 // they were present in the offer.
831 negotiated.preference = theirs.preference;
832 negotiated_codecs->push_back(negotiated);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000833 }
834 }
835}
836
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800837// Finds a codec in |codecs2| that matches |codec_to_match|, which is
838// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
839// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000840template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800841static bool FindMatchingCodec(const std::vector<C>& codecs1,
842 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000843 const C& codec_to_match,
844 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800845 for (const C& potential_match : codecs2) {
846 if (potential_match.Matches(codec_to_match)) {
847 if (IsRtxCodec(codec_to_match)) {
848 std::string apt_value_1;
849 std::string apt_value_2;
850 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
851 &apt_value_1) ||
852 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
853 &apt_value_2)) {
854 LOG(LS_WARNING) << "RTX missing associated payload type.";
855 continue;
856 }
857 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
858 apt_value_2)) {
859 continue;
860 }
861 }
862 if (found_codec) {
863 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000864 }
865 return true;
866 }
867 }
868 return false;
869}
870
871// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
872// already exist in |offered_codecs| and ensure the payload types don't
873// collide.
874template <class C>
875static void FindCodecsToOffer(
876 const std::vector<C>& reference_codecs,
877 std::vector<C>* offered_codecs,
878 UsedPayloadTypes* used_pltypes) {
879
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000880 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800881 for (const C& reference_codec : reference_codecs) {
882 if (!IsRtxCodec(reference_codec) &&
883 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
884 reference_codec, nullptr)) {
885 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000886 used_pltypes->FindAndSetIdUsed(&codec);
887 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000888 }
889 }
890
891 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800892 for (const C& reference_codec : reference_codecs) {
893 if (IsRtxCodec(reference_codec) &&
894 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
895 reference_codec, nullptr)) {
896 C rtx_codec = reference_codec;
897
898 std::string associated_pt_str;
899 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
900 &associated_pt_str)) {
901 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
902 << " is missing an associated payload type.";
903 continue;
904 }
905
906 int associated_pt;
907 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
908 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
909 << " of RTX codec " << rtx_codec.name
910 << " to an integer.";
911 continue;
912 }
913
914 // Find the associated reference codec for the reference RTX codec.
915 C associated_codec;
916 if (!FindCodecById(reference_codecs, associated_pt, &associated_codec)) {
917 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
918 << associated_pt << " for RTX codec " << rtx_codec.name
919 << ".";
920 continue;
921 }
922
923 // Find a codec in the offered list that matches the reference codec.
924 // Its payload type may be different than the reference codec.
925 C matching_codec;
926 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
927 associated_codec, &matching_codec)) {
928 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec.name
929 << " codec.";
930 continue;
931 }
932
933 rtx_codec.params[kCodecParamAssociatedPayloadType] =
934 rtc::ToString(matching_codec.id);
935 used_pltypes->FindAndSetIdUsed(&rtx_codec);
936 offered_codecs->push_back(rtx_codec);
937 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000938 }
939}
940
941
942static bool FindByUri(const RtpHeaderExtensions& extensions,
943 const RtpHeaderExtension& ext_to_match,
944 RtpHeaderExtension* found_extension) {
945 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
946 it != extensions.end(); ++it) {
947 // We assume that all URIs are given in a canonical format.
948 if (it->uri == ext_to_match.uri) {
949 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000950 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000951 }
952 return true;
953 }
954 }
955 return false;
956}
957
deadbeefa5b273a2015-08-20 17:30:13 -0700958// Iterates through |offered_extensions|, adding each one to |all_extensions|
959// and |used_ids|, and resolving ID conflicts. If an offered extension has the
960// same URI as one in |all_extensions|, it will re-use the same ID and won't be
961// treated as a conflict.
962static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
963 RtpHeaderExtensions* all_extensions,
964 UsedRtpHeaderExtensionIds* used_ids) {
965 for (auto& extension : *offered_extensions) {
966 RtpHeaderExtension existing;
967 if (FindByUri(*all_extensions, extension, &existing)) {
968 extension.id = existing.id;
969 } else {
970 used_ids->FindAndSetIdUsed(&extension);
971 all_extensions->push_back(extension);
972 }
973 }
974}
975
976// Adds |reference_extensions| to |offered_extensions|, while updating
977// |all_extensions| and |used_ids|.
978static void FindRtpHdrExtsToOffer(
979 const RtpHeaderExtensions& reference_extensions,
980 RtpHeaderExtensions* offered_extensions,
981 RtpHeaderExtensions* all_extensions,
982 UsedRtpHeaderExtensionIds* used_ids) {
983 for (auto reference_extension : reference_extensions) {
984 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
985 RtpHeaderExtension existing;
986 if (FindByUri(*all_extensions, reference_extension, &existing)) {
987 offered_extensions->push_back(existing);
988 } else {
989 used_ids->FindAndSetIdUsed(&reference_extension);
990 all_extensions->push_back(reference_extension);
991 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000992 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000993 }
994 }
995}
996
997static void NegotiateRtpHeaderExtensions(
998 const RtpHeaderExtensions& local_extensions,
999 const RtpHeaderExtensions& offered_extensions,
1000 RtpHeaderExtensions* negotiated_extenstions) {
1001 RtpHeaderExtensions::const_iterator ours;
1002 for (ours = local_extensions.begin();
1003 ours != local_extensions.end(); ++ours) {
1004 RtpHeaderExtension theirs;
1005 if (FindByUri(offered_extensions, *ours, &theirs)) {
1006 // We respond with their RTP header extension id.
1007 negotiated_extenstions->push_back(theirs);
1008 }
1009 }
1010}
1011
1012static void StripCNCodecs(AudioCodecs* audio_codecs) {
1013 AudioCodecs::iterator iter = audio_codecs->begin();
1014 while (iter != audio_codecs->end()) {
1015 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
1016 iter = audio_codecs->erase(iter);
1017 } else {
1018 ++iter;
1019 }
1020 }
1021}
1022
1023// Create a media content to be answered in a session-accept,
1024// according to the given options.rtcp_mux, options.streams, codecs,
1025// crypto, and streams. If we don't currently have crypto (in
1026// current_cryptos) and it is enabled (in secure_policy), crypto is
1027// created (according to crypto_suites). If add_legacy_stream is
1028// true, and current_streams is empty, a legacy stream is created.
1029// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1030// from the incoming session-initiate. If the negotiation fails, this
1031// method returns false. The created content is added to the offer.
1032template <class C>
1033static bool CreateMediaContentAnswer(
1034 const MediaContentDescriptionImpl<C>* offer,
1035 const MediaSessionOptions& options,
1036 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001037 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038 const CryptoParamsVec* current_cryptos,
1039 const RtpHeaderExtensions& local_rtp_extenstions,
1040 StreamParamsVec* current_streams,
1041 bool add_legacy_stream,
1042 bool bundle_enabled,
1043 MediaContentDescriptionImpl<C>* answer) {
1044 std::vector<C> negotiated_codecs;
1045 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1046 answer->AddCodecs(negotiated_codecs);
1047 answer->SortCodecs();
1048 answer->set_protocol(offer->protocol());
1049 RtpHeaderExtensions negotiated_rtp_extensions;
1050 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1051 offer->rtp_header_extensions(),
1052 &negotiated_rtp_extensions);
1053 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1054
1055 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
deadbeef13871492015-12-09 12:37:51 -08001056 // TODO(deadbeef): Once we're sure this works correctly, enable it in
1057 // CreateAnswer.
1058 // if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1059 // answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1060 // }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001061
1062 if (sdes_policy != SEC_DISABLED) {
1063 CryptoParams crypto;
1064 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
1065 if (current_cryptos) {
1066 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1067 }
1068 answer->AddCrypto(crypto);
1069 }
1070 }
1071
1072 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001073 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001074 return false;
1075 }
1076
1077 if (!AddStreamParams(
1078 answer->type(), options.streams, current_streams,
1079 answer, add_legacy_stream)) {
1080 return false; // Something went seriously wrong.
1081 }
1082
1083 // Make sure the answer media content direction is per default set as
1084 // described in RFC3264 section 6.1.
1085 switch (offer->direction()) {
1086 case MD_INACTIVE:
1087 answer->set_direction(MD_INACTIVE);
1088 break;
1089 case MD_SENDONLY:
1090 answer->set_direction(MD_RECVONLY);
1091 break;
1092 case MD_RECVONLY:
deadbeefb5cb19b2015-11-23 16:39:12 -08001093 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1094 answer->streams().empty()
1095 ? MD_INACTIVE
1096 : MD_SENDONLY);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001097 break;
1098 case MD_SENDRECV:
deadbeefb5cb19b2015-11-23 16:39:12 -08001099 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1100 answer->streams().empty()
1101 ? MD_RECVONLY
1102 : MD_SENDRECV);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001103 break;
1104 default:
deadbeefc80741f2015-10-22 13:14:45 -07001105 RTC_DCHECK(false && "MediaContentDescription has unexpected direction.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001106 break;
1107 }
1108
1109 return true;
1110}
1111
1112static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001113 const std::string& protocol,
1114 bool secure_transport) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001115 // Data channels can have a protocol of SCTP or SCTP/DTLS.
1116 if (type == MEDIA_TYPE_DATA &&
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001117 ((protocol == kMediaProtocolSctp && !secure_transport)||
1118 (protocol == kMediaProtocolDtlsSctp && secure_transport))) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001119 return true;
1120 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001121
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001122 // Since not all applications serialize and deserialize the media protocol,
1123 // we will have to accept |protocol| to be empty.
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001124 return protocol == kMediaProtocolAvpf || protocol.empty() ||
1125 protocol == kMediaProtocolSavpf ||
1126 (protocol == kMediaProtocolDtlsSavpf && secure_transport);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001127}
1128
1129static void SetMediaProtocol(bool secure_transport,
1130 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001131 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001133 else if (secure_transport)
1134 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001135 else
1136 desc->set_protocol(kMediaProtocolAvpf);
1137}
1138
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001139// Gets the TransportInfo of the given |content_name| from the
1140// |current_description|. If doesn't exist, returns a new one.
1141static const TransportDescription* GetTransportDescription(
1142 const std::string& content_name,
1143 const SessionDescription* current_description) {
1144 const TransportDescription* desc = NULL;
1145 if (current_description) {
1146 const TransportInfo* info =
1147 current_description->GetTransportInfoByName(content_name);
1148 if (info) {
1149 desc = &info->description;
1150 }
1151 }
1152 return desc;
1153}
1154
1155// Gets the current DTLS state from the transport description.
1156static bool IsDtlsActive(
1157 const std::string& content_name,
1158 const SessionDescription* current_description) {
1159 if (!current_description)
1160 return false;
1161
1162 const ContentInfo* content =
1163 current_description->GetContentByName(content_name);
1164 if (!content)
1165 return false;
1166
1167 const TransportDescription* current_tdesc =
1168 GetTransportDescription(content_name, current_description);
1169 if (!current_tdesc)
1170 return false;
1171
1172 return current_tdesc->secure();
1173}
1174
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001175std::string MediaTypeToString(MediaType type) {
1176 std::string type_str;
1177 switch (type) {
1178 case MEDIA_TYPE_AUDIO:
1179 type_str = "audio";
1180 break;
1181 case MEDIA_TYPE_VIDEO:
1182 type_str = "video";
1183 break;
1184 case MEDIA_TYPE_DATA:
1185 type_str = "data";
1186 break;
1187 default:
1188 ASSERT(false);
1189 break;
1190 }
1191 return type_str;
1192}
1193
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001194void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001195 const std::string& id,
1196 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001197 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001198}
1199
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001200void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001201 const std::string& id,
1202 const std::string& sync_label,
1203 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001204 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001205}
1206
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001207void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001208 MediaType type,
1209 const std::string& id,
1210 const std::string& sync_label,
1211 int num_sim_layers) {
1212 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001213
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001214 // If we haven't already set the data_channel_type, and we add a
1215 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001216 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 data_channel_type = DCT_RTP;
1218}
1219
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001220void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001221 const std::string& id) {
1222 Streams::iterator stream_it = streams.begin();
1223 for (; stream_it != streams.end(); ++stream_it) {
1224 if (stream_it->type == type && stream_it->id == id) {
1225 streams.erase(stream_it);
1226 return;
1227 }
1228 }
1229 ASSERT(false);
1230}
1231
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001232bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1233 Streams::const_iterator stream_it = streams.begin();
1234 for (; stream_it != streams.end(); ++stream_it) {
1235 if (stream_it->type == type) {
1236 return true;
1237 }
1238 }
1239 return false;
1240}
1241
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001242MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1243 const TransportDescriptionFactory* transport_desc_factory)
1244 : secure_(SEC_DISABLED),
1245 add_legacy_(true),
1246 transport_desc_factory_(transport_desc_factory) {
1247}
1248
1249MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1250 ChannelManager* channel_manager,
1251 const TransportDescriptionFactory* transport_desc_factory)
1252 : secure_(SEC_DISABLED),
1253 add_legacy_(true),
1254 transport_desc_factory_(transport_desc_factory) {
1255 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1256 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1257 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1258 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1259 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1260}
1261
1262SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1263 const MediaSessionOptions& options,
1264 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001265 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001266
1267 StreamParamsVec current_streams;
1268 GetCurrentStreamParams(current_description, &current_streams);
1269
1270 AudioCodecs audio_codecs;
1271 VideoCodecs video_codecs;
1272 DataCodecs data_codecs;
1273 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1274 &data_codecs);
1275
1276 if (!options.vad_enabled) {
1277 // If application doesn't want CN codecs in offer.
1278 StripCNCodecs(&audio_codecs);
1279 }
1280
1281 RtpHeaderExtensions audio_rtp_extensions;
1282 RtpHeaderExtensions video_rtp_extensions;
1283 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1284 &video_rtp_extensions);
1285
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001286 bool audio_added = false;
1287 bool video_added = false;
1288 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001289
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001290 // Iterate through the contents of |current_description| to maintain the order
1291 // of the m-lines in the new offer.
1292 if (current_description) {
1293 ContentInfos::const_iterator it = current_description->contents().begin();
1294 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001295 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001296 if (!AddAudioContentForOffer(options, current_description,
1297 audio_rtp_extensions, audio_codecs,
1298 &current_streams, offer.get())) {
1299 return NULL;
1300 }
1301 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001302 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001303 if (!AddVideoContentForOffer(options, current_description,
1304 video_rtp_extensions, video_codecs,
1305 &current_streams, offer.get())) {
1306 return NULL;
1307 }
1308 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001309 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001310 MediaSessionOptions options_copy(options);
1311 if (IsSctp(static_cast<const MediaContentDescription*>(
1312 it->description))) {
1313 options_copy.data_channel_type = DCT_SCTP;
1314 }
1315 if (!AddDataContentForOffer(options_copy, current_description,
1316 &data_codecs, &current_streams,
1317 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001318 return NULL;
1319 }
1320 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001321 } else {
1322 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001323 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001324 }
1325 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001326
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001327 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001328 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001329 !AddAudioContentForOffer(options, current_description,
1330 audio_rtp_extensions, audio_codecs,
1331 &current_streams, offer.get())) {
1332 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001333 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001334 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001335 !AddVideoContentForOffer(options, current_description,
1336 video_rtp_extensions, video_codecs,
1337 &current_streams, offer.get())) {
1338 return NULL;
1339 }
1340 if (!data_added && options.has_data() &&
1341 !AddDataContentForOffer(options, current_description, &data_codecs,
1342 &current_streams, offer.get())) {
1343 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001344 }
1345
1346 // Bundle the contents together, if we've been asked to do so, and update any
1347 // parameters that need to be tweaked for BUNDLE.
1348 if (options.bundle_enabled) {
1349 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1350 for (ContentInfos::const_iterator content = offer->contents().begin();
1351 content != offer->contents().end(); ++content) {
1352 offer_bundle.AddContentName(content->name);
1353 }
1354 offer->AddGroup(offer_bundle);
1355 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1356 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1357 return NULL;
1358 }
1359 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1360 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1361 return NULL;
1362 }
1363 }
1364
1365 return offer.release();
1366}
1367
1368SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1369 const SessionDescription* offer, const MediaSessionOptions& options,
1370 const SessionDescription* current_description) const {
1371 // The answer contains the intersection of the codecs in the offer with the
1372 // codecs we support, ordered by our local preference. As indicated by
1373 // XEP-0167, we retain the same payload ids from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001374 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001375
1376 StreamParamsVec current_streams;
1377 GetCurrentStreamParams(current_description, &current_streams);
1378
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001379 if (offer) {
1380 ContentInfos::const_iterator it = offer->contents().begin();
1381 for (; it != offer->contents().end(); ++it) {
1382 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1383 if (!AddAudioContentForAnswer(offer, options, current_description,
1384 &current_streams, answer.get())) {
1385 return NULL;
1386 }
1387 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1388 if (!AddVideoContentForAnswer(offer, options, current_description,
1389 &current_streams, answer.get())) {
1390 return NULL;
1391 }
1392 } else {
1393 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1394 if (!AddDataContentForAnswer(offer, options, current_description,
1395 &current_streams, answer.get())) {
1396 return NULL;
1397 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001399 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001400 }
1401
1402 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1403 // group in the answer with the appropriate content names.
1404 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1405 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1406 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1407 for (ContentInfos::const_iterator content = answer->contents().begin();
1408 content != answer->contents().end(); ++content) {
1409 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1410 answer_bundle.AddContentName(content->name);
1411 }
1412 }
1413 if (answer_bundle.FirstContentName()) {
1414 answer->AddGroup(answer_bundle);
1415
1416 // Share the same ICE credentials and crypto params across all contents,
1417 // as BUNDLE requires.
1418 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1419 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1420 return NULL;
1421 }
1422
1423 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1424 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1425 return NULL;
1426 }
1427 }
1428 }
1429
1430 return answer.release();
1431}
1432
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001433void MediaSessionDescriptionFactory::GetCodecsToOffer(
1434 const SessionDescription* current_description,
1435 AudioCodecs* audio_codecs,
1436 VideoCodecs* video_codecs,
1437 DataCodecs* data_codecs) const {
1438 UsedPayloadTypes used_pltypes;
1439 audio_codecs->clear();
1440 video_codecs->clear();
1441 data_codecs->clear();
1442
1443
1444 // First - get all codecs from the current description if the media type
1445 // is used.
1446 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1447 // type is added.
1448 if (current_description) {
1449 const AudioContentDescription* audio =
1450 GetFirstAudioContentDescription(current_description);
1451 if (audio) {
1452 *audio_codecs = audio->codecs();
1453 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1454 }
1455 const VideoContentDescription* video =
1456 GetFirstVideoContentDescription(current_description);
1457 if (video) {
1458 *video_codecs = video->codecs();
1459 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1460 }
1461 const DataContentDescription* data =
1462 GetFirstDataContentDescription(current_description);
1463 if (data) {
1464 *data_codecs = data->codecs();
1465 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1466 }
1467 }
1468
1469 // Add our codecs that are not in |current_description|.
1470 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1471 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1472 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1473}
1474
1475void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1476 const SessionDescription* current_description,
1477 RtpHeaderExtensions* audio_extensions,
1478 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001479 // All header extensions allocated from the same range to avoid potential
1480 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001481 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001482 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001483 audio_extensions->clear();
1484 video_extensions->clear();
1485
1486 // First - get all extensions from the current description if the media type
1487 // is used.
1488 // Add them to |used_ids| so the local ids are not reused if a new media
1489 // type is added.
1490 if (current_description) {
1491 const AudioContentDescription* audio =
1492 GetFirstAudioContentDescription(current_description);
1493 if (audio) {
1494 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001495 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001496 }
1497 const VideoContentDescription* video =
1498 GetFirstVideoContentDescription(current_description);
1499 if (video) {
1500 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001501 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001502 }
1503 }
1504
1505 // Add our default RTP header extensions that are not in
1506 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001507 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1508 &all_extensions, &used_ids);
1509 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1510 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001511}
1512
1513bool MediaSessionDescriptionFactory::AddTransportOffer(
1514 const std::string& content_name,
1515 const TransportOptions& transport_options,
1516 const SessionDescription* current_desc,
1517 SessionDescription* offer_desc) const {
1518 if (!transport_desc_factory_)
1519 return false;
1520 const TransportDescription* current_tdesc =
1521 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001522 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001523 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1524 bool ret = (new_tdesc.get() != NULL &&
1525 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1526 if (!ret) {
1527 LOG(LS_ERROR)
1528 << "Failed to AddTransportOffer, content name=" << content_name;
1529 }
1530 return ret;
1531}
1532
1533TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1534 const std::string& content_name,
1535 const SessionDescription* offer_desc,
1536 const TransportOptions& transport_options,
1537 const SessionDescription* current_desc) const {
1538 if (!transport_desc_factory_)
1539 return NULL;
1540 const TransportDescription* offer_tdesc =
1541 GetTransportDescription(content_name, offer_desc);
1542 const TransportDescription* current_tdesc =
1543 GetTransportDescription(content_name, current_desc);
1544 return
1545 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1546 current_tdesc);
1547}
1548
1549bool MediaSessionDescriptionFactory::AddTransportAnswer(
1550 const std::string& content_name,
1551 const TransportDescription& transport_desc,
1552 SessionDescription* answer_desc) const {
1553 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1554 transport_desc))) {
1555 LOG(LS_ERROR)
1556 << "Failed to AddTransportAnswer, content name=" << content_name;
1557 return false;
1558 }
1559 return true;
1560}
1561
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001562bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1563 const MediaSessionOptions& options,
1564 const SessionDescription* current_description,
1565 const RtpHeaderExtensions& audio_rtp_extensions,
1566 const AudioCodecs& audio_codecs,
1567 StreamParamsVec* current_streams,
1568 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001569 const ContentInfo* current_audio_content =
1570 GetFirstAudioContent(current_description);
1571 std::string content_name =
1572 current_audio_content ? current_audio_content->name : CN_AUDIO;
1573
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001574 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001575 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1576 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001577
kwiberg31022942016-03-11 14:18:21 -08001578 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001579 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001580 GetSupportedAudioCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001581 if (!CreateMediaContentOffer(
1582 options,
1583 audio_codecs,
1584 sdes_policy,
1585 GetCryptos(GetFirstAudioContentDescription(current_description)),
1586 crypto_suites,
1587 audio_rtp_extensions,
1588 add_legacy_,
1589 current_streams,
1590 audio.get())) {
1591 return false;
1592 }
1593 audio->set_lang(lang_);
1594
1595 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1596 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001597
deadbeefc80741f2015-10-22 13:14:45 -07001598 if (!audio->streams().empty()) {
1599 if (options.recv_audio) {
1600 audio->set_direction(MD_SENDRECV);
1601 } else {
1602 audio->set_direction(MD_SENDONLY);
1603 }
1604 } else {
1605 if (options.recv_audio) {
1606 audio->set_direction(MD_RECVONLY);
1607 } else {
1608 audio->set_direction(MD_INACTIVE);
1609 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001610 }
1611
deadbeef44f08192015-12-15 16:20:09 -08001612 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001613 if (!AddTransportOffer(content_name,
1614 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001615 current_description, desc)) {
1616 return false;
1617 }
1618
1619 return true;
1620}
1621
1622bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1623 const MediaSessionOptions& options,
1624 const SessionDescription* current_description,
1625 const RtpHeaderExtensions& video_rtp_extensions,
1626 const VideoCodecs& video_codecs,
1627 StreamParamsVec* current_streams,
1628 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001629 const ContentInfo* current_video_content =
1630 GetFirstVideoContent(current_description);
1631 std::string content_name =
1632 current_video_content ? current_video_content->name : CN_VIDEO;
1633
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001634 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001635 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1636 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001637
kwiberg31022942016-03-11 14:18:21 -08001638 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001639 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001640 GetSupportedVideoCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001641 if (!CreateMediaContentOffer(
1642 options,
1643 video_codecs,
1644 sdes_policy,
1645 GetCryptos(GetFirstVideoContentDescription(current_description)),
1646 crypto_suites,
1647 video_rtp_extensions,
1648 add_legacy_,
1649 current_streams,
1650 video.get())) {
1651 return false;
1652 }
1653
1654 video->set_bandwidth(options.video_bandwidth);
1655
1656 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1657 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001658
deadbeefc80741f2015-10-22 13:14:45 -07001659 if (!video->streams().empty()) {
1660 if (options.recv_video) {
1661 video->set_direction(MD_SENDRECV);
1662 } else {
1663 video->set_direction(MD_SENDONLY);
1664 }
1665 } else {
1666 if (options.recv_video) {
1667 video->set_direction(MD_RECVONLY);
1668 } else {
1669 video->set_direction(MD_INACTIVE);
1670 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001671 }
1672
deadbeef44f08192015-12-15 16:20:09 -08001673 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001674 if (!AddTransportOffer(content_name,
1675 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001676 current_description, desc)) {
1677 return false;
1678 }
1679
1680 return true;
1681}
1682
1683bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1684 const MediaSessionOptions& options,
1685 const SessionDescription* current_description,
1686 DataCodecs* data_codecs,
1687 StreamParamsVec* current_streams,
1688 SessionDescription* desc) const {
1689 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1690
kwiberg31022942016-03-11 14:18:21 -08001691 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001692 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1693
1694 FilterDataCodecs(data_codecs, is_sctp);
1695
deadbeef44f08192015-12-15 16:20:09 -08001696 const ContentInfo* current_data_content =
1697 GetFirstDataContent(current_description);
1698 std::string content_name =
1699 current_data_content ? current_data_content->name : CN_DATA;
1700
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001701 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001702 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1703 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001704 std::vector<std::string> crypto_suites;
1705 if (is_sctp) {
1706 // SDES doesn't make sense for SCTP, so we disable it, and we only
1707 // get SDES crypto suites for RTP-based data channels.
1708 sdes_policy = cricket::SEC_DISABLED;
1709 // Unlike SetMediaProtocol below, we need to set the protocol
1710 // before we call CreateMediaContentOffer. Otherwise,
1711 // CreateMediaContentOffer won't know this is SCTP and will
1712 // generate SSRCs rather than SIDs.
1713 data->set_protocol(
1714 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1715 } else {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001716 GetSupportedDataCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001717 }
1718
1719 if (!CreateMediaContentOffer(
1720 options,
1721 *data_codecs,
1722 sdes_policy,
1723 GetCryptos(GetFirstDataContentDescription(current_description)),
1724 crypto_suites,
1725 RtpHeaderExtensions(),
1726 add_legacy_,
1727 current_streams,
1728 data.get())) {
1729 return false;
1730 }
1731
1732 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001733 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001734 } else {
1735 data->set_bandwidth(options.data_bandwidth);
1736 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001737 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001738 }
deadbeef0ed85b22016-02-23 17:24:52 -08001739 if (!AddTransportOffer(content_name,
1740 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001741 current_description, desc)) {
1742 return false;
1743 }
1744 return true;
1745}
1746
1747bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1748 const SessionDescription* offer,
1749 const MediaSessionOptions& options,
1750 const SessionDescription* current_description,
1751 StreamParamsVec* current_streams,
1752 SessionDescription* answer) const {
1753 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1754
kwiberg31022942016-03-11 14:18:21 -08001755 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001756 audio_content->name, offer,
1757 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001758 if (!audio_transport) {
1759 return false;
1760 }
1761
1762 AudioCodecs audio_codecs = audio_codecs_;
1763 if (!options.vad_enabled) {
1764 StripCNCodecs(&audio_codecs);
1765 }
1766
1767 bool bundle_enabled =
1768 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08001769 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001770 new AudioContentDescription());
1771 // Do not require or create SDES cryptos if DTLS is used.
1772 cricket::SecurePolicy sdes_policy =
1773 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1774 if (!CreateMediaContentAnswer(
1775 static_cast<const AudioContentDescription*>(
1776 audio_content->description),
1777 options,
1778 audio_codecs,
1779 sdes_policy,
1780 GetCryptos(GetFirstAudioContentDescription(current_description)),
1781 audio_rtp_extensions_,
1782 current_streams,
1783 add_legacy_,
1784 bundle_enabled,
1785 audio_answer.get())) {
1786 return false; // Fails the session setup.
1787 }
1788
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001789 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001790 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1791 audio_answer->protocol(),
1792 audio_transport->secure());
1793 if (!rejected) {
1794 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1795 } else {
1796 // RFC 3264
1797 // The answer MUST contain the same number of m-lines as the offer.
1798 LOG(LS_INFO) << "Audio is not supported in the answer.";
1799 }
1800
1801 answer->AddContent(audio_content->name, audio_content->type, rejected,
1802 audio_answer.release());
1803 return true;
1804}
1805
1806bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1807 const SessionDescription* offer,
1808 const MediaSessionOptions& options,
1809 const SessionDescription* current_description,
1810 StreamParamsVec* current_streams,
1811 SessionDescription* answer) const {
1812 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001813 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001814 video_content->name, offer,
1815 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001816 if (!video_transport) {
1817 return false;
1818 }
1819
kwiberg31022942016-03-11 14:18:21 -08001820 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001821 new VideoContentDescription());
1822 // Do not require or create SDES cryptos if DTLS is used.
1823 cricket::SecurePolicy sdes_policy =
1824 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1825 bool bundle_enabled =
1826 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1827 if (!CreateMediaContentAnswer(
1828 static_cast<const VideoContentDescription*>(
1829 video_content->description),
1830 options,
1831 video_codecs_,
1832 sdes_policy,
1833 GetCryptos(GetFirstVideoContentDescription(current_description)),
1834 video_rtp_extensions_,
1835 current_streams,
1836 add_legacy_,
1837 bundle_enabled,
1838 video_answer.get())) {
1839 return false;
1840 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001841 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001842 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1843 video_answer->protocol(),
1844 video_transport->secure());
1845 if (!rejected) {
1846 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1847 answer)) {
1848 return false;
1849 }
1850 video_answer->set_bandwidth(options.video_bandwidth);
1851 } else {
1852 // RFC 3264
1853 // The answer MUST contain the same number of m-lines as the offer.
1854 LOG(LS_INFO) << "Video is not supported in the answer.";
1855 }
1856 answer->AddContent(video_content->name, video_content->type, rejected,
1857 video_answer.release());
1858 return true;
1859}
1860
1861bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1862 const SessionDescription* offer,
1863 const MediaSessionOptions& options,
1864 const SessionDescription* current_description,
1865 StreamParamsVec* current_streams,
1866 SessionDescription* answer) const {
1867 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001868 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001869 data_content->name, offer,
1870 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001871 if (!data_transport) {
1872 return false;
1873 }
1874 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1875 std::vector<DataCodec> data_codecs(data_codecs_);
1876 FilterDataCodecs(&data_codecs, is_sctp);
1877
kwiberg31022942016-03-11 14:18:21 -08001878 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001879 new DataContentDescription());
1880 // Do not require or create SDES cryptos if DTLS is used.
1881 cricket::SecurePolicy sdes_policy =
1882 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1883 bool bundle_enabled =
1884 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1885 if (!CreateMediaContentAnswer(
1886 static_cast<const DataContentDescription*>(
1887 data_content->description),
1888 options,
1889 data_codecs_,
1890 sdes_policy,
1891 GetCryptos(GetFirstDataContentDescription(current_description)),
1892 RtpHeaderExtensions(),
1893 current_streams,
1894 add_legacy_,
1895 bundle_enabled,
1896 data_answer.get())) {
1897 return false; // Fails the session setup.
1898 }
1899
1900 bool rejected = !options.has_data() || data_content->rejected ||
1901 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1902 data_answer->protocol(),
1903 data_transport->secure());
1904 if (!rejected) {
1905 data_answer->set_bandwidth(options.data_bandwidth);
1906 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1907 answer)) {
1908 return false;
1909 }
1910 } else {
1911 // RFC 3264
1912 // The answer MUST contain the same number of m-lines as the offer.
1913 LOG(LS_INFO) << "Data is not supported in the answer.";
1914 }
1915 answer->AddContent(data_content->name, data_content->type, rejected,
1916 data_answer.release());
1917 return true;
1918}
1919
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001920bool IsMediaContent(const ContentInfo* content) {
1921 return (content &&
1922 (content->type == NS_JINGLE_RTP ||
1923 content->type == NS_JINGLE_DRAFT_SCTP));
1924}
1925
1926bool IsAudioContent(const ContentInfo* content) {
1927 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1928}
1929
1930bool IsVideoContent(const ContentInfo* content) {
1931 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1932}
1933
1934bool IsDataContent(const ContentInfo* content) {
1935 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1936}
1937
deadbeef0ed85b22016-02-23 17:24:52 -08001938const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1939 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001940 for (ContentInfos::const_iterator content = contents.begin();
1941 content != contents.end(); content++) {
1942 if (IsMediaContentOfType(&*content, media_type)) {
1943 return &*content;
1944 }
1945 }
deadbeef0ed85b22016-02-23 17:24:52 -08001946 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001947}
1948
1949const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1950 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1951}
1952
1953const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1954 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1955}
1956
1957const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1958 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1959}
1960
1961static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1962 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08001963 if (sdesc == nullptr) {
1964 return nullptr;
1965 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001966
1967 return GetFirstMediaContent(sdesc->contents(), media_type);
1968}
1969
1970const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1971 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1972}
1973
1974const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1975 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1976}
1977
1978const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1979 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1980}
1981
1982const MediaContentDescription* GetFirstMediaContentDescription(
1983 const SessionDescription* sdesc, MediaType media_type) {
1984 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1985 const ContentDescription* description = content ? content->description : NULL;
1986 return static_cast<const MediaContentDescription*>(description);
1987}
1988
1989const AudioContentDescription* GetFirstAudioContentDescription(
1990 const SessionDescription* sdesc) {
1991 return static_cast<const AudioContentDescription*>(
1992 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1993}
1994
1995const VideoContentDescription* GetFirstVideoContentDescription(
1996 const SessionDescription* sdesc) {
1997 return static_cast<const VideoContentDescription*>(
1998 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1999}
2000
2001const DataContentDescription* GetFirstDataContentDescription(
2002 const SessionDescription* sdesc) {
2003 return static_cast<const DataContentDescription*>(
2004 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2005}
2006
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002007} // namespace cricket