blob: 000c289c186b9c2daa5f41b0730d96d55f2b2665 [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
deadbeef67cf2c12016-04-13 10:07:16 -070013#include <algorithm> // For std::find_if, std::sort.
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>
deadbeef67cf2c12016-04-13 10:07:16 -070018#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <utility>
20
jbauchcb560652016-08-04 05:20:32 -070021#include "webrtc/base/base64.h"
nissec80e7412017-01-11 05:56:46 -080022#include "webrtc/base/checks.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000023#include "webrtc/base/helpers.h"
24#include "webrtc/base/logging.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000025#include "webrtc/base/stringutils.h"
nisse21e4e0b2017-02-20 05:01:01 -080026#include "webrtc/common_types.h"
magjedf823ede2016-11-12 09:53:04 -080027#include "webrtc/common_video/h264/profile_level_id.h"
kjellandera96e2d72016-02-04 23:52:28 -080028#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080029#include "webrtc/media/base/mediaconstants.h"
30#include "webrtc/p2p/base/p2pconstants.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010031#include "webrtc/pc/channelmanager.h"
32#include "webrtc/pc/srtpfilter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000033
34namespace {
35const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080036
jbauchcb560652016-08-04 05:20:32 -070037void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
38 std::vector<int>*),
39 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080040 std::vector<std::string>* names) {
41#ifdef HAVE_SRTP
42 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070043 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080044 for (const auto crypto : crypto_suites) {
45 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
46 }
47#endif
48}
terelius8c011e52016-04-26 05:28:11 -070049} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000050
51namespace cricket {
52
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053// 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
ossu075af922016-06-14 03:29:38 -070071RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
72 MediaContentDirection md) {
73 const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
74 const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
75 return RtpTransceiverDirection(send, recv);
76}
77
78MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
79 if (send && recv) {
80 return MD_SENDRECV;
81 } else if (send) {
82 return MD_SENDONLY;
83 } else if (recv) {
84 return MD_RECVONLY;
85 }
86
87 return MD_INACTIVE;
88}
89
90RtpTransceiverDirection
91NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
92 RtpTransceiverDirection wants) {
93 return RtpTransceiverDirection(offer.recv && wants.send,
94 offer.send && wants.recv);
95}
96
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097static bool IsMediaContentOfType(const ContentInfo* content,
98 MediaType media_type) {
99 if (!IsMediaContent(content)) {
100 return false;
101 }
102
103 const MediaContentDescription* mdesc =
104 static_cast<const MediaContentDescription*>(content->description);
105 return mdesc && mdesc->type() == media_type;
106}
107
108static bool CreateCryptoParams(int tag, const std::string& cipher,
109 CryptoParams *out) {
jbauchcb560652016-08-04 05:20:32 -0700110 int key_len;
111 int salt_len;
112 if (!rtc::GetSrtpKeyAndSaltLengths(
113 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114 return false;
115 }
jbauchcb560652016-08-04 05:20:32 -0700116
117 int master_key_len = key_len + salt_len;
118 std::string master_key;
119 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
120 return false;
121 }
122
kwiberg352444f2016-11-28 15:58:53 -0800123 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700124 std::string key = rtc::Base64::Encode(master_key);
125
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126 out->tag = tag;
127 out->cipher_suite = cipher;
128 out->key_params = kInline;
129 out->key_params += key;
130 return true;
131}
132
133#ifdef HAVE_SRTP
134static bool AddCryptoParams(const std::string& cipher_suite,
135 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000136 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137
138 out->resize(size + 1);
139 return CreateCryptoParams(size, cipher_suite, &out->at(size));
140}
141
142void AddMediaCryptos(const CryptoParamsVec& cryptos,
143 MediaContentDescription* media) {
144 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
145 crypto != cryptos.end(); ++crypto) {
146 media->AddCrypto(*crypto);
147 }
148}
149
150bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
151 MediaContentDescription* media) {
152 CryptoParamsVec cryptos;
153 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
154 it != crypto_suites.end(); ++it) {
155 if (!AddCryptoParams(*it, &cryptos)) {
156 return false;
157 }
158 }
159 AddMediaCryptos(cryptos, media);
160 return true;
161}
162#endif
163
164const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
165 if (!media) {
166 return NULL;
167 }
168 return &media->cryptos();
169}
170
171bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
172 const CryptoParams& crypto,
173 CryptoParams* out) {
174 for (CryptoParamsVec::const_iterator it = cryptos.begin();
175 it != cryptos.end(); ++it) {
176 if (crypto.Matches(*it)) {
177 *out = *it;
178 return true;
179 }
180 }
181 return false;
182}
183
jbauchcb560652016-08-04 05:20:32 -0700184// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
185void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
186 std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000187#ifdef HAVE_SRTP
jbauchcb560652016-08-04 05:20:32 -0700188 if (crypto_options.enable_gcm_crypto_suites) {
189 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
190 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
191 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800192 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
193 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194#endif
195}
196
jbauchcb560652016-08-04 05:20:32 -0700197void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800198 std::vector<std::string>* crypto_suite_names) {
199 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700200 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201}
202
jbauchcb560652016-08-04 05:20:32 -0700203void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
204 std::vector<int>* crypto_suites) {
205 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206}
207
jbauchcb560652016-08-04 05:20:32 -0700208void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800209 std::vector<std::string>* crypto_suite_names) {
210 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700211 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800212}
213
jbauchcb560652016-08-04 05:20:32 -0700214void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
215 std::vector<int>* crypto_suites) {
216 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800217}
218
jbauchcb560652016-08-04 05:20:32 -0700219void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800220 std::vector<std::string>* crypto_suite_names) {
221 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700222 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800223}
224
jbauchcb560652016-08-04 05:20:32 -0700225void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
226 std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227#ifdef HAVE_SRTP
jbauchcb560652016-08-04 05:20:32 -0700228 if (crypto_options.enable_gcm_crypto_suites) {
229 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
230 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
231 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800232 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233#endif
234}
235
jbauchcb560652016-08-04 05:20:32 -0700236void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800237 std::vector<std::string>* crypto_suite_names) {
jbauchcb560652016-08-04 05:20:32 -0700238 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites,
239 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800240}
241
jbauchcb560652016-08-04 05:20:32 -0700242// Support any GCM cipher (if enabled through options). For video support only
243// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
244// because it is low overhead.
245// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246static bool SelectCrypto(const MediaContentDescription* offer,
247 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700248 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 CryptoParams *crypto) {
250 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
251 const CryptoParamsVec& cryptos = offer->cryptos();
252
253 for (CryptoParamsVec::const_iterator i = cryptos.begin();
254 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700255 if ((crypto_options.enable_gcm_crypto_suites &&
256 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
257 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700258 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
259 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
261 }
262 }
263 return false;
264}
265
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000267// The generated values are added to |ssrcs|.
268// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000270 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200271 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000272 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200273 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000275 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000276 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
278 ssrcs->push_back(candidate);
279 }
280}
281
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282// Finds all StreamParams of all media types and attach them to stream_params.
283static void GetCurrentStreamParams(const SessionDescription* sdesc,
284 StreamParamsVec* stream_params) {
285 if (!sdesc)
286 return;
287
288 const ContentInfos& contents = sdesc->contents();
289 for (ContentInfos::const_iterator content = contents.begin();
290 content != contents.end(); ++content) {
291 if (!IsMediaContent(&*content)) {
292 continue;
293 }
294 const MediaContentDescription* media =
295 static_cast<const MediaContentDescription*>(
296 content->description);
297 const StreamParamsVec& streams = media->streams();
298 for (StreamParamsVec::const_iterator it = streams.begin();
299 it != streams.end(); ++it) {
300 stream_params->push_back(*it);
301 }
302 }
303}
304
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000305// Filters the data codecs for the data channel type.
306void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
307 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700308 const char* codec_name =
309 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000310 for (std::vector<DataCodec>::iterator iter = codecs->begin();
311 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700312 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000313 iter = codecs->erase(iter);
314 } else {
315 ++iter;
316 }
317 }
318}
319
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000320template <typename IdStruct>
321class UsedIds {
322 public:
323 UsedIds(int min_allowed_id, int max_allowed_id)
324 : min_allowed_id_(min_allowed_id),
325 max_allowed_id_(max_allowed_id),
326 next_id_(max_allowed_id) {
327 }
328
329 // Loops through all Id in |ids| and changes its id if it is
330 // already in use by another IdStruct. Call this methods with all Id
331 // in a session description to make sure no duplicate ids exists.
332 // Note that typename Id must be a type of IdStruct.
333 template <typename Id>
334 void FindAndSetIdUsed(std::vector<Id>* ids) {
335 for (typename std::vector<Id>::iterator it = ids->begin();
336 it != ids->end(); ++it) {
337 FindAndSetIdUsed(&*it);
338 }
339 }
340
341 // Finds and sets an unused id if the |idstruct| id is already in use.
342 void FindAndSetIdUsed(IdStruct* idstruct) {
343 const int original_id = idstruct->id;
344 int new_id = idstruct->id;
345
346 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
347 // If the original id is not in range - this is an id that can't be
348 // dynamically changed.
349 return;
350 }
351
352 if (IsIdUsed(original_id)) {
353 new_id = FindUnusedId();
354 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
355 << " to " << new_id;
356 idstruct->id = new_id;
357 }
358 SetIdUsed(new_id);
359 }
360
361 private:
362 // Returns the first unused id in reverse order.
363 // This hopefully reduce the risk of more collisions. We want to change the
364 // default ids as little as possible.
365 int FindUnusedId() {
366 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
367 --next_id_;
368 }
nisseede5da42017-01-12 05:15:36 -0800369 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000370 return next_id_;
371 }
372
373 bool IsIdUsed(int new_id) {
374 return id_set_.find(new_id) != id_set_.end();
375 }
376
377 void SetIdUsed(int new_id) {
378 id_set_.insert(new_id);
379 }
380
381 const int min_allowed_id_;
382 const int max_allowed_id_;
383 int next_id_;
384 std::set<int> id_set_;
385};
386
387// Helper class used for finding duplicate RTP payload types among audio, video
388// and data codecs. When bundle is used the payload types may not collide.
389class UsedPayloadTypes : public UsedIds<Codec> {
390 public:
391 UsedPayloadTypes()
392 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
393 }
394
395
396 private:
397 static const int kDynamicPayloadTypeMin = 96;
398 static const int kDynamicPayloadTypeMax = 127;
399};
400
401// Helper class used for finding duplicate RTP Header extension ids among
402// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700403class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404 public:
405 UsedRtpHeaderExtensionIds()
isheriff6f8d6862016-05-26 11:24:55 -0700406 : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000407
408 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000409 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000411 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000412};
413
414static bool IsSctp(const MediaContentDescription* desc) {
415 return ((desc->protocol() == kMediaProtocolSctp) ||
416 (desc->protocol() == kMediaProtocolDtlsSctp));
417}
418
419// Adds a StreamParams for each Stream in Streams with media type
420// media_type to content_description.
421// |current_params| - All currently known StreamParams of any media type.
422template <class C>
zhihuang8f65cdf2016-05-06 18:40:30 -0700423static bool AddStreamParams(MediaType media_type,
424 const MediaSessionOptions& options,
425 StreamParamsVec* current_streams,
426 MediaContentDescriptionImpl<C>* content_description,
427 const bool add_legacy_stream) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700428 // SCTP streams are not negotiated using SDP/ContentDescriptions.
429 if (IsSctp(content_description)) {
430 return true;
431 }
432
Noah Richards2e7a0982015-05-18 14:02:54 -0700433 const bool include_rtx_streams =
434 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435
zhihuang8f65cdf2016-05-06 18:40:30 -0700436 const MediaSessionOptions::Streams& streams = options.streams;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 if (streams.empty() && add_legacy_stream) {
438 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200439 std::vector<uint32_t> ssrcs;
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700440 int num_ssrcs = include_rtx_streams ? 2 : 1;
441 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
Noah Richards2e7a0982015-05-18 14:02:54 -0700442 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000443 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
444 content_description->set_multistream(true);
445 } else {
446 content_description->AddLegacyStream(ssrcs[0]);
447 }
448 return true;
449 }
450
brandtr03d5fb12016-11-22 03:37:59 -0800451 const bool include_flexfec_stream =
452 ContainsFlexfecCodec(content_description->codecs());
453
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000454 MediaSessionOptions::Streams::const_iterator stream_it;
455 for (stream_it = streams.begin();
456 stream_it != streams.end(); ++stream_it) {
457 if (stream_it->type != media_type)
458 continue; // Wrong media type.
459
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000460 const StreamParams* param =
461 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000462 // groupid is empty for StreamParams generated using
463 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000464 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 // This is a new stream.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200466 std::vector<uint32_t> ssrcs;
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700467 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000468 StreamParams stream_param;
469 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000470 // Add the generated ssrc.
471 for (size_t i = 0; i < ssrcs.size(); ++i) {
472 stream_param.ssrcs.push_back(ssrcs[i]);
473 }
474 if (stream_it->num_sim_layers > 1) {
475 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
476 stream_param.ssrc_groups.push_back(group);
477 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700478 // Generate extra ssrcs for include_rtx_streams case.
479 if (include_rtx_streams) {
480 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200481 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700482 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
483 &rtx_ssrcs);
484 for (size_t i = 0; i < ssrcs.size(); ++i) {
485 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
486 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000487 content_description->set_multistream(true);
488 }
brandtr03d5fb12016-11-22 03:37:59 -0800489 // Generate extra ssrc for include_flexfec_stream case.
490 if (include_flexfec_stream) {
491 // TODO(brandtr): Update when we support multistream protection.
492 if (ssrcs.size() == 1) {
493 std::vector<uint32_t> flexfec_ssrcs;
494 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
495 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
496 content_description->set_multistream(true);
497 } else if (!ssrcs.empty()) {
498 LOG(LS_WARNING)
499 << "Our FlexFEC implementation only supports protecting "
500 << "a single media streams. This session has multiple "
501 << "media streams however, so no FlexFEC SSRC will be generated.";
502 }
503 }
zhihuang8f65cdf2016-05-06 18:40:30 -0700504 stream_param.cname = options.rtcp_cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000505 stream_param.sync_label = stream_it->sync_label;
506 content_description->AddStream(stream_param);
507
508 // Store the new StreamParams in current_streams.
509 // This is necessary so that we can use the CNAME for other media types.
510 current_streams->push_back(stream_param);
511 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000512 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000513 }
514 }
515 return true;
516}
517
518// Updates the transport infos of the |sdesc| according to the given
519// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800520// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
521// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
523 SessionDescription* sdesc) {
524 // The bundle should not be empty.
525 if (!sdesc || !bundle_group.FirstContentName()) {
526 return false;
527 }
528
529 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700530 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 const TransportInfo* selected_transport_info =
532 sdesc->GetTransportInfoByName(selected_content_name);
533 if (!selected_transport_info) {
534 return false;
535 }
536
537 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700538 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000539 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700540 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000541 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800542 ConnectionRole selected_connection_role =
543 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000544 for (TransportInfos::iterator it =
545 sdesc->transport_infos().begin();
546 it != sdesc->transport_infos().end(); ++it) {
547 if (bundle_group.HasContentName(it->content_name) &&
548 it->content_name != selected_content_name) {
549 it->description.ice_ufrag = selected_ufrag;
550 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800551 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000552 }
553 }
554 return true;
555}
556
557// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
558// sets it to |cryptos|.
559static bool GetCryptosByName(const SessionDescription* sdesc,
560 const std::string& content_name,
561 CryptoParamsVec* cryptos) {
562 if (!sdesc || !cryptos) {
563 return false;
564 }
565
566 const ContentInfo* content = sdesc->GetContentByName(content_name);
567 if (!IsMediaContent(content) || !content->description) {
568 return false;
569 }
570
571 const MediaContentDescription* media_desc =
572 static_cast<const MediaContentDescription*>(content->description);
573 *cryptos = media_desc->cryptos();
574 return true;
575}
576
577// Predicate function used by the remove_if.
578// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
579static bool CryptoNotFound(const CryptoParams crypto,
580 const CryptoParamsVec* filter) {
581 if (filter == NULL) {
582 return true;
583 }
584 for (CryptoParamsVec::const_iterator it = filter->begin();
585 it != filter->end(); ++it) {
586 if (it->cipher_suite == crypto.cipher_suite) {
587 return false;
588 }
589 }
590 return true;
591}
592
593// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
594// which are not available in |filter|.
595static void PruneCryptos(const CryptoParamsVec& filter,
596 CryptoParamsVec* target_cryptos) {
597 if (!target_cryptos) {
598 return;
599 }
600 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
601 target_cryptos->end(),
602 bind2nd(ptr_fun(CryptoNotFound),
603 &filter)),
604 target_cryptos->end());
605}
606
deadbeefb5cb19b2015-11-23 16:39:12 -0800607static bool IsRtpProtocol(const std::string& protocol) {
608 return protocol.empty() ||
609 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
610}
611
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612static bool IsRtpContent(SessionDescription* sdesc,
613 const std::string& content_name) {
614 bool is_rtp = false;
615 ContentInfo* content = sdesc->GetContentByName(content_name);
616 if (IsMediaContent(content)) {
617 MediaContentDescription* media_desc =
618 static_cast<MediaContentDescription*>(content->description);
619 if (!media_desc) {
620 return false;
621 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800622 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000623 }
624 return is_rtp;
625}
626
627// Updates the crypto parameters of the |sdesc| according to the given
628// |bundle_group|. The crypto parameters of all the contents within the
629// |bundle_group| should be updated to use the common subset of the
630// available cryptos.
631static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
632 SessionDescription* sdesc) {
633 // The bundle should not be empty.
634 if (!sdesc || !bundle_group.FirstContentName()) {
635 return false;
636 }
637
wu@webrtc.org78187522013-10-07 23:32:02 +0000638 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 // Get the common cryptos.
640 const ContentNames& content_names = bundle_group.content_names();
641 CryptoParamsVec common_cryptos;
642 for (ContentNames::const_iterator it = content_names.begin();
643 it != content_names.end(); ++it) {
644 if (!IsRtpContent(sdesc, *it)) {
645 continue;
646 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000647 // The common cryptos are needed if any of the content does not have DTLS
648 // enabled.
649 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
650 common_cryptos_needed = true;
651 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000652 if (it == content_names.begin()) {
653 // Initial the common_cryptos with the first content in the bundle group.
654 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
655 return false;
656 }
657 if (common_cryptos.empty()) {
658 // If there's no crypto params, we should just return.
659 return true;
660 }
661 } else {
662 CryptoParamsVec cryptos;
663 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
664 return false;
665 }
666 PruneCryptos(cryptos, &common_cryptos);
667 }
668 }
669
wu@webrtc.org78187522013-10-07 23:32:02 +0000670 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 return false;
672 }
673
674 // Update to use the common cryptos.
675 for (ContentNames::const_iterator it = content_names.begin();
676 it != content_names.end(); ++it) {
677 if (!IsRtpContent(sdesc, *it)) {
678 continue;
679 }
680 ContentInfo* content = sdesc->GetContentByName(*it);
681 if (IsMediaContent(content)) {
682 MediaContentDescription* media_desc =
683 static_cast<MediaContentDescription*>(content->description);
684 if (!media_desc) {
685 return false;
686 }
687 media_desc->set_cryptos(common_cryptos);
688 }
689 }
690 return true;
691}
692
693template <class C>
694static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800695 for (const auto& codec : codecs) {
696 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000697 return true;
698 }
699 }
700 return false;
701}
702
703template <class C>
704static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800705 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000706}
707
brandtr03d5fb12016-11-22 03:37:59 -0800708template <class C>
709static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
710 for (const auto& codec : codecs) {
711 if (IsFlexfecCodec(codec)) {
712 return true;
713 }
714 }
715 return false;
716}
717
718template <class C>
719static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800720 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800721}
722
deadbeef0ed85b22016-02-23 17:24:52 -0800723static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
724 const std::string& content_name) {
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700725 TransportOptions transport_options;
deadbeef0ed85b22016-02-23 17:24:52 -0800726 auto it = options.transport_options.find(content_name);
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700727 if (it != options.transport_options.end()) {
728 transport_options = it->second;
deadbeef0ed85b22016-02-23 17:24:52 -0800729 }
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700730 transport_options.enable_ice_renomination = options.enable_ice_renomination;
731 return transport_options;
deadbeef0ed85b22016-02-23 17:24:52 -0800732}
733
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734// Create a media content to be offered in a session-initiate,
735// according to the given options.rtcp_mux, options.is_muc,
736// options.streams, codecs, secure_transport, crypto, and streams. If we don't
737// currently have crypto (in current_cryptos) and it is enabled (in
738// secure_policy), crypto is created (according to crypto_suites). If
739// add_legacy_stream is true, and current_streams is empty, a legacy
740// stream is created. The created content is added to the offer.
741template <class C>
742static bool CreateMediaContentOffer(
743 const MediaSessionOptions& options,
744 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000745 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000746 const CryptoParamsVec* current_cryptos,
747 const std::vector<std::string>& crypto_suites,
748 const RtpHeaderExtensions& rtp_extensions,
749 bool add_legacy_stream,
750 StreamParamsVec* current_streams,
751 MediaContentDescriptionImpl<C>* offer) {
752 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000753
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754 offer->set_rtcp_mux(options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700755 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
756 offer->set_rtcp_reduced_size(true);
757 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000758 offer->set_multistream(options.is_muc);
759 offer->set_rtp_header_extensions(rtp_extensions);
760
zhihuang8f65cdf2016-05-06 18:40:30 -0700761 if (!AddStreamParams(offer->type(), options, current_streams, offer,
762 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000763 return false;
764 }
765
766#ifdef HAVE_SRTP
767 if (secure_policy != SEC_DISABLED) {
768 if (current_cryptos) {
769 AddMediaCryptos(*current_cryptos, offer);
770 }
771 if (offer->cryptos().empty()) {
772 if (!CreateMediaCryptos(crypto_suites, offer)) {
773 return false;
774 }
775 }
776 }
777#endif
778
deadbeef7af91dd2016-12-13 11:29:11 -0800779 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780 return false;
781 }
782 return true;
783}
784
785template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000786static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800787 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000788 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800789 const int codec2_id) {
790 const C* codec1 = FindCodecById(codecs1, codec1_id);
791 const C* codec2 = FindCodecById(codecs2, codec2_id);
792 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000793}
794
795template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000796static void NegotiateCodecs(const std::vector<C>& local_codecs,
797 const std::vector<C>& offered_codecs,
798 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800799 for (const C& ours : local_codecs) {
800 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700801 // Note that we intentionally only find one matching codec for each of our
802 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800803 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
804 C negotiated = ours;
805 negotiated.IntersectFeedbackParams(theirs);
806 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800807 const auto apt_it =
808 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800809 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800810 RTC_DCHECK(apt_it != theirs.params.end());
811 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000812 }
magjedf823ede2016-11-12 09:53:04 -0800813 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
814 webrtc::H264::GenerateProfileLevelIdForAnswer(
815 ours.params, theirs.params, &negotiated.params);
816 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800817 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700818 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800819 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000820 }
821 }
deadbeef67cf2c12016-04-13 10:07:16 -0700822 // RFC3264: Although the answerer MAY list the formats in their desired
823 // order of preference, it is RECOMMENDED that unless there is a
824 // specific reason, the answerer list formats in the same relative order
825 // they were present in the offer.
826 std::unordered_map<int, int> payload_type_preferences;
827 int preference = static_cast<int>(offered_codecs.size() + 1);
828 for (const C& codec : offered_codecs) {
829 payload_type_preferences[codec.id] = preference--;
830 }
831 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
832 [&payload_type_preferences](const C& a, const C& b) {
833 return payload_type_preferences[a.id] >
834 payload_type_preferences[b.id];
835 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000836}
837
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800838// Finds a codec in |codecs2| that matches |codec_to_match|, which is
839// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
840// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000841template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800842static bool FindMatchingCodec(const std::vector<C>& codecs1,
843 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000844 const C& codec_to_match,
845 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800846 for (const C& potential_match : codecs2) {
847 if (potential_match.Matches(codec_to_match)) {
848 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800849 int apt_value_1 = 0;
850 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800851 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
852 &apt_value_1) ||
853 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
854 &apt_value_2)) {
855 LOG(LS_WARNING) << "RTX missing associated payload type.";
856 continue;
857 }
858 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
859 apt_value_2)) {
860 continue;
861 }
862 }
863 if (found_codec) {
864 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000865 }
866 return true;
867 }
868 }
869 return false;
870}
871
872// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
873// already exist in |offered_codecs| and ensure the payload types don't
874// collide.
875template <class C>
876static void FindCodecsToOffer(
877 const std::vector<C>& reference_codecs,
878 std::vector<C>* offered_codecs,
879 UsedPayloadTypes* used_pltypes) {
880
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000881 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800882 for (const C& reference_codec : reference_codecs) {
883 if (!IsRtxCodec(reference_codec) &&
884 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
885 reference_codec, nullptr)) {
886 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000887 used_pltypes->FindAndSetIdUsed(&codec);
888 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 }
890 }
891
892 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800893 for (const C& reference_codec : reference_codecs) {
894 if (IsRtxCodec(reference_codec) &&
895 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
896 reference_codec, nullptr)) {
897 C rtx_codec = reference_codec;
898
899 std::string associated_pt_str;
900 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
901 &associated_pt_str)) {
902 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
903 << " is missing an associated payload type.";
904 continue;
905 }
906
907 int associated_pt;
908 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
909 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
910 << " of RTX codec " << rtx_codec.name
911 << " to an integer.";
912 continue;
913 }
914
915 // Find the associated reference codec for the reference RTX codec.
magjedb05fa242016-11-11 04:00:16 -0800916 const C* associated_codec =
917 FindCodecById(reference_codecs, associated_pt);
918 if (!associated_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800919 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
920 << associated_pt << " for RTX codec " << rtx_codec.name
921 << ".";
922 continue;
923 }
924
925 // Find a codec in the offered list that matches the reference codec.
926 // Its payload type may be different than the reference codec.
927 C matching_codec;
928 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800929 *associated_codec, &matching_codec)) {
930 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800931 << " codec.";
932 continue;
933 }
934
935 rtx_codec.params[kCodecParamAssociatedPayloadType] =
936 rtc::ToString(matching_codec.id);
937 used_pltypes->FindAndSetIdUsed(&rtx_codec);
938 offered_codecs->push_back(rtx_codec);
939 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000940 }
941}
942
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000943static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700944 const webrtc::RtpExtension& ext_to_match,
945 webrtc::RtpExtension* found_extension) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000946 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
947 it != extensions.end(); ++it) {
948 // We assume that all URIs are given in a canonical format.
949 if (it->uri == ext_to_match.uri) {
950 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000951 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000952 }
953 return true;
954 }
955 }
956 return false;
957}
958
deadbeefa5b273a2015-08-20 17:30:13 -0700959// Iterates through |offered_extensions|, adding each one to |all_extensions|
960// and |used_ids|, and resolving ID conflicts. If an offered extension has the
961// same URI as one in |all_extensions|, it will re-use the same ID and won't be
962// treated as a conflict.
963static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
964 RtpHeaderExtensions* all_extensions,
965 UsedRtpHeaderExtensionIds* used_ids) {
966 for (auto& extension : *offered_extensions) {
isheriff6f8d6862016-05-26 11:24:55 -0700967 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700968 if (FindByUri(*all_extensions, extension, &existing)) {
969 extension.id = existing.id;
970 } else {
971 used_ids->FindAndSetIdUsed(&extension);
972 all_extensions->push_back(extension);
973 }
974 }
975}
976
977// Adds |reference_extensions| to |offered_extensions|, while updating
978// |all_extensions| and |used_ids|.
979static void FindRtpHdrExtsToOffer(
980 const RtpHeaderExtensions& reference_extensions,
981 RtpHeaderExtensions* offered_extensions,
982 RtpHeaderExtensions* all_extensions,
983 UsedRtpHeaderExtensionIds* used_ids) {
984 for (auto reference_extension : reference_extensions) {
985 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
isheriff6f8d6862016-05-26 11:24:55 -0700986 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700987 if (FindByUri(*all_extensions, reference_extension, &existing)) {
988 offered_extensions->push_back(existing);
989 } else {
990 used_ids->FindAndSetIdUsed(&reference_extension);
991 all_extensions->push_back(reference_extension);
992 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000993 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000994 }
995 }
996}
997
998static void NegotiateRtpHeaderExtensions(
999 const RtpHeaderExtensions& local_extensions,
1000 const RtpHeaderExtensions& offered_extensions,
1001 RtpHeaderExtensions* negotiated_extenstions) {
1002 RtpHeaderExtensions::const_iterator ours;
1003 for (ours = local_extensions.begin();
1004 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001005 webrtc::RtpExtension theirs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001006 if (FindByUri(offered_extensions, *ours, &theirs)) {
1007 // We respond with their RTP header extension id.
1008 negotiated_extenstions->push_back(theirs);
1009 }
1010 }
1011}
1012
1013static void StripCNCodecs(AudioCodecs* audio_codecs) {
1014 AudioCodecs::iterator iter = audio_codecs->begin();
1015 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001016 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001017 iter = audio_codecs->erase(iter);
1018 } else {
1019 ++iter;
1020 }
1021 }
1022}
1023
1024// Create a media content to be answered in a session-accept,
1025// according to the given options.rtcp_mux, options.streams, codecs,
1026// crypto, and streams. If we don't currently have crypto (in
1027// current_cryptos) and it is enabled (in secure_policy), crypto is
1028// created (according to crypto_suites). If add_legacy_stream is
1029// true, and current_streams is empty, a legacy stream is created.
1030// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1031// from the incoming session-initiate. If the negotiation fails, this
1032// method returns false. The created content is added to the offer.
1033template <class C>
1034static bool CreateMediaContentAnswer(
1035 const MediaContentDescriptionImpl<C>* offer,
1036 const MediaSessionOptions& options,
1037 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001038 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039 const CryptoParamsVec* current_cryptos,
1040 const RtpHeaderExtensions& local_rtp_extenstions,
1041 StreamParamsVec* current_streams,
1042 bool add_legacy_stream,
1043 bool bundle_enabled,
1044 MediaContentDescriptionImpl<C>* answer) {
1045 std::vector<C> negotiated_codecs;
1046 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1047 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048 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());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001056 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1057 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1058 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001059
1060 if (sdes_policy != SEC_DISABLED) {
1061 CryptoParams crypto;
jbauchcb560652016-08-04 05:20:32 -07001062 if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001063 if (current_cryptos) {
1064 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1065 }
1066 answer->AddCrypto(crypto);
1067 }
1068 }
1069
deadbeef7af91dd2016-12-13 11:29:11 -08001070 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001071 return false;
1072 }
1073
zhihuang8f65cdf2016-05-06 18:40:30 -07001074 if (!AddStreamParams(answer->type(), options, current_streams, answer,
1075 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001076 return false; // Something went seriously wrong.
1077 }
1078
1079 // Make sure the answer media content direction is per default set as
1080 // described in RFC3264 section 6.1.
ossu075af922016-06-14 03:29:38 -07001081 const bool is_data = !IsRtpProtocol(answer->protocol());
1082 const bool has_send_streams = !answer->streams().empty();
1083 const bool wants_send = has_send_streams || is_data;
1084 const bool recv_audio =
1085 answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
1086 const bool recv_video =
1087 answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
1088 const bool recv_data =
1089 answer->type() == cricket::MEDIA_TYPE_DATA;
1090 const bool wants_receive = recv_audio || recv_video || recv_data;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001091
ossu075af922016-06-14 03:29:38 -07001092 auto offer_rtd =
1093 RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
1094 auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
1095 answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
1096 .ToMediaContentDirection());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001097 return true;
1098}
1099
zhihuangcf5b37c2016-05-05 11:44:35 -07001100static bool IsDtlsRtp(const std::string& protocol) {
1101 // Most-likely values first.
1102 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1103 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1104}
1105
1106static bool IsPlainRtp(const std::string& protocol) {
1107 // Most-likely values first.
1108 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1109 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1110}
1111
1112static bool IsDtlsSctp(const std::string& protocol) {
1113 return protocol == "DTLS/SCTP";
1114}
1115
1116static bool IsPlainSctp(const std::string& protocol) {
1117 return protocol == "SCTP";
1118}
1119
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001120static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001121 const std::string& protocol,
1122 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001123 // Since not all applications serialize and deserialize the media protocol,
1124 // we will have to accept |protocol| to be empty.
1125 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001126 return true;
1127 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001128
zhihuangcf5b37c2016-05-05 11:44:35 -07001129 if (type == MEDIA_TYPE_DATA) {
1130 // Check for SCTP, but also for RTP for RTP-based data channels.
1131 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1132 if (secure_transport) {
1133 // Most likely scenarios first.
1134 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1135 IsPlainRtp(protocol);
1136 } else {
1137 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1138 }
1139 }
1140
1141 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1142 // JSEP specifies.
1143 if (secure_transport) {
1144 // Most likely scenarios first.
1145 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1146 } else {
1147 return IsPlainRtp(protocol);
1148 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001149}
1150
1151static void SetMediaProtocol(bool secure_transport,
1152 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001153 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001154 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001155 else if (secure_transport)
1156 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001157 else
1158 desc->set_protocol(kMediaProtocolAvpf);
1159}
1160
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001161// Gets the TransportInfo of the given |content_name| from the
1162// |current_description|. If doesn't exist, returns a new one.
1163static const TransportDescription* GetTransportDescription(
1164 const std::string& content_name,
1165 const SessionDescription* current_description) {
1166 const TransportDescription* desc = NULL;
1167 if (current_description) {
1168 const TransportInfo* info =
1169 current_description->GetTransportInfoByName(content_name);
1170 if (info) {
1171 desc = &info->description;
1172 }
1173 }
1174 return desc;
1175}
1176
1177// Gets the current DTLS state from the transport description.
1178static bool IsDtlsActive(
1179 const std::string& content_name,
1180 const SessionDescription* current_description) {
1181 if (!current_description)
1182 return false;
1183
1184 const ContentInfo* content =
1185 current_description->GetContentByName(content_name);
1186 if (!content)
1187 return false;
1188
1189 const TransportDescription* current_tdesc =
1190 GetTransportDescription(content_name, current_description);
1191 if (!current_tdesc)
1192 return false;
1193
1194 return current_tdesc->secure();
1195}
1196
ossu075af922016-06-14 03:29:38 -07001197std::string MediaContentDirectionToString(MediaContentDirection direction) {
1198 std::string dir_str;
1199 switch (direction) {
1200 case MD_INACTIVE:
1201 dir_str = "inactive";
1202 break;
1203 case MD_SENDONLY:
1204 dir_str = "sendonly";
1205 break;
1206 case MD_RECVONLY:
1207 dir_str = "recvonly";
1208 break;
1209 case MD_SENDRECV:
1210 dir_str = "sendrecv";
1211 break;
1212 default:
nissec80e7412017-01-11 05:56:46 -08001213 RTC_NOTREACHED();
ossu075af922016-06-14 03:29:38 -07001214 break;
1215 }
1216
1217 return dir_str;
1218}
1219
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001220void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001221 const std::string& id,
1222 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001223 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001224}
1225
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001226void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001227 const std::string& id,
1228 const std::string& sync_label,
1229 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001230 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001231}
1232
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001233void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001234 MediaType type,
1235 const std::string& id,
1236 const std::string& sync_label,
1237 int num_sim_layers) {
1238 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001239
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240 // If we haven't already set the data_channel_type, and we add a
1241 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001242 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001243 data_channel_type = DCT_RTP;
1244}
1245
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001246void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001247 const std::string& id) {
1248 Streams::iterator stream_it = streams.begin();
1249 for (; stream_it != streams.end(); ++stream_it) {
1250 if (stream_it->type == type && stream_it->id == id) {
1251 streams.erase(stream_it);
1252 return;
1253 }
1254 }
nissec80e7412017-01-11 05:56:46 -08001255 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001256}
1257
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001258bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1259 Streams::const_iterator stream_it = streams.begin();
1260 for (; stream_it != streams.end(); ++stream_it) {
1261 if (stream_it->type == type) {
1262 return true;
1263 }
1264 }
1265 return false;
1266}
1267
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001268MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1269 const TransportDescriptionFactory* transport_desc_factory)
1270 : secure_(SEC_DISABLED),
1271 add_legacy_(true),
1272 transport_desc_factory_(transport_desc_factory) {
1273}
1274
1275MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1276 ChannelManager* channel_manager,
1277 const TransportDescriptionFactory* transport_desc_factory)
1278 : secure_(SEC_DISABLED),
1279 add_legacy_(true),
1280 transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001281 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1282 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1283 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001284 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001285 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001286 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1287 channel_manager->GetSupportedDataCodecs(&data_codecs_);
ossudedfd282016-06-14 07:12:39 -07001288 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
1289 &audio_sendrecv_codecs_);
ossu075af922016-06-14 03:29:38 -07001290}
1291
ossudedfd282016-06-14 07:12:39 -07001292const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1293 const {
ossu075af922016-06-14 03:29:38 -07001294 return audio_sendrecv_codecs_;
1295}
1296
1297const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1298 return audio_send_codecs_;
1299}
1300
1301const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1302 return audio_recv_codecs_;
1303}
1304
1305void MediaSessionDescriptionFactory::set_audio_codecs(
1306 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1307 audio_send_codecs_ = send_codecs;
1308 audio_recv_codecs_ = recv_codecs;
1309 audio_sendrecv_codecs_.clear();
1310 // Use NegotiateCodecs to merge our codec lists, since the operation is
1311 // essentially the same. Put send_codecs as the offered_codecs, which is the
1312 // order we'd like to follow. The reasoning is that encoding is usually more
1313 // expensive than decoding, and prioritizing a codec in the send list probably
1314 // means it's a codec we can handle efficiently.
1315 NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001316}
1317
1318SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1319 const MediaSessionOptions& options,
1320 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001321 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001322
1323 StreamParamsVec current_streams;
1324 GetCurrentStreamParams(current_description, &current_streams);
1325
ossu075af922016-06-14 03:29:38 -07001326 const bool wants_send =
1327 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1328 const AudioCodecs& supported_audio_codecs =
1329 GetAudioCodecsForOffer({wants_send, options.recv_audio});
1330
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001331 AudioCodecs audio_codecs;
1332 VideoCodecs video_codecs;
1333 DataCodecs data_codecs;
ossu075af922016-06-14 03:29:38 -07001334 GetCodecsToOffer(current_description, supported_audio_codecs,
1335 video_codecs_, data_codecs_,
1336 &audio_codecs, &video_codecs, &data_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001337
1338 if (!options.vad_enabled) {
1339 // If application doesn't want CN codecs in offer.
1340 StripCNCodecs(&audio_codecs);
1341 }
1342
1343 RtpHeaderExtensions audio_rtp_extensions;
1344 RtpHeaderExtensions video_rtp_extensions;
1345 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1346 &video_rtp_extensions);
1347
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001348 bool audio_added = false;
1349 bool video_added = false;
1350 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001351
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001352 // Iterate through the contents of |current_description| to maintain the order
1353 // of the m-lines in the new offer.
1354 if (current_description) {
1355 ContentInfos::const_iterator it = current_description->contents().begin();
1356 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001357 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001358 if (!AddAudioContentForOffer(options, current_description,
1359 audio_rtp_extensions, audio_codecs,
1360 &current_streams, offer.get())) {
1361 return NULL;
1362 }
1363 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001364 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001365 if (!AddVideoContentForOffer(options, current_description,
1366 video_rtp_extensions, video_codecs,
1367 &current_streams, offer.get())) {
1368 return NULL;
1369 }
1370 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001371 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001372 MediaSessionOptions options_copy(options);
1373 if (IsSctp(static_cast<const MediaContentDescription*>(
1374 it->description))) {
1375 options_copy.data_channel_type = DCT_SCTP;
1376 }
1377 if (!AddDataContentForOffer(options_copy, current_description,
1378 &data_codecs, &current_streams,
1379 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001380 return NULL;
1381 }
1382 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001383 } else {
nissec80e7412017-01-11 05:56:46 -08001384 RTC_NOTREACHED();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001385 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001386 }
1387 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001388
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001389 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001390 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001391 !AddAudioContentForOffer(options, current_description,
1392 audio_rtp_extensions, audio_codecs,
1393 &current_streams, offer.get())) {
1394 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001396 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001397 !AddVideoContentForOffer(options, current_description,
1398 video_rtp_extensions, video_codecs,
1399 &current_streams, offer.get())) {
1400 return NULL;
1401 }
1402 if (!data_added && options.has_data() &&
1403 !AddDataContentForOffer(options, current_description, &data_codecs,
1404 &current_streams, offer.get())) {
1405 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001406 }
1407
1408 // Bundle the contents together, if we've been asked to do so, and update any
1409 // parameters that need to be tweaked for BUNDLE.
1410 if (options.bundle_enabled) {
1411 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1412 for (ContentInfos::const_iterator content = offer->contents().begin();
1413 content != offer->contents().end(); ++content) {
1414 offer_bundle.AddContentName(content->name);
1415 }
1416 offer->AddGroup(offer_bundle);
1417 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1418 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1419 return NULL;
1420 }
1421 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1422 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1423 return NULL;
1424 }
1425 }
1426
1427 return offer.release();
1428}
1429
1430SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1431 const SessionDescription* offer, const MediaSessionOptions& options,
1432 const SessionDescription* current_description) const {
1433 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001434 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1435 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001436 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001437
1438 StreamParamsVec current_streams;
1439 GetCurrentStreamParams(current_description, &current_streams);
1440
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001441 if (offer) {
1442 ContentInfos::const_iterator it = offer->contents().begin();
1443 for (; it != offer->contents().end(); ++it) {
1444 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1445 if (!AddAudioContentForAnswer(offer, options, current_description,
1446 &current_streams, answer.get())) {
1447 return NULL;
1448 }
1449 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1450 if (!AddVideoContentForAnswer(offer, options, current_description,
1451 &current_streams, answer.get())) {
1452 return NULL;
1453 }
1454 } else {
nisseede5da42017-01-12 05:15:36 -08001455 RTC_DCHECK(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001456 if (!AddDataContentForAnswer(offer, options, current_description,
1457 &current_streams, answer.get())) {
1458 return NULL;
1459 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001460 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001461 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001462 }
1463
1464 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1465 // group in the answer with the appropriate content names.
1466 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1467 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1468 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1469 for (ContentInfos::const_iterator content = answer->contents().begin();
1470 content != answer->contents().end(); ++content) {
1471 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1472 answer_bundle.AddContentName(content->name);
1473 }
1474 }
1475 if (answer_bundle.FirstContentName()) {
1476 answer->AddGroup(answer_bundle);
1477
1478 // Share the same ICE credentials and crypto params across all contents,
1479 // as BUNDLE requires.
1480 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1481 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1482 return NULL;
1483 }
1484
1485 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1486 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1487 return NULL;
1488 }
1489 }
1490 }
1491
1492 return answer.release();
1493}
1494
ossu075af922016-06-14 03:29:38 -07001495const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1496 const RtpTransceiverDirection& direction) const {
1497 // If stream is inactive - generate list as if sendrecv.
1498 if (direction.send == direction.recv) {
1499 return audio_sendrecv_codecs_;
1500 } else if (direction.send) {
1501 return audio_send_codecs_;
1502 } else {
1503 return audio_recv_codecs_;
1504 }
1505}
1506
1507const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1508 const RtpTransceiverDirection& offer,
1509 const RtpTransceiverDirection& answer) const {
1510 // For inactive and sendrecv answers, generate lists as if we were to accept
1511 // the offer's direction. See RFC 3264 Section 6.1.
1512 if (answer.send == answer.recv) {
1513 if (offer.send == offer.recv) {
1514 return audio_sendrecv_codecs_;
1515 } else if (offer.send) {
1516 return audio_recv_codecs_;
1517 } else {
1518 return audio_send_codecs_;
1519 }
1520 } else if (answer.send) {
1521 return audio_send_codecs_;
1522 } else {
1523 return audio_recv_codecs_;
1524 }
1525}
1526
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001527void MediaSessionDescriptionFactory::GetCodecsToOffer(
1528 const SessionDescription* current_description,
ossu075af922016-06-14 03:29:38 -07001529 const AudioCodecs& supported_audio_codecs,
1530 const VideoCodecs& supported_video_codecs,
1531 const DataCodecs& supported_data_codecs,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001532 AudioCodecs* audio_codecs,
1533 VideoCodecs* video_codecs,
1534 DataCodecs* data_codecs) const {
1535 UsedPayloadTypes used_pltypes;
1536 audio_codecs->clear();
1537 video_codecs->clear();
1538 data_codecs->clear();
1539
1540
1541 // First - get all codecs from the current description if the media type
1542 // is used.
1543 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1544 // type is added.
1545 if (current_description) {
1546 const AudioContentDescription* audio =
1547 GetFirstAudioContentDescription(current_description);
1548 if (audio) {
1549 *audio_codecs = audio->codecs();
1550 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1551 }
1552 const VideoContentDescription* video =
1553 GetFirstVideoContentDescription(current_description);
1554 if (video) {
1555 *video_codecs = video->codecs();
1556 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1557 }
1558 const DataContentDescription* data =
1559 GetFirstDataContentDescription(current_description);
1560 if (data) {
1561 *data_codecs = data->codecs();
1562 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1563 }
1564 }
1565
1566 // Add our codecs that are not in |current_description|.
ossu075af922016-06-14 03:29:38 -07001567 FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
1568 &used_pltypes);
1569 FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
1570 &used_pltypes);
1571 FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
1572 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001573}
1574
1575void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1576 const SessionDescription* current_description,
1577 RtpHeaderExtensions* audio_extensions,
1578 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001579 // All header extensions allocated from the same range to avoid potential
1580 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001581 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001582 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001583 audio_extensions->clear();
1584 video_extensions->clear();
1585
1586 // First - get all extensions from the current description if the media type
1587 // is used.
1588 // Add them to |used_ids| so the local ids are not reused if a new media
1589 // type is added.
1590 if (current_description) {
1591 const AudioContentDescription* audio =
1592 GetFirstAudioContentDescription(current_description);
1593 if (audio) {
1594 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001595 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001596 }
1597 const VideoContentDescription* video =
1598 GetFirstVideoContentDescription(current_description);
1599 if (video) {
1600 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001601 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001602 }
1603 }
1604
1605 // Add our default RTP header extensions that are not in
1606 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001607 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1608 &all_extensions, &used_ids);
1609 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1610 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001611}
1612
1613bool MediaSessionDescriptionFactory::AddTransportOffer(
1614 const std::string& content_name,
1615 const TransportOptions& transport_options,
1616 const SessionDescription* current_desc,
1617 SessionDescription* offer_desc) const {
1618 if (!transport_desc_factory_)
1619 return false;
1620 const TransportDescription* current_tdesc =
1621 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001622 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001623 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1624 bool ret = (new_tdesc.get() != NULL &&
1625 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1626 if (!ret) {
1627 LOG(LS_ERROR)
1628 << "Failed to AddTransportOffer, content name=" << content_name;
1629 }
1630 return ret;
1631}
1632
1633TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1634 const std::string& content_name,
1635 const SessionDescription* offer_desc,
1636 const TransportOptions& transport_options,
1637 const SessionDescription* current_desc) const {
1638 if (!transport_desc_factory_)
1639 return NULL;
1640 const TransportDescription* offer_tdesc =
1641 GetTransportDescription(content_name, offer_desc);
1642 const TransportDescription* current_tdesc =
1643 GetTransportDescription(content_name, current_desc);
1644 return
1645 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1646 current_tdesc);
1647}
1648
1649bool MediaSessionDescriptionFactory::AddTransportAnswer(
1650 const std::string& content_name,
1651 const TransportDescription& transport_desc,
1652 SessionDescription* answer_desc) const {
1653 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1654 transport_desc))) {
1655 LOG(LS_ERROR)
1656 << "Failed to AddTransportAnswer, content name=" << content_name;
1657 return false;
1658 }
1659 return true;
1660}
1661
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001662bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1663 const MediaSessionOptions& options,
1664 const SessionDescription* current_description,
1665 const RtpHeaderExtensions& audio_rtp_extensions,
1666 const AudioCodecs& audio_codecs,
1667 StreamParamsVec* current_streams,
1668 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001669 const ContentInfo* current_audio_content =
1670 GetFirstAudioContent(current_description);
1671 std::string content_name =
1672 current_audio_content ? current_audio_content->name : CN_AUDIO;
1673
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001674 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001675 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1676 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001677
kwiberg31022942016-03-11 14:18:21 -08001678 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001679 std::vector<std::string> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -07001680 GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001681 if (!CreateMediaContentOffer(
1682 options,
1683 audio_codecs,
1684 sdes_policy,
1685 GetCryptos(GetFirstAudioContentDescription(current_description)),
1686 crypto_suites,
1687 audio_rtp_extensions,
1688 add_legacy_,
1689 current_streams,
1690 audio.get())) {
1691 return false;
1692 }
1693 audio->set_lang(lang_);
1694
1695 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1696 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001697
ossu075af922016-06-14 03:29:38 -07001698 auto offer_rtd =
1699 RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
1700 audio->set_direction(offer_rtd.ToMediaContentDirection());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001701
deadbeef44f08192015-12-15 16:20:09 -08001702 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001703 if (!AddTransportOffer(content_name,
1704 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001705 current_description, desc)) {
1706 return false;
1707 }
1708
1709 return true;
1710}
1711
1712bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1713 const MediaSessionOptions& options,
1714 const SessionDescription* current_description,
1715 const RtpHeaderExtensions& video_rtp_extensions,
1716 const VideoCodecs& video_codecs,
1717 StreamParamsVec* current_streams,
1718 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001719 const ContentInfo* current_video_content =
1720 GetFirstVideoContent(current_description);
1721 std::string content_name =
1722 current_video_content ? current_video_content->name : CN_VIDEO;
1723
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001724 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001725 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1726 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001727
kwiberg31022942016-03-11 14:18:21 -08001728 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001729 std::vector<std::string> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -07001730 GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001731 if (!CreateMediaContentOffer(
1732 options,
1733 video_codecs,
1734 sdes_policy,
1735 GetCryptos(GetFirstVideoContentDescription(current_description)),
1736 crypto_suites,
1737 video_rtp_extensions,
1738 add_legacy_,
1739 current_streams,
1740 video.get())) {
1741 return false;
1742 }
1743
1744 video->set_bandwidth(options.video_bandwidth);
1745
1746 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1747 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001748
deadbeefc80741f2015-10-22 13:14:45 -07001749 if (!video->streams().empty()) {
1750 if (options.recv_video) {
1751 video->set_direction(MD_SENDRECV);
1752 } else {
1753 video->set_direction(MD_SENDONLY);
1754 }
1755 } else {
1756 if (options.recv_video) {
1757 video->set_direction(MD_RECVONLY);
1758 } else {
1759 video->set_direction(MD_INACTIVE);
1760 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001761 }
1762
deadbeef44f08192015-12-15 16:20:09 -08001763 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001764 if (!AddTransportOffer(content_name,
1765 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001766 current_description, desc)) {
1767 return false;
1768 }
1769
1770 return true;
1771}
1772
1773bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1774 const MediaSessionOptions& options,
1775 const SessionDescription* current_description,
1776 DataCodecs* data_codecs,
1777 StreamParamsVec* current_streams,
1778 SessionDescription* desc) const {
1779 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1780
kwiberg31022942016-03-11 14:18:21 -08001781 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001782 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1783
1784 FilterDataCodecs(data_codecs, is_sctp);
1785
deadbeef44f08192015-12-15 16:20:09 -08001786 const ContentInfo* current_data_content =
1787 GetFirstDataContent(current_description);
1788 std::string content_name =
1789 current_data_content ? current_data_content->name : CN_DATA;
1790
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001791 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001792 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1793 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001794 std::vector<std::string> crypto_suites;
1795 if (is_sctp) {
1796 // SDES doesn't make sense for SCTP, so we disable it, and we only
1797 // get SDES crypto suites for RTP-based data channels.
1798 sdes_policy = cricket::SEC_DISABLED;
1799 // Unlike SetMediaProtocol below, we need to set the protocol
1800 // before we call CreateMediaContentOffer. Otherwise,
1801 // CreateMediaContentOffer won't know this is SCTP and will
1802 // generate SSRCs rather than SIDs.
1803 data->set_protocol(
1804 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1805 } else {
jbauchcb560652016-08-04 05:20:32 -07001806 GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001807 }
1808
1809 if (!CreateMediaContentOffer(
1810 options,
1811 *data_codecs,
1812 sdes_policy,
1813 GetCryptos(GetFirstDataContentDescription(current_description)),
1814 crypto_suites,
1815 RtpHeaderExtensions(),
1816 add_legacy_,
1817 current_streams,
1818 data.get())) {
1819 return false;
1820 }
1821
1822 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001823 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001824 } else {
1825 data->set_bandwidth(options.data_bandwidth);
1826 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001827 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001828 }
deadbeef0ed85b22016-02-23 17:24:52 -08001829 if (!AddTransportOffer(content_name,
1830 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001831 current_description, desc)) {
1832 return false;
1833 }
1834 return true;
1835}
1836
1837bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1838 const SessionDescription* offer,
1839 const MediaSessionOptions& options,
1840 const SessionDescription* current_description,
1841 StreamParamsVec* current_streams,
1842 SessionDescription* answer) const {
1843 const ContentInfo* audio_content = GetFirstAudioContent(offer);
ossu075af922016-06-14 03:29:38 -07001844 const AudioContentDescription* offer_audio =
1845 static_cast<const AudioContentDescription*>(audio_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001846
kwiberg31022942016-03-11 14:18:21 -08001847 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001848 audio_content->name, offer,
1849 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001850 if (!audio_transport) {
1851 return false;
1852 }
1853
ossu075af922016-06-14 03:29:38 -07001854 // Pick codecs based on the requested communications direction in the offer.
1855 const bool wants_send =
1856 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1857 auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
1858 auto offer_rtd =
1859 RtpTransceiverDirection::FromMediaContentDirection(
1860 offer_audio->direction());
1861 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
1862 AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001863 if (!options.vad_enabled) {
1864 StripCNCodecs(&audio_codecs);
1865 }
1866
1867 bool bundle_enabled =
1868 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08001869 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001870 new AudioContentDescription());
1871 // Do not require or create SDES cryptos if DTLS is used.
1872 cricket::SecurePolicy sdes_policy =
1873 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1874 if (!CreateMediaContentAnswer(
ossu075af922016-06-14 03:29:38 -07001875 offer_audio,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001876 options,
1877 audio_codecs,
1878 sdes_policy,
1879 GetCryptos(GetFirstAudioContentDescription(current_description)),
1880 audio_rtp_extensions_,
1881 current_streams,
1882 add_legacy_,
1883 bundle_enabled,
1884 audio_answer.get())) {
1885 return false; // Fails the session setup.
1886 }
1887
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001888 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001889 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1890 audio_answer->protocol(),
1891 audio_transport->secure());
1892 if (!rejected) {
1893 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1894 } else {
1895 // RFC 3264
1896 // The answer MUST contain the same number of m-lines as the offer.
1897 LOG(LS_INFO) << "Audio is not supported in the answer.";
1898 }
1899
1900 answer->AddContent(audio_content->name, audio_content->type, rejected,
1901 audio_answer.release());
1902 return true;
1903}
1904
1905bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1906 const SessionDescription* offer,
1907 const MediaSessionOptions& options,
1908 const SessionDescription* current_description,
1909 StreamParamsVec* current_streams,
1910 SessionDescription* answer) const {
1911 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001912 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001913 video_content->name, offer,
1914 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001915 if (!video_transport) {
1916 return false;
1917 }
1918
kwiberg31022942016-03-11 14:18:21 -08001919 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001920 new VideoContentDescription());
1921 // Do not require or create SDES cryptos if DTLS is used.
1922 cricket::SecurePolicy sdes_policy =
1923 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1924 bool bundle_enabled =
1925 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1926 if (!CreateMediaContentAnswer(
1927 static_cast<const VideoContentDescription*>(
1928 video_content->description),
1929 options,
1930 video_codecs_,
1931 sdes_policy,
1932 GetCryptos(GetFirstVideoContentDescription(current_description)),
1933 video_rtp_extensions_,
1934 current_streams,
1935 add_legacy_,
1936 bundle_enabled,
1937 video_answer.get())) {
1938 return false;
1939 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001940 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001941 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1942 video_answer->protocol(),
1943 video_transport->secure());
1944 if (!rejected) {
1945 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1946 answer)) {
1947 return false;
1948 }
1949 video_answer->set_bandwidth(options.video_bandwidth);
1950 } else {
1951 // RFC 3264
1952 // The answer MUST contain the same number of m-lines as the offer.
1953 LOG(LS_INFO) << "Video is not supported in the answer.";
1954 }
1955 answer->AddContent(video_content->name, video_content->type, rejected,
1956 video_answer.release());
1957 return true;
1958}
1959
1960bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1961 const SessionDescription* offer,
1962 const MediaSessionOptions& options,
1963 const SessionDescription* current_description,
1964 StreamParamsVec* current_streams,
1965 SessionDescription* answer) const {
1966 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001967 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001968 data_content->name, offer,
1969 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001970 if (!data_transport) {
1971 return false;
1972 }
1973 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1974 std::vector<DataCodec> data_codecs(data_codecs_);
1975 FilterDataCodecs(&data_codecs, is_sctp);
1976
kwiberg31022942016-03-11 14:18:21 -08001977 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001978 new DataContentDescription());
1979 // Do not require or create SDES cryptos if DTLS is used.
1980 cricket::SecurePolicy sdes_policy =
1981 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1982 bool bundle_enabled =
1983 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1984 if (!CreateMediaContentAnswer(
1985 static_cast<const DataContentDescription*>(
1986 data_content->description),
1987 options,
1988 data_codecs_,
1989 sdes_policy,
1990 GetCryptos(GetFirstDataContentDescription(current_description)),
1991 RtpHeaderExtensions(),
1992 current_streams,
1993 add_legacy_,
1994 bundle_enabled,
1995 data_answer.get())) {
1996 return false; // Fails the session setup.
1997 }
1998
zstein4b2e0822017-02-17 19:48:38 -08001999 // Respond with sctpmap if the offer uses sctpmap.
2000 const DataContentDescription* offer_data_description =
2001 static_cast<const DataContentDescription*>(data_content->description);
2002 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2003 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2004
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002005 bool rejected = !options.has_data() || data_content->rejected ||
2006 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2007 data_answer->protocol(),
2008 data_transport->secure());
2009 if (!rejected) {
2010 data_answer->set_bandwidth(options.data_bandwidth);
2011 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
2012 answer)) {
2013 return false;
2014 }
2015 } else {
2016 // RFC 3264
2017 // The answer MUST contain the same number of m-lines as the offer.
2018 LOG(LS_INFO) << "Data is not supported in the answer.";
2019 }
2020 answer->AddContent(data_content->name, data_content->type, rejected,
2021 data_answer.release());
2022 return true;
2023}
2024
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002025bool IsMediaContent(const ContentInfo* content) {
2026 return (content &&
2027 (content->type == NS_JINGLE_RTP ||
2028 content->type == NS_JINGLE_DRAFT_SCTP));
2029}
2030
2031bool IsAudioContent(const ContentInfo* content) {
2032 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2033}
2034
2035bool IsVideoContent(const ContentInfo* content) {
2036 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2037}
2038
2039bool IsDataContent(const ContentInfo* content) {
2040 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2041}
2042
deadbeef0ed85b22016-02-23 17:24:52 -08002043const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2044 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002045 for (const ContentInfo& content : contents) {
2046 if (IsMediaContentOfType(&content, media_type)) {
2047 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002048 }
2049 }
deadbeef0ed85b22016-02-23 17:24:52 -08002050 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002051}
2052
2053const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2054 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2055}
2056
2057const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2058 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2059}
2060
2061const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2062 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2063}
2064
2065static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2066 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002067 if (sdesc == nullptr) {
2068 return nullptr;
2069 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002070
2071 return GetFirstMediaContent(sdesc->contents(), media_type);
2072}
2073
2074const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2075 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2076}
2077
2078const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2079 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2080}
2081
2082const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2083 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2084}
2085
2086const MediaContentDescription* GetFirstMediaContentDescription(
2087 const SessionDescription* sdesc, MediaType media_type) {
2088 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2089 const ContentDescription* description = content ? content->description : NULL;
2090 return static_cast<const MediaContentDescription*>(description);
2091}
2092
2093const AudioContentDescription* GetFirstAudioContentDescription(
2094 const SessionDescription* sdesc) {
2095 return static_cast<const AudioContentDescription*>(
2096 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2097}
2098
2099const VideoContentDescription* GetFirstVideoContentDescription(
2100 const SessionDescription* sdesc) {
2101 return static_cast<const VideoContentDescription*>(
2102 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2103}
2104
2105const DataContentDescription* GetFirstDataContentDescription(
2106 const SessionDescription* sdesc) {
2107 return static_cast<const DataContentDescription*>(
2108 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2109}
2110
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002111//
2112// Non-const versions of the above functions.
2113//
2114
2115ContentInfo* GetFirstMediaContent(ContentInfos& contents,
2116 MediaType media_type) {
2117 for (ContentInfo& content : contents) {
2118 if (IsMediaContentOfType(&content, media_type)) {
2119 return &content;
2120 }
2121 }
2122 return nullptr;
2123}
2124
2125ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2126 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2127}
2128
2129ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2130 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2131}
2132
2133ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2134 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2135}
2136
2137static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2138 MediaType media_type) {
2139 if (sdesc == nullptr) {
2140 return nullptr;
2141 }
2142
2143 return GetFirstMediaContent(sdesc->contents(), media_type);
2144}
2145
2146ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2147 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2148}
2149
2150ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2151 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2152}
2153
2154ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2155 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2156}
2157
2158MediaContentDescription* GetFirstMediaContentDescription(
2159 SessionDescription* sdesc,
2160 MediaType media_type) {
2161 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2162 ContentDescription* description = content ? content->description : NULL;
2163 return static_cast<MediaContentDescription*>(description);
2164}
2165
2166AudioContentDescription* GetFirstAudioContentDescription(
2167 SessionDescription* sdesc) {
2168 return static_cast<AudioContentDescription*>(
2169 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2170}
2171
2172VideoContentDescription* GetFirstVideoContentDescription(
2173 SessionDescription* sdesc) {
2174 return static_cast<VideoContentDescription*>(
2175 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2176}
2177
2178DataContentDescription* GetFirstDataContentDescription(
2179 SessionDescription* sdesc) {
2180 return static_cast<DataContentDescription*>(
2181 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2182}
2183
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002184} // namespace cricket