blob: f91a7297204541d7fcf24b82b053273e9dc614f6 [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"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000022#include "webrtc/base/helpers.h"
23#include "webrtc/base/logging.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000024#include "webrtc/base/stringutils.h"
magjedf823ede2016-11-12 09:53:04 -080025#include "webrtc/common_video/h264/profile_level_id.h"
kjellandera96e2d72016-02-04 23:52:28 -080026#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080027#include "webrtc/media/base/mediaconstants.h"
28#include "webrtc/p2p/base/p2pconstants.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010029#include "webrtc/pc/channelmanager.h"
30#include "webrtc/pc/srtpfilter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000031
32namespace {
33const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080034
jbauchcb560652016-08-04 05:20:32 -070035void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
36 std::vector<int>*),
37 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080038 std::vector<std::string>* names) {
39#ifdef HAVE_SRTP
40 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070041 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080042 for (const auto crypto : crypto_suites) {
43 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
44 }
45#endif
46}
terelius8c011e52016-04-26 05:28:11 -070047} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048
49namespace cricket {
50
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051// RTP Profile names
52// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
53// RFC4585
54const char kMediaProtocolAvpf[] = "RTP/AVPF";
55// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000056const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
57
deadbeeff3938292015-07-15 12:20:53 -070058// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
59// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060const char kMediaProtocolSavpf[] = "RTP/SAVPF";
61
62const char kMediaProtocolRtpPrefix[] = "RTP/";
63
64const char kMediaProtocolSctp[] = "SCTP";
65const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000066const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000067const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068
ossu075af922016-06-14 03:29:38 -070069RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
70 MediaContentDirection md) {
71 const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
72 const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
73 return RtpTransceiverDirection(send, recv);
74}
75
76MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
77 if (send && recv) {
78 return MD_SENDRECV;
79 } else if (send) {
80 return MD_SENDONLY;
81 } else if (recv) {
82 return MD_RECVONLY;
83 }
84
85 return MD_INACTIVE;
86}
87
88RtpTransceiverDirection
89NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
90 RtpTransceiverDirection wants) {
91 return RtpTransceiverDirection(offer.recv && wants.send,
92 offer.send && wants.recv);
93}
94
henrike@webrtc.org28e20752013-07-10 00:45:36 +000095static bool IsMediaContentOfType(const ContentInfo* content,
96 MediaType media_type) {
97 if (!IsMediaContent(content)) {
98 return false;
99 }
100
101 const MediaContentDescription* mdesc =
102 static_cast<const MediaContentDescription*>(content->description);
103 return mdesc && mdesc->type() == media_type;
104}
105
106static bool CreateCryptoParams(int tag, const std::string& cipher,
107 CryptoParams *out) {
jbauchcb560652016-08-04 05:20:32 -0700108 int key_len;
109 int salt_len;
110 if (!rtc::GetSrtpKeyAndSaltLengths(
111 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112 return false;
113 }
jbauchcb560652016-08-04 05:20:32 -0700114
115 int master_key_len = key_len + salt_len;
116 std::string master_key;
117 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
118 return false;
119 }
120
121 RTC_CHECK_EQ(static_cast<size_t>(master_key_len), master_key.size());
122 std::string key = rtc::Base64::Encode(master_key);
123
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124 out->tag = tag;
125 out->cipher_suite = cipher;
126 out->key_params = kInline;
127 out->key_params += key;
128 return true;
129}
130
131#ifdef HAVE_SRTP
132static bool AddCryptoParams(const std::string& cipher_suite,
133 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000134 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135
136 out->resize(size + 1);
137 return CreateCryptoParams(size, cipher_suite, &out->at(size));
138}
139
140void AddMediaCryptos(const CryptoParamsVec& cryptos,
141 MediaContentDescription* media) {
142 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
143 crypto != cryptos.end(); ++crypto) {
144 media->AddCrypto(*crypto);
145 }
146}
147
148bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
149 MediaContentDescription* media) {
150 CryptoParamsVec cryptos;
151 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
152 it != crypto_suites.end(); ++it) {
153 if (!AddCryptoParams(*it, &cryptos)) {
154 return false;
155 }
156 }
157 AddMediaCryptos(cryptos, media);
158 return true;
159}
160#endif
161
162const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
163 if (!media) {
164 return NULL;
165 }
166 return &media->cryptos();
167}
168
169bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
170 const CryptoParams& crypto,
171 CryptoParams* out) {
172 for (CryptoParamsVec::const_iterator it = cryptos.begin();
173 it != cryptos.end(); ++it) {
174 if (crypto.Matches(*it)) {
175 *out = *it;
176 return true;
177 }
178 }
179 return false;
180}
181
jbauchcb560652016-08-04 05:20:32 -0700182// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
183void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
184 std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185#ifdef HAVE_SRTP
jbauchcb560652016-08-04 05:20:32 -0700186 if (crypto_options.enable_gcm_crypto_suites) {
187 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
188 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
189 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800190 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
191 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192#endif
193}
194
jbauchcb560652016-08-04 05:20:32 -0700195void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800196 std::vector<std::string>* crypto_suite_names) {
197 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700198 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199}
200
jbauchcb560652016-08-04 05:20:32 -0700201void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
202 std::vector<int>* crypto_suites) {
203 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000204}
205
jbauchcb560652016-08-04 05:20:32 -0700206void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800207 std::vector<std::string>* crypto_suite_names) {
208 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700209 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800210}
211
jbauchcb560652016-08-04 05:20:32 -0700212void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
213 std::vector<int>* crypto_suites) {
214 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800215}
216
jbauchcb560652016-08-04 05:20:32 -0700217void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800218 std::vector<std::string>* crypto_suite_names) {
219 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
jbauchcb560652016-08-04 05:20:32 -0700220 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800221}
222
jbauchcb560652016-08-04 05:20:32 -0700223void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
224 std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225#ifdef HAVE_SRTP
jbauchcb560652016-08-04 05:20:32 -0700226 if (crypto_options.enable_gcm_crypto_suites) {
227 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
228 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
229 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800230 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231#endif
232}
233
jbauchcb560652016-08-04 05:20:32 -0700234void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800235 std::vector<std::string>* crypto_suite_names) {
jbauchcb560652016-08-04 05:20:32 -0700236 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites,
237 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800238}
239
jbauchcb560652016-08-04 05:20:32 -0700240// Support any GCM cipher (if enabled through options). For video support only
241// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
242// because it is low overhead.
243// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244static bool SelectCrypto(const MediaContentDescription* offer,
245 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700246 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247 CryptoParams *crypto) {
248 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
249 const CryptoParamsVec& cryptos = offer->cryptos();
250
251 for (CryptoParamsVec::const_iterator i = cryptos.begin();
252 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700253 if ((crypto_options.enable_gcm_crypto_suites &&
254 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
255 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700256 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
257 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
259 }
260 }
261 return false;
262}
263
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000265// The generated values are added to |ssrcs|.
266// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000267static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000268 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200269 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000270 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200271 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000273 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000274 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
276 ssrcs->push_back(candidate);
277 }
278}
279
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280// Finds all StreamParams of all media types and attach them to stream_params.
281static void GetCurrentStreamParams(const SessionDescription* sdesc,
282 StreamParamsVec* stream_params) {
283 if (!sdesc)
284 return;
285
286 const ContentInfos& contents = sdesc->contents();
287 for (ContentInfos::const_iterator content = contents.begin();
288 content != contents.end(); ++content) {
289 if (!IsMediaContent(&*content)) {
290 continue;
291 }
292 const MediaContentDescription* media =
293 static_cast<const MediaContentDescription*>(
294 content->description);
295 const StreamParamsVec& streams = media->streams();
296 for (StreamParamsVec::const_iterator it = streams.begin();
297 it != streams.end(); ++it) {
298 stream_params->push_back(*it);
299 }
300 }
301}
302
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000303// Filters the data codecs for the data channel type.
304void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
305 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700306 const char* codec_name =
307 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000308 for (std::vector<DataCodec>::iterator iter = codecs->begin();
309 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700310 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000311 iter = codecs->erase(iter);
312 } else {
313 ++iter;
314 }
315 }
316}
317
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318template <typename IdStruct>
319class UsedIds {
320 public:
321 UsedIds(int min_allowed_id, int max_allowed_id)
322 : min_allowed_id_(min_allowed_id),
323 max_allowed_id_(max_allowed_id),
324 next_id_(max_allowed_id) {
325 }
326
327 // Loops through all Id in |ids| and changes its id if it is
328 // already in use by another IdStruct. Call this methods with all Id
329 // in a session description to make sure no duplicate ids exists.
330 // Note that typename Id must be a type of IdStruct.
331 template <typename Id>
332 void FindAndSetIdUsed(std::vector<Id>* ids) {
333 for (typename std::vector<Id>::iterator it = ids->begin();
334 it != ids->end(); ++it) {
335 FindAndSetIdUsed(&*it);
336 }
337 }
338
339 // Finds and sets an unused id if the |idstruct| id is already in use.
340 void FindAndSetIdUsed(IdStruct* idstruct) {
341 const int original_id = idstruct->id;
342 int new_id = idstruct->id;
343
344 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
345 // If the original id is not in range - this is an id that can't be
346 // dynamically changed.
347 return;
348 }
349
350 if (IsIdUsed(original_id)) {
351 new_id = FindUnusedId();
352 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
353 << " to " << new_id;
354 idstruct->id = new_id;
355 }
356 SetIdUsed(new_id);
357 }
358
359 private:
360 // Returns the first unused id in reverse order.
361 // This hopefully reduce the risk of more collisions. We want to change the
362 // default ids as little as possible.
363 int FindUnusedId() {
364 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
365 --next_id_;
366 }
367 ASSERT(next_id_ >= min_allowed_id_);
368 return next_id_;
369 }
370
371 bool IsIdUsed(int new_id) {
372 return id_set_.find(new_id) != id_set_.end();
373 }
374
375 void SetIdUsed(int new_id) {
376 id_set_.insert(new_id);
377 }
378
379 const int min_allowed_id_;
380 const int max_allowed_id_;
381 int next_id_;
382 std::set<int> id_set_;
383};
384
385// Helper class used for finding duplicate RTP payload types among audio, video
386// and data codecs. When bundle is used the payload types may not collide.
387class UsedPayloadTypes : public UsedIds<Codec> {
388 public:
389 UsedPayloadTypes()
390 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
391 }
392
393
394 private:
395 static const int kDynamicPayloadTypeMin = 96;
396 static const int kDynamicPayloadTypeMax = 127;
397};
398
399// Helper class used for finding duplicate RTP Header extension ids among
400// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700401class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000402 public:
403 UsedRtpHeaderExtensionIds()
isheriff6f8d6862016-05-26 11:24:55 -0700404 : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000405
406 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000407 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000408 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000409 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410};
411
412static bool IsSctp(const MediaContentDescription* desc) {
413 return ((desc->protocol() == kMediaProtocolSctp) ||
414 (desc->protocol() == kMediaProtocolDtlsSctp));
415}
416
417// Adds a StreamParams for each Stream in Streams with media type
418// media_type to content_description.
419// |current_params| - All currently known StreamParams of any media type.
420template <class C>
zhihuang8f65cdf2016-05-06 18:40:30 -0700421static bool AddStreamParams(MediaType media_type,
422 const MediaSessionOptions& options,
423 StreamParamsVec* current_streams,
424 MediaContentDescriptionImpl<C>* content_description,
425 const bool add_legacy_stream) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700426 // SCTP streams are not negotiated using SDP/ContentDescriptions.
427 if (IsSctp(content_description)) {
428 return true;
429 }
430
Noah Richards2e7a0982015-05-18 14:02:54 -0700431 const bool include_rtx_streams =
432 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433
zhihuang8f65cdf2016-05-06 18:40:30 -0700434 const MediaSessionOptions::Streams& streams = options.streams;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 if (streams.empty() && add_legacy_stream) {
436 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200437 std::vector<uint32_t> ssrcs;
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700438 int num_ssrcs = include_rtx_streams ? 2 : 1;
439 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
Noah Richards2e7a0982015-05-18 14:02:54 -0700440 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
442 content_description->set_multistream(true);
443 } else {
444 content_description->AddLegacyStream(ssrcs[0]);
445 }
446 return true;
447 }
448
449 MediaSessionOptions::Streams::const_iterator stream_it;
450 for (stream_it = streams.begin();
451 stream_it != streams.end(); ++stream_it) {
452 if (stream_it->type != media_type)
453 continue; // Wrong media type.
454
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000455 const StreamParams* param =
456 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457 // groupid is empty for StreamParams generated using
458 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000459 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000460 // This is a new stream.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200461 std::vector<uint32_t> ssrcs;
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700462 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 StreamParams stream_param;
464 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000465 // Add the generated ssrc.
466 for (size_t i = 0; i < ssrcs.size(); ++i) {
467 stream_param.ssrcs.push_back(ssrcs[i]);
468 }
469 if (stream_it->num_sim_layers > 1) {
470 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
471 stream_param.ssrc_groups.push_back(group);
472 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700473 // Generate extra ssrcs for include_rtx_streams case.
474 if (include_rtx_streams) {
475 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200476 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700477 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
478 &rtx_ssrcs);
479 for (size_t i = 0; i < ssrcs.size(); ++i) {
480 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
481 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 content_description->set_multistream(true);
483 }
zhihuang8f65cdf2016-05-06 18:40:30 -0700484 stream_param.cname = options.rtcp_cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000485 stream_param.sync_label = stream_it->sync_label;
486 content_description->AddStream(stream_param);
487
488 // Store the new StreamParams in current_streams.
489 // This is necessary so that we can use the CNAME for other media types.
490 current_streams->push_back(stream_param);
491 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000492 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 }
494 }
495 return true;
496}
497
498// Updates the transport infos of the |sdesc| according to the given
499// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800500// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
501// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
503 SessionDescription* sdesc) {
504 // The bundle should not be empty.
505 if (!sdesc || !bundle_group.FirstContentName()) {
506 return false;
507 }
508
509 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700510 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 const TransportInfo* selected_transport_info =
512 sdesc->GetTransportInfoByName(selected_content_name);
513 if (!selected_transport_info) {
514 return false;
515 }
516
517 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700518 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000519 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700520 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000521 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800522 ConnectionRole selected_connection_role =
523 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000524 for (TransportInfos::iterator it =
525 sdesc->transport_infos().begin();
526 it != sdesc->transport_infos().end(); ++it) {
527 if (bundle_group.HasContentName(it->content_name) &&
528 it->content_name != selected_content_name) {
529 it->description.ice_ufrag = selected_ufrag;
530 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800531 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532 }
533 }
534 return true;
535}
536
537// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
538// sets it to |cryptos|.
539static bool GetCryptosByName(const SessionDescription* sdesc,
540 const std::string& content_name,
541 CryptoParamsVec* cryptos) {
542 if (!sdesc || !cryptos) {
543 return false;
544 }
545
546 const ContentInfo* content = sdesc->GetContentByName(content_name);
547 if (!IsMediaContent(content) || !content->description) {
548 return false;
549 }
550
551 const MediaContentDescription* media_desc =
552 static_cast<const MediaContentDescription*>(content->description);
553 *cryptos = media_desc->cryptos();
554 return true;
555}
556
557// Predicate function used by the remove_if.
558// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
559static bool CryptoNotFound(const CryptoParams crypto,
560 const CryptoParamsVec* filter) {
561 if (filter == NULL) {
562 return true;
563 }
564 for (CryptoParamsVec::const_iterator it = filter->begin();
565 it != filter->end(); ++it) {
566 if (it->cipher_suite == crypto.cipher_suite) {
567 return false;
568 }
569 }
570 return true;
571}
572
573// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
574// which are not available in |filter|.
575static void PruneCryptos(const CryptoParamsVec& filter,
576 CryptoParamsVec* target_cryptos) {
577 if (!target_cryptos) {
578 return;
579 }
580 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
581 target_cryptos->end(),
582 bind2nd(ptr_fun(CryptoNotFound),
583 &filter)),
584 target_cryptos->end());
585}
586
deadbeefb5cb19b2015-11-23 16:39:12 -0800587static bool IsRtpProtocol(const std::string& protocol) {
588 return protocol.empty() ||
589 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
590}
591
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592static bool IsRtpContent(SessionDescription* sdesc,
593 const std::string& content_name) {
594 bool is_rtp = false;
595 ContentInfo* content = sdesc->GetContentByName(content_name);
596 if (IsMediaContent(content)) {
597 MediaContentDescription* media_desc =
598 static_cast<MediaContentDescription*>(content->description);
599 if (!media_desc) {
600 return false;
601 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800602 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 }
604 return is_rtp;
605}
606
607// Updates the crypto parameters of the |sdesc| according to the given
608// |bundle_group|. The crypto parameters of all the contents within the
609// |bundle_group| should be updated to use the common subset of the
610// available cryptos.
611static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
612 SessionDescription* sdesc) {
613 // The bundle should not be empty.
614 if (!sdesc || !bundle_group.FirstContentName()) {
615 return false;
616 }
617
wu@webrtc.org78187522013-10-07 23:32:02 +0000618 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000619 // Get the common cryptos.
620 const ContentNames& content_names = bundle_group.content_names();
621 CryptoParamsVec common_cryptos;
622 for (ContentNames::const_iterator it = content_names.begin();
623 it != content_names.end(); ++it) {
624 if (!IsRtpContent(sdesc, *it)) {
625 continue;
626 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000627 // The common cryptos are needed if any of the content does not have DTLS
628 // enabled.
629 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
630 common_cryptos_needed = true;
631 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000632 if (it == content_names.begin()) {
633 // Initial the common_cryptos with the first content in the bundle group.
634 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
635 return false;
636 }
637 if (common_cryptos.empty()) {
638 // If there's no crypto params, we should just return.
639 return true;
640 }
641 } else {
642 CryptoParamsVec cryptos;
643 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
644 return false;
645 }
646 PruneCryptos(cryptos, &common_cryptos);
647 }
648 }
649
wu@webrtc.org78187522013-10-07 23:32:02 +0000650 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000651 return false;
652 }
653
654 // Update to use the common cryptos.
655 for (ContentNames::const_iterator it = content_names.begin();
656 it != content_names.end(); ++it) {
657 if (!IsRtpContent(sdesc, *it)) {
658 continue;
659 }
660 ContentInfo* content = sdesc->GetContentByName(*it);
661 if (IsMediaContent(content)) {
662 MediaContentDescription* media_desc =
663 static_cast<MediaContentDescription*>(content->description);
664 if (!media_desc) {
665 return false;
666 }
667 media_desc->set_cryptos(common_cryptos);
668 }
669 }
670 return true;
671}
672
673template <class C>
674static bool ContainsRtxCodec(const std::vector<C>& codecs) {
675 typename std::vector<C>::const_iterator it;
676 for (it = codecs.begin(); it != codecs.end(); ++it) {
677 if (IsRtxCodec(*it)) {
678 return true;
679 }
680 }
681 return false;
682}
683
684template <class C>
685static bool IsRtxCodec(const C& codec) {
686 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
687}
688
deadbeef0ed85b22016-02-23 17:24:52 -0800689static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
690 const std::string& content_name) {
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700691 TransportOptions transport_options;
deadbeef0ed85b22016-02-23 17:24:52 -0800692 auto it = options.transport_options.find(content_name);
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700693 if (it != options.transport_options.end()) {
694 transport_options = it->second;
deadbeef0ed85b22016-02-23 17:24:52 -0800695 }
Honghai Zhang4cedf2b2016-08-31 08:18:11 -0700696 transport_options.enable_ice_renomination = options.enable_ice_renomination;
697 return transport_options;
deadbeef0ed85b22016-02-23 17:24:52 -0800698}
699
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000700// Create a media content to be offered in a session-initiate,
701// according to the given options.rtcp_mux, options.is_muc,
702// options.streams, codecs, secure_transport, crypto, and streams. If we don't
703// currently have crypto (in current_cryptos) and it is enabled (in
704// secure_policy), crypto is created (according to crypto_suites). If
705// add_legacy_stream is true, and current_streams is empty, a legacy
706// stream is created. The created content is added to the offer.
707template <class C>
708static bool CreateMediaContentOffer(
709 const MediaSessionOptions& options,
710 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000711 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 const CryptoParamsVec* current_cryptos,
713 const std::vector<std::string>& crypto_suites,
714 const RtpHeaderExtensions& rtp_extensions,
715 bool add_legacy_stream,
716 StreamParamsVec* current_streams,
717 MediaContentDescriptionImpl<C>* offer) {
718 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000719
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000720 if (secure_policy == SEC_REQUIRED) {
721 offer->set_crypto_required(CT_SDES);
722 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000723 offer->set_rtcp_mux(options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700724 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
725 offer->set_rtcp_reduced_size(true);
726 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000727 offer->set_multistream(options.is_muc);
728 offer->set_rtp_header_extensions(rtp_extensions);
729
zhihuang8f65cdf2016-05-06 18:40:30 -0700730 if (!AddStreamParams(offer->type(), options, current_streams, offer,
731 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000732 return false;
733 }
734
735#ifdef HAVE_SRTP
736 if (secure_policy != SEC_DISABLED) {
737 if (current_cryptos) {
738 AddMediaCryptos(*current_cryptos, offer);
739 }
740 if (offer->cryptos().empty()) {
741 if (!CreateMediaCryptos(crypto_suites, offer)) {
742 return false;
743 }
744 }
745 }
746#endif
747
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000748 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000749 return false;
750 }
751 return true;
752}
753
754template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000755static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800756 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000757 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800758 const int codec2_id) {
759 const C* codec1 = FindCodecById(codecs1, codec1_id);
760 const C* codec2 = FindCodecById(codecs2, codec2_id);
761 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000762}
763
764template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765static void NegotiateCodecs(const std::vector<C>& local_codecs,
766 const std::vector<C>& offered_codecs,
767 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800768 for (const C& ours : local_codecs) {
769 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700770 // Note that we intentionally only find one matching codec for each of our
771 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800772 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
773 C negotiated = ours;
774 negotiated.IntersectFeedbackParams(theirs);
775 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800776 const auto apt_it =
777 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800778 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800779 RTC_DCHECK(apt_it != theirs.params.end());
780 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000781 }
magjedf823ede2016-11-12 09:53:04 -0800782 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
783 webrtc::H264::GenerateProfileLevelIdForAnswer(
784 ours.params, theirs.params, &negotiated.params);
785 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800786 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700787 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800788 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000789 }
790 }
deadbeef67cf2c12016-04-13 10:07:16 -0700791 // RFC3264: Although the answerer MAY list the formats in their desired
792 // order of preference, it is RECOMMENDED that unless there is a
793 // specific reason, the answerer list formats in the same relative order
794 // they were present in the offer.
795 std::unordered_map<int, int> payload_type_preferences;
796 int preference = static_cast<int>(offered_codecs.size() + 1);
797 for (const C& codec : offered_codecs) {
798 payload_type_preferences[codec.id] = preference--;
799 }
800 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
801 [&payload_type_preferences](const C& a, const C& b) {
802 return payload_type_preferences[a.id] >
803 payload_type_preferences[b.id];
804 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000805}
806
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800807// Finds a codec in |codecs2| that matches |codec_to_match|, which is
808// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
809// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000810template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800811static bool FindMatchingCodec(const std::vector<C>& codecs1,
812 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000813 const C& codec_to_match,
814 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800815 for (const C& potential_match : codecs2) {
816 if (potential_match.Matches(codec_to_match)) {
817 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800818 int apt_value_1 = 0;
819 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800820 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
821 &apt_value_1) ||
822 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
823 &apt_value_2)) {
824 LOG(LS_WARNING) << "RTX missing associated payload type.";
825 continue;
826 }
827 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
828 apt_value_2)) {
829 continue;
830 }
831 }
832 if (found_codec) {
833 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000834 }
835 return true;
836 }
837 }
838 return false;
839}
840
841// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
842// already exist in |offered_codecs| and ensure the payload types don't
843// collide.
844template <class C>
845static void FindCodecsToOffer(
846 const std::vector<C>& reference_codecs,
847 std::vector<C>* offered_codecs,
848 UsedPayloadTypes* used_pltypes) {
849
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000850 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800851 for (const C& reference_codec : reference_codecs) {
852 if (!IsRtxCodec(reference_codec) &&
853 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
854 reference_codec, nullptr)) {
855 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000856 used_pltypes->FindAndSetIdUsed(&codec);
857 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000858 }
859 }
860
861 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800862 for (const C& reference_codec : reference_codecs) {
863 if (IsRtxCodec(reference_codec) &&
864 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
865 reference_codec, nullptr)) {
866 C rtx_codec = reference_codec;
867
868 std::string associated_pt_str;
869 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
870 &associated_pt_str)) {
871 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
872 << " is missing an associated payload type.";
873 continue;
874 }
875
876 int associated_pt;
877 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
878 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
879 << " of RTX codec " << rtx_codec.name
880 << " to an integer.";
881 continue;
882 }
883
884 // Find the associated reference codec for the reference RTX codec.
magjedb05fa242016-11-11 04:00:16 -0800885 const C* associated_codec =
886 FindCodecById(reference_codecs, associated_pt);
887 if (!associated_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800888 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
889 << associated_pt << " for RTX codec " << rtx_codec.name
890 << ".";
891 continue;
892 }
893
894 // Find a codec in the offered list that matches the reference codec.
895 // Its payload type may be different than the reference codec.
896 C matching_codec;
897 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800898 *associated_codec, &matching_codec)) {
899 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800900 << " codec.";
901 continue;
902 }
903
904 rtx_codec.params[kCodecParamAssociatedPayloadType] =
905 rtc::ToString(matching_codec.id);
906 used_pltypes->FindAndSetIdUsed(&rtx_codec);
907 offered_codecs->push_back(rtx_codec);
908 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000909 }
910}
911
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000912static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700913 const webrtc::RtpExtension& ext_to_match,
914 webrtc::RtpExtension* found_extension) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
916 it != extensions.end(); ++it) {
917 // We assume that all URIs are given in a canonical format.
918 if (it->uri == ext_to_match.uri) {
919 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000920 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000921 }
922 return true;
923 }
924 }
925 return false;
926}
927
deadbeefa5b273a2015-08-20 17:30:13 -0700928// Iterates through |offered_extensions|, adding each one to |all_extensions|
929// and |used_ids|, and resolving ID conflicts. If an offered extension has the
930// same URI as one in |all_extensions|, it will re-use the same ID and won't be
931// treated as a conflict.
932static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
933 RtpHeaderExtensions* all_extensions,
934 UsedRtpHeaderExtensionIds* used_ids) {
935 for (auto& extension : *offered_extensions) {
isheriff6f8d6862016-05-26 11:24:55 -0700936 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700937 if (FindByUri(*all_extensions, extension, &existing)) {
938 extension.id = existing.id;
939 } else {
940 used_ids->FindAndSetIdUsed(&extension);
941 all_extensions->push_back(extension);
942 }
943 }
944}
945
946// Adds |reference_extensions| to |offered_extensions|, while updating
947// |all_extensions| and |used_ids|.
948static void FindRtpHdrExtsToOffer(
949 const RtpHeaderExtensions& reference_extensions,
950 RtpHeaderExtensions* offered_extensions,
951 RtpHeaderExtensions* all_extensions,
952 UsedRtpHeaderExtensionIds* used_ids) {
953 for (auto reference_extension : reference_extensions) {
954 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
isheriff6f8d6862016-05-26 11:24:55 -0700955 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700956 if (FindByUri(*all_extensions, reference_extension, &existing)) {
957 offered_extensions->push_back(existing);
958 } else {
959 used_ids->FindAndSetIdUsed(&reference_extension);
960 all_extensions->push_back(reference_extension);
961 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000962 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000963 }
964 }
965}
966
967static void NegotiateRtpHeaderExtensions(
968 const RtpHeaderExtensions& local_extensions,
969 const RtpHeaderExtensions& offered_extensions,
970 RtpHeaderExtensions* negotiated_extenstions) {
971 RtpHeaderExtensions::const_iterator ours;
972 for (ours = local_extensions.begin();
973 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -0700974 webrtc::RtpExtension theirs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000975 if (FindByUri(offered_extensions, *ours, &theirs)) {
976 // We respond with their RTP header extension id.
977 negotiated_extenstions->push_back(theirs);
978 }
979 }
980}
981
982static void StripCNCodecs(AudioCodecs* audio_codecs) {
983 AudioCodecs::iterator iter = audio_codecs->begin();
984 while (iter != audio_codecs->end()) {
985 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
986 iter = audio_codecs->erase(iter);
987 } else {
988 ++iter;
989 }
990 }
991}
992
993// Create a media content to be answered in a session-accept,
994// according to the given options.rtcp_mux, options.streams, codecs,
995// crypto, and streams. If we don't currently have crypto (in
996// current_cryptos) and it is enabled (in secure_policy), crypto is
997// created (according to crypto_suites). If add_legacy_stream is
998// true, and current_streams is empty, a legacy stream is created.
999// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1000// from the incoming session-initiate. If the negotiation fails, this
1001// method returns false. The created content is added to the offer.
1002template <class C>
1003static bool CreateMediaContentAnswer(
1004 const MediaContentDescriptionImpl<C>* offer,
1005 const MediaSessionOptions& options,
1006 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001007 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001008 const CryptoParamsVec* current_cryptos,
1009 const RtpHeaderExtensions& local_rtp_extenstions,
1010 StreamParamsVec* current_streams,
1011 bool add_legacy_stream,
1012 bool bundle_enabled,
1013 MediaContentDescriptionImpl<C>* answer) {
1014 std::vector<C> negotiated_codecs;
1015 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1016 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001017 answer->set_protocol(offer->protocol());
1018 RtpHeaderExtensions negotiated_rtp_extensions;
1019 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1020 offer->rtp_header_extensions(),
1021 &negotiated_rtp_extensions);
1022 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1023
1024 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001025 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1026 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1027 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001028
1029 if (sdes_policy != SEC_DISABLED) {
1030 CryptoParams crypto;
jbauchcb560652016-08-04 05:20:32 -07001031 if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001032 if (current_cryptos) {
1033 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1034 }
1035 answer->AddCrypto(crypto);
1036 }
1037 }
1038
1039 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001040 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001041 return false;
1042 }
1043
zhihuang8f65cdf2016-05-06 18:40:30 -07001044 if (!AddStreamParams(answer->type(), options, current_streams, answer,
1045 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001046 return false; // Something went seriously wrong.
1047 }
1048
1049 // Make sure the answer media content direction is per default set as
1050 // described in RFC3264 section 6.1.
ossu075af922016-06-14 03:29:38 -07001051 const bool is_data = !IsRtpProtocol(answer->protocol());
1052 const bool has_send_streams = !answer->streams().empty();
1053 const bool wants_send = has_send_streams || is_data;
1054 const bool recv_audio =
1055 answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
1056 const bool recv_video =
1057 answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
1058 const bool recv_data =
1059 answer->type() == cricket::MEDIA_TYPE_DATA;
1060 const bool wants_receive = recv_audio || recv_video || recv_data;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001061
ossu075af922016-06-14 03:29:38 -07001062 auto offer_rtd =
1063 RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
1064 auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
1065 answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
1066 .ToMediaContentDirection());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001067 return true;
1068}
1069
zhihuangcf5b37c2016-05-05 11:44:35 -07001070static bool IsDtlsRtp(const std::string& protocol) {
1071 // Most-likely values first.
1072 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1073 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1074}
1075
1076static bool IsPlainRtp(const std::string& protocol) {
1077 // Most-likely values first.
1078 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1079 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1080}
1081
1082static bool IsDtlsSctp(const std::string& protocol) {
1083 return protocol == "DTLS/SCTP";
1084}
1085
1086static bool IsPlainSctp(const std::string& protocol) {
1087 return protocol == "SCTP";
1088}
1089
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001090static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001091 const std::string& protocol,
1092 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001093 // Since not all applications serialize and deserialize the media protocol,
1094 // we will have to accept |protocol| to be empty.
1095 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001096 return true;
1097 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001098
zhihuangcf5b37c2016-05-05 11:44:35 -07001099 if (type == MEDIA_TYPE_DATA) {
1100 // Check for SCTP, but also for RTP for RTP-based data channels.
1101 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1102 if (secure_transport) {
1103 // Most likely scenarios first.
1104 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1105 IsPlainRtp(protocol);
1106 } else {
1107 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1108 }
1109 }
1110
1111 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1112 // JSEP specifies.
1113 if (secure_transport) {
1114 // Most likely scenarios first.
1115 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1116 } else {
1117 return IsPlainRtp(protocol);
1118 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001119}
1120
1121static void SetMediaProtocol(bool secure_transport,
1122 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001123 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001124 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001125 else if (secure_transport)
1126 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001127 else
1128 desc->set_protocol(kMediaProtocolAvpf);
1129}
1130
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001131// Gets the TransportInfo of the given |content_name| from the
1132// |current_description|. If doesn't exist, returns a new one.
1133static const TransportDescription* GetTransportDescription(
1134 const std::string& content_name,
1135 const SessionDescription* current_description) {
1136 const TransportDescription* desc = NULL;
1137 if (current_description) {
1138 const TransportInfo* info =
1139 current_description->GetTransportInfoByName(content_name);
1140 if (info) {
1141 desc = &info->description;
1142 }
1143 }
1144 return desc;
1145}
1146
1147// Gets the current DTLS state from the transport description.
1148static bool IsDtlsActive(
1149 const std::string& content_name,
1150 const SessionDescription* current_description) {
1151 if (!current_description)
1152 return false;
1153
1154 const ContentInfo* content =
1155 current_description->GetContentByName(content_name);
1156 if (!content)
1157 return false;
1158
1159 const TransportDescription* current_tdesc =
1160 GetTransportDescription(content_name, current_description);
1161 if (!current_tdesc)
1162 return false;
1163
1164 return current_tdesc->secure();
1165}
1166
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001167std::string MediaTypeToString(MediaType type) {
1168 std::string type_str;
1169 switch (type) {
1170 case MEDIA_TYPE_AUDIO:
1171 type_str = "audio";
1172 break;
1173 case MEDIA_TYPE_VIDEO:
1174 type_str = "video";
1175 break;
1176 case MEDIA_TYPE_DATA:
1177 type_str = "data";
1178 break;
1179 default:
1180 ASSERT(false);
1181 break;
1182 }
1183 return type_str;
1184}
1185
ossu075af922016-06-14 03:29:38 -07001186std::string MediaContentDirectionToString(MediaContentDirection direction) {
1187 std::string dir_str;
1188 switch (direction) {
1189 case MD_INACTIVE:
1190 dir_str = "inactive";
1191 break;
1192 case MD_SENDONLY:
1193 dir_str = "sendonly";
1194 break;
1195 case MD_RECVONLY:
1196 dir_str = "recvonly";
1197 break;
1198 case MD_SENDRECV:
1199 dir_str = "sendrecv";
1200 break;
1201 default:
1202 ASSERT(false);
1203 break;
1204 }
1205
1206 return dir_str;
1207}
1208
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001209void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001210 const std::string& id,
1211 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001212 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001213}
1214
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001215void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001216 const std::string& id,
1217 const std::string& sync_label,
1218 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001219 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001220}
1221
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001222void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001223 MediaType type,
1224 const std::string& id,
1225 const std::string& sync_label,
1226 int num_sim_layers) {
1227 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001228
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001229 // If we haven't already set the data_channel_type, and we add a
1230 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001231 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001232 data_channel_type = DCT_RTP;
1233}
1234
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001235void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001236 const std::string& id) {
1237 Streams::iterator stream_it = streams.begin();
1238 for (; stream_it != streams.end(); ++stream_it) {
1239 if (stream_it->type == type && stream_it->id == id) {
1240 streams.erase(stream_it);
1241 return;
1242 }
1243 }
1244 ASSERT(false);
1245}
1246
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001247bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1248 Streams::const_iterator stream_it = streams.begin();
1249 for (; stream_it != streams.end(); ++stream_it) {
1250 if (stream_it->type == type) {
1251 return true;
1252 }
1253 }
1254 return false;
1255}
1256
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001257MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1258 const TransportDescriptionFactory* transport_desc_factory)
1259 : secure_(SEC_DISABLED),
1260 add_legacy_(true),
1261 transport_desc_factory_(transport_desc_factory) {
1262}
1263
1264MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1265 ChannelManager* channel_manager,
1266 const TransportDescriptionFactory* transport_desc_factory)
1267 : secure_(SEC_DISABLED),
1268 add_legacy_(true),
1269 transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001270 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1271 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1272 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001273 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001274 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001275 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1276 channel_manager->GetSupportedDataCodecs(&data_codecs_);
ossudedfd282016-06-14 07:12:39 -07001277 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
1278 &audio_sendrecv_codecs_);
ossu075af922016-06-14 03:29:38 -07001279}
1280
ossudedfd282016-06-14 07:12:39 -07001281const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1282 const {
ossu075af922016-06-14 03:29:38 -07001283 return audio_sendrecv_codecs_;
1284}
1285
1286const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1287 return audio_send_codecs_;
1288}
1289
1290const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1291 return audio_recv_codecs_;
1292}
1293
1294void MediaSessionDescriptionFactory::set_audio_codecs(
1295 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1296 audio_send_codecs_ = send_codecs;
1297 audio_recv_codecs_ = recv_codecs;
1298 audio_sendrecv_codecs_.clear();
1299 // Use NegotiateCodecs to merge our codec lists, since the operation is
1300 // essentially the same. Put send_codecs as the offered_codecs, which is the
1301 // order we'd like to follow. The reasoning is that encoding is usually more
1302 // expensive than decoding, and prioritizing a codec in the send list probably
1303 // means it's a codec we can handle efficiently.
1304 NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001305}
1306
1307SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1308 const MediaSessionOptions& options,
1309 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001310 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001311
1312 StreamParamsVec current_streams;
1313 GetCurrentStreamParams(current_description, &current_streams);
1314
ossu075af922016-06-14 03:29:38 -07001315 const bool wants_send =
1316 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1317 const AudioCodecs& supported_audio_codecs =
1318 GetAudioCodecsForOffer({wants_send, options.recv_audio});
1319
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001320 AudioCodecs audio_codecs;
1321 VideoCodecs video_codecs;
1322 DataCodecs data_codecs;
ossu075af922016-06-14 03:29:38 -07001323 GetCodecsToOffer(current_description, supported_audio_codecs,
1324 video_codecs_, data_codecs_,
1325 &audio_codecs, &video_codecs, &data_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001326
1327 if (!options.vad_enabled) {
1328 // If application doesn't want CN codecs in offer.
1329 StripCNCodecs(&audio_codecs);
1330 }
1331
1332 RtpHeaderExtensions audio_rtp_extensions;
1333 RtpHeaderExtensions video_rtp_extensions;
1334 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1335 &video_rtp_extensions);
1336
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001337 bool audio_added = false;
1338 bool video_added = false;
1339 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001340
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001341 // Iterate through the contents of |current_description| to maintain the order
1342 // of the m-lines in the new offer.
1343 if (current_description) {
1344 ContentInfos::const_iterator it = current_description->contents().begin();
1345 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001346 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001347 if (!AddAudioContentForOffer(options, current_description,
1348 audio_rtp_extensions, audio_codecs,
1349 &current_streams, offer.get())) {
1350 return NULL;
1351 }
1352 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001353 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001354 if (!AddVideoContentForOffer(options, current_description,
1355 video_rtp_extensions, video_codecs,
1356 &current_streams, offer.get())) {
1357 return NULL;
1358 }
1359 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001360 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001361 MediaSessionOptions options_copy(options);
1362 if (IsSctp(static_cast<const MediaContentDescription*>(
1363 it->description))) {
1364 options_copy.data_channel_type = DCT_SCTP;
1365 }
1366 if (!AddDataContentForOffer(options_copy, current_description,
1367 &data_codecs, &current_streams,
1368 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001369 return NULL;
1370 }
1371 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001372 } else {
1373 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001374 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001375 }
1376 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001377
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001378 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001379 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001380 !AddAudioContentForOffer(options, current_description,
1381 audio_rtp_extensions, audio_codecs,
1382 &current_streams, offer.get())) {
1383 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001384 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001385 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001386 !AddVideoContentForOffer(options, current_description,
1387 video_rtp_extensions, video_codecs,
1388 &current_streams, offer.get())) {
1389 return NULL;
1390 }
1391 if (!data_added && options.has_data() &&
1392 !AddDataContentForOffer(options, current_description, &data_codecs,
1393 &current_streams, offer.get())) {
1394 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395 }
1396
1397 // Bundle the contents together, if we've been asked to do so, and update any
1398 // parameters that need to be tweaked for BUNDLE.
1399 if (options.bundle_enabled) {
1400 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1401 for (ContentInfos::const_iterator content = offer->contents().begin();
1402 content != offer->contents().end(); ++content) {
1403 offer_bundle.AddContentName(content->name);
1404 }
1405 offer->AddGroup(offer_bundle);
1406 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1407 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1408 return NULL;
1409 }
1410 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1411 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1412 return NULL;
1413 }
1414 }
1415
1416 return offer.release();
1417}
1418
1419SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1420 const SessionDescription* offer, const MediaSessionOptions& options,
1421 const SessionDescription* current_description) const {
1422 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001423 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1424 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001425 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001426
1427 StreamParamsVec current_streams;
1428 GetCurrentStreamParams(current_description, &current_streams);
1429
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001430 if (offer) {
1431 ContentInfos::const_iterator it = offer->contents().begin();
1432 for (; it != offer->contents().end(); ++it) {
1433 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1434 if (!AddAudioContentForAnswer(offer, options, current_description,
1435 &current_streams, answer.get())) {
1436 return NULL;
1437 }
1438 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1439 if (!AddVideoContentForAnswer(offer, options, current_description,
1440 &current_streams, answer.get())) {
1441 return NULL;
1442 }
1443 } else {
1444 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1445 if (!AddDataContentForAnswer(offer, options, current_description,
1446 &current_streams, answer.get())) {
1447 return NULL;
1448 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001449 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001450 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001451 }
1452
1453 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1454 // group in the answer with the appropriate content names.
1455 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1456 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1457 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1458 for (ContentInfos::const_iterator content = answer->contents().begin();
1459 content != answer->contents().end(); ++content) {
1460 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1461 answer_bundle.AddContentName(content->name);
1462 }
1463 }
1464 if (answer_bundle.FirstContentName()) {
1465 answer->AddGroup(answer_bundle);
1466
1467 // Share the same ICE credentials and crypto params across all contents,
1468 // as BUNDLE requires.
1469 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1470 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1471 return NULL;
1472 }
1473
1474 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1475 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1476 return NULL;
1477 }
1478 }
1479 }
1480
1481 return answer.release();
1482}
1483
ossu075af922016-06-14 03:29:38 -07001484const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1485 const RtpTransceiverDirection& direction) const {
1486 // If stream is inactive - generate list as if sendrecv.
1487 if (direction.send == direction.recv) {
1488 return audio_sendrecv_codecs_;
1489 } else if (direction.send) {
1490 return audio_send_codecs_;
1491 } else {
1492 return audio_recv_codecs_;
1493 }
1494}
1495
1496const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1497 const RtpTransceiverDirection& offer,
1498 const RtpTransceiverDirection& answer) const {
1499 // For inactive and sendrecv answers, generate lists as if we were to accept
1500 // the offer's direction. See RFC 3264 Section 6.1.
1501 if (answer.send == answer.recv) {
1502 if (offer.send == offer.recv) {
1503 return audio_sendrecv_codecs_;
1504 } else if (offer.send) {
1505 return audio_recv_codecs_;
1506 } else {
1507 return audio_send_codecs_;
1508 }
1509 } else if (answer.send) {
1510 return audio_send_codecs_;
1511 } else {
1512 return audio_recv_codecs_;
1513 }
1514}
1515
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001516void MediaSessionDescriptionFactory::GetCodecsToOffer(
1517 const SessionDescription* current_description,
ossu075af922016-06-14 03:29:38 -07001518 const AudioCodecs& supported_audio_codecs,
1519 const VideoCodecs& supported_video_codecs,
1520 const DataCodecs& supported_data_codecs,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001521 AudioCodecs* audio_codecs,
1522 VideoCodecs* video_codecs,
1523 DataCodecs* data_codecs) const {
1524 UsedPayloadTypes used_pltypes;
1525 audio_codecs->clear();
1526 video_codecs->clear();
1527 data_codecs->clear();
1528
1529
1530 // First - get all codecs from the current description if the media type
1531 // is used.
1532 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1533 // type is added.
1534 if (current_description) {
1535 const AudioContentDescription* audio =
1536 GetFirstAudioContentDescription(current_description);
1537 if (audio) {
1538 *audio_codecs = audio->codecs();
1539 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1540 }
1541 const VideoContentDescription* video =
1542 GetFirstVideoContentDescription(current_description);
1543 if (video) {
1544 *video_codecs = video->codecs();
1545 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1546 }
1547 const DataContentDescription* data =
1548 GetFirstDataContentDescription(current_description);
1549 if (data) {
1550 *data_codecs = data->codecs();
1551 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1552 }
1553 }
1554
1555 // Add our codecs that are not in |current_description|.
ossu075af922016-06-14 03:29:38 -07001556 FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
1557 &used_pltypes);
1558 FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
1559 &used_pltypes);
1560 FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
1561 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001562}
1563
1564void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1565 const SessionDescription* current_description,
1566 RtpHeaderExtensions* audio_extensions,
1567 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001568 // All header extensions allocated from the same range to avoid potential
1569 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001570 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001571 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001572 audio_extensions->clear();
1573 video_extensions->clear();
1574
1575 // First - get all extensions from the current description if the media type
1576 // is used.
1577 // Add them to |used_ids| so the local ids are not reused if a new media
1578 // type is added.
1579 if (current_description) {
1580 const AudioContentDescription* audio =
1581 GetFirstAudioContentDescription(current_description);
1582 if (audio) {
1583 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001584 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001585 }
1586 const VideoContentDescription* video =
1587 GetFirstVideoContentDescription(current_description);
1588 if (video) {
1589 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001590 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001591 }
1592 }
1593
1594 // Add our default RTP header extensions that are not in
1595 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001596 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1597 &all_extensions, &used_ids);
1598 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1599 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001600}
1601
1602bool MediaSessionDescriptionFactory::AddTransportOffer(
1603 const std::string& content_name,
1604 const TransportOptions& transport_options,
1605 const SessionDescription* current_desc,
1606 SessionDescription* offer_desc) const {
1607 if (!transport_desc_factory_)
1608 return false;
1609 const TransportDescription* current_tdesc =
1610 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001611 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001612 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1613 bool ret = (new_tdesc.get() != NULL &&
1614 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1615 if (!ret) {
1616 LOG(LS_ERROR)
1617 << "Failed to AddTransportOffer, content name=" << content_name;
1618 }
1619 return ret;
1620}
1621
1622TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1623 const std::string& content_name,
1624 const SessionDescription* offer_desc,
1625 const TransportOptions& transport_options,
1626 const SessionDescription* current_desc) const {
1627 if (!transport_desc_factory_)
1628 return NULL;
1629 const TransportDescription* offer_tdesc =
1630 GetTransportDescription(content_name, offer_desc);
1631 const TransportDescription* current_tdesc =
1632 GetTransportDescription(content_name, current_desc);
1633 return
1634 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1635 current_tdesc);
1636}
1637
1638bool MediaSessionDescriptionFactory::AddTransportAnswer(
1639 const std::string& content_name,
1640 const TransportDescription& transport_desc,
1641 SessionDescription* answer_desc) const {
1642 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1643 transport_desc))) {
1644 LOG(LS_ERROR)
1645 << "Failed to AddTransportAnswer, content name=" << content_name;
1646 return false;
1647 }
1648 return true;
1649}
1650
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001651bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1652 const MediaSessionOptions& options,
1653 const SessionDescription* current_description,
1654 const RtpHeaderExtensions& audio_rtp_extensions,
1655 const AudioCodecs& audio_codecs,
1656 StreamParamsVec* current_streams,
1657 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001658 const ContentInfo* current_audio_content =
1659 GetFirstAudioContent(current_description);
1660 std::string content_name =
1661 current_audio_content ? current_audio_content->name : CN_AUDIO;
1662
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001663 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001664 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1665 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001666
kwiberg31022942016-03-11 14:18:21 -08001667 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001668 std::vector<std::string> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -07001669 GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001670 if (!CreateMediaContentOffer(
1671 options,
1672 audio_codecs,
1673 sdes_policy,
1674 GetCryptos(GetFirstAudioContentDescription(current_description)),
1675 crypto_suites,
1676 audio_rtp_extensions,
1677 add_legacy_,
1678 current_streams,
1679 audio.get())) {
1680 return false;
1681 }
1682 audio->set_lang(lang_);
1683
1684 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1685 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001686
ossu075af922016-06-14 03:29:38 -07001687 auto offer_rtd =
1688 RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
1689 audio->set_direction(offer_rtd.ToMediaContentDirection());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001690
deadbeef44f08192015-12-15 16:20:09 -08001691 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001692 if (!AddTransportOffer(content_name,
1693 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001694 current_description, desc)) {
1695 return false;
1696 }
1697
1698 return true;
1699}
1700
1701bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1702 const MediaSessionOptions& options,
1703 const SessionDescription* current_description,
1704 const RtpHeaderExtensions& video_rtp_extensions,
1705 const VideoCodecs& video_codecs,
1706 StreamParamsVec* current_streams,
1707 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001708 const ContentInfo* current_video_content =
1709 GetFirstVideoContent(current_description);
1710 std::string content_name =
1711 current_video_content ? current_video_content->name : CN_VIDEO;
1712
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001713 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001714 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1715 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001716
kwiberg31022942016-03-11 14:18:21 -08001717 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001718 std::vector<std::string> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -07001719 GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001720 if (!CreateMediaContentOffer(
1721 options,
1722 video_codecs,
1723 sdes_policy,
1724 GetCryptos(GetFirstVideoContentDescription(current_description)),
1725 crypto_suites,
1726 video_rtp_extensions,
1727 add_legacy_,
1728 current_streams,
1729 video.get())) {
1730 return false;
1731 }
1732
1733 video->set_bandwidth(options.video_bandwidth);
1734
1735 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1736 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001737
deadbeefc80741f2015-10-22 13:14:45 -07001738 if (!video->streams().empty()) {
1739 if (options.recv_video) {
1740 video->set_direction(MD_SENDRECV);
1741 } else {
1742 video->set_direction(MD_SENDONLY);
1743 }
1744 } else {
1745 if (options.recv_video) {
1746 video->set_direction(MD_RECVONLY);
1747 } else {
1748 video->set_direction(MD_INACTIVE);
1749 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001750 }
1751
deadbeef44f08192015-12-15 16:20:09 -08001752 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001753 if (!AddTransportOffer(content_name,
1754 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001755 current_description, desc)) {
1756 return false;
1757 }
1758
1759 return true;
1760}
1761
1762bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1763 const MediaSessionOptions& options,
1764 const SessionDescription* current_description,
1765 DataCodecs* data_codecs,
1766 StreamParamsVec* current_streams,
1767 SessionDescription* desc) const {
1768 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1769
kwiberg31022942016-03-11 14:18:21 -08001770 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001771 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1772
1773 FilterDataCodecs(data_codecs, is_sctp);
1774
deadbeef44f08192015-12-15 16:20:09 -08001775 const ContentInfo* current_data_content =
1776 GetFirstDataContent(current_description);
1777 std::string content_name =
1778 current_data_content ? current_data_content->name : CN_DATA;
1779
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001780 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001781 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1782 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001783 std::vector<std::string> crypto_suites;
1784 if (is_sctp) {
1785 // SDES doesn't make sense for SCTP, so we disable it, and we only
1786 // get SDES crypto suites for RTP-based data channels.
1787 sdes_policy = cricket::SEC_DISABLED;
1788 // Unlike SetMediaProtocol below, we need to set the protocol
1789 // before we call CreateMediaContentOffer. Otherwise,
1790 // CreateMediaContentOffer won't know this is SCTP and will
1791 // generate SSRCs rather than SIDs.
1792 data->set_protocol(
1793 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1794 } else {
jbauchcb560652016-08-04 05:20:32 -07001795 GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001796 }
1797
1798 if (!CreateMediaContentOffer(
1799 options,
1800 *data_codecs,
1801 sdes_policy,
1802 GetCryptos(GetFirstDataContentDescription(current_description)),
1803 crypto_suites,
1804 RtpHeaderExtensions(),
1805 add_legacy_,
1806 current_streams,
1807 data.get())) {
1808 return false;
1809 }
1810
1811 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001812 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001813 } else {
1814 data->set_bandwidth(options.data_bandwidth);
1815 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001816 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001817 }
deadbeef0ed85b22016-02-23 17:24:52 -08001818 if (!AddTransportOffer(content_name,
1819 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001820 current_description, desc)) {
1821 return false;
1822 }
1823 return true;
1824}
1825
1826bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1827 const SessionDescription* offer,
1828 const MediaSessionOptions& options,
1829 const SessionDescription* current_description,
1830 StreamParamsVec* current_streams,
1831 SessionDescription* answer) const {
1832 const ContentInfo* audio_content = GetFirstAudioContent(offer);
ossu075af922016-06-14 03:29:38 -07001833 const AudioContentDescription* offer_audio =
1834 static_cast<const AudioContentDescription*>(audio_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001835
kwiberg31022942016-03-11 14:18:21 -08001836 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001837 audio_content->name, offer,
1838 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001839 if (!audio_transport) {
1840 return false;
1841 }
1842
ossu075af922016-06-14 03:29:38 -07001843 // Pick codecs based on the requested communications direction in the offer.
1844 const bool wants_send =
1845 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1846 auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
1847 auto offer_rtd =
1848 RtpTransceiverDirection::FromMediaContentDirection(
1849 offer_audio->direction());
1850 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
1851 AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001852 if (!options.vad_enabled) {
1853 StripCNCodecs(&audio_codecs);
1854 }
1855
1856 bool bundle_enabled =
1857 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08001858 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001859 new AudioContentDescription());
1860 // Do not require or create SDES cryptos if DTLS is used.
1861 cricket::SecurePolicy sdes_policy =
1862 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1863 if (!CreateMediaContentAnswer(
ossu075af922016-06-14 03:29:38 -07001864 offer_audio,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001865 options,
1866 audio_codecs,
1867 sdes_policy,
1868 GetCryptos(GetFirstAudioContentDescription(current_description)),
1869 audio_rtp_extensions_,
1870 current_streams,
1871 add_legacy_,
1872 bundle_enabled,
1873 audio_answer.get())) {
1874 return false; // Fails the session setup.
1875 }
1876
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001877 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001878 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1879 audio_answer->protocol(),
1880 audio_transport->secure());
1881 if (!rejected) {
1882 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1883 } else {
1884 // RFC 3264
1885 // The answer MUST contain the same number of m-lines as the offer.
1886 LOG(LS_INFO) << "Audio is not supported in the answer.";
1887 }
1888
1889 answer->AddContent(audio_content->name, audio_content->type, rejected,
1890 audio_answer.release());
1891 return true;
1892}
1893
1894bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1895 const SessionDescription* offer,
1896 const MediaSessionOptions& options,
1897 const SessionDescription* current_description,
1898 StreamParamsVec* current_streams,
1899 SessionDescription* answer) const {
1900 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001901 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001902 video_content->name, offer,
1903 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001904 if (!video_transport) {
1905 return false;
1906 }
1907
kwiberg31022942016-03-11 14:18:21 -08001908 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001909 new VideoContentDescription());
1910 // Do not require or create SDES cryptos if DTLS is used.
1911 cricket::SecurePolicy sdes_policy =
1912 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1913 bool bundle_enabled =
1914 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1915 if (!CreateMediaContentAnswer(
1916 static_cast<const VideoContentDescription*>(
1917 video_content->description),
1918 options,
1919 video_codecs_,
1920 sdes_policy,
1921 GetCryptos(GetFirstVideoContentDescription(current_description)),
1922 video_rtp_extensions_,
1923 current_streams,
1924 add_legacy_,
1925 bundle_enabled,
1926 video_answer.get())) {
1927 return false;
1928 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001929 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001930 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1931 video_answer->protocol(),
1932 video_transport->secure());
1933 if (!rejected) {
1934 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1935 answer)) {
1936 return false;
1937 }
1938 video_answer->set_bandwidth(options.video_bandwidth);
1939 } else {
1940 // RFC 3264
1941 // The answer MUST contain the same number of m-lines as the offer.
1942 LOG(LS_INFO) << "Video is not supported in the answer.";
1943 }
1944 answer->AddContent(video_content->name, video_content->type, rejected,
1945 video_answer.release());
1946 return true;
1947}
1948
1949bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1950 const SessionDescription* offer,
1951 const MediaSessionOptions& options,
1952 const SessionDescription* current_description,
1953 StreamParamsVec* current_streams,
1954 SessionDescription* answer) const {
1955 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001956 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001957 data_content->name, offer,
1958 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001959 if (!data_transport) {
1960 return false;
1961 }
1962 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1963 std::vector<DataCodec> data_codecs(data_codecs_);
1964 FilterDataCodecs(&data_codecs, is_sctp);
1965
kwiberg31022942016-03-11 14:18:21 -08001966 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001967 new DataContentDescription());
1968 // Do not require or create SDES cryptos if DTLS is used.
1969 cricket::SecurePolicy sdes_policy =
1970 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1971 bool bundle_enabled =
1972 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1973 if (!CreateMediaContentAnswer(
1974 static_cast<const DataContentDescription*>(
1975 data_content->description),
1976 options,
1977 data_codecs_,
1978 sdes_policy,
1979 GetCryptos(GetFirstDataContentDescription(current_description)),
1980 RtpHeaderExtensions(),
1981 current_streams,
1982 add_legacy_,
1983 bundle_enabled,
1984 data_answer.get())) {
1985 return false; // Fails the session setup.
1986 }
1987
1988 bool rejected = !options.has_data() || data_content->rejected ||
1989 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1990 data_answer->protocol(),
1991 data_transport->secure());
1992 if (!rejected) {
1993 data_answer->set_bandwidth(options.data_bandwidth);
1994 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1995 answer)) {
1996 return false;
1997 }
1998 } else {
1999 // RFC 3264
2000 // The answer MUST contain the same number of m-lines as the offer.
2001 LOG(LS_INFO) << "Data is not supported in the answer.";
2002 }
2003 answer->AddContent(data_content->name, data_content->type, rejected,
2004 data_answer.release());
2005 return true;
2006}
2007
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002008bool IsMediaContent(const ContentInfo* content) {
2009 return (content &&
2010 (content->type == NS_JINGLE_RTP ||
2011 content->type == NS_JINGLE_DRAFT_SCTP));
2012}
2013
2014bool IsAudioContent(const ContentInfo* content) {
2015 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2016}
2017
2018bool IsVideoContent(const ContentInfo* content) {
2019 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2020}
2021
2022bool IsDataContent(const ContentInfo* content) {
2023 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2024}
2025
deadbeef0ed85b22016-02-23 17:24:52 -08002026const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2027 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002028 for (const ContentInfo& content : contents) {
2029 if (IsMediaContentOfType(&content, media_type)) {
2030 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002031 }
2032 }
deadbeef0ed85b22016-02-23 17:24:52 -08002033 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002034}
2035
2036const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2037 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2038}
2039
2040const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2041 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2042}
2043
2044const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2045 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2046}
2047
2048static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2049 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002050 if (sdesc == nullptr) {
2051 return nullptr;
2052 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002053
2054 return GetFirstMediaContent(sdesc->contents(), media_type);
2055}
2056
2057const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2058 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2059}
2060
2061const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2062 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2063}
2064
2065const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2066 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2067}
2068
2069const MediaContentDescription* GetFirstMediaContentDescription(
2070 const SessionDescription* sdesc, MediaType media_type) {
2071 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2072 const ContentDescription* description = content ? content->description : NULL;
2073 return static_cast<const MediaContentDescription*>(description);
2074}
2075
2076const AudioContentDescription* GetFirstAudioContentDescription(
2077 const SessionDescription* sdesc) {
2078 return static_cast<const AudioContentDescription*>(
2079 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2080}
2081
2082const VideoContentDescription* GetFirstVideoContentDescription(
2083 const SessionDescription* sdesc) {
2084 return static_cast<const VideoContentDescription*>(
2085 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2086}
2087
2088const DataContentDescription* GetFirstDataContentDescription(
2089 const SessionDescription* sdesc) {
2090 return static_cast<const DataContentDescription*>(
2091 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2092}
2093
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002094//
2095// Non-const versions of the above functions.
2096//
2097
2098ContentInfo* GetFirstMediaContent(ContentInfos& contents,
2099 MediaType media_type) {
2100 for (ContentInfo& content : contents) {
2101 if (IsMediaContentOfType(&content, media_type)) {
2102 return &content;
2103 }
2104 }
2105 return nullptr;
2106}
2107
2108ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2109 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2110}
2111
2112ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2113 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2114}
2115
2116ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2117 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2118}
2119
2120static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2121 MediaType media_type) {
2122 if (sdesc == nullptr) {
2123 return nullptr;
2124 }
2125
2126 return GetFirstMediaContent(sdesc->contents(), media_type);
2127}
2128
2129ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2130 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2131}
2132
2133ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2134 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2135}
2136
2137ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2138 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2139}
2140
2141MediaContentDescription* GetFirstMediaContentDescription(
2142 SessionDescription* sdesc,
2143 MediaType media_type) {
2144 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2145 ContentDescription* description = content ? content->description : NULL;
2146 return static_cast<MediaContentDescription*>(description);
2147}
2148
2149AudioContentDescription* GetFirstAudioContentDescription(
2150 SessionDescription* sdesc) {
2151 return static_cast<AudioContentDescription*>(
2152 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2153}
2154
2155VideoContentDescription* GetFirstVideoContentDescription(
2156 SessionDescription* sdesc) {
2157 return static_cast<VideoContentDescription*>(
2158 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2159}
2160
2161DataContentDescription* GetFirstDataContentDescription(
2162 SessionDescription* sdesc) {
2163 return static_cast<DataContentDescription*>(
2164 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2165}
2166
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002167} // namespace cricket