blob: 3ca1074d798542772ff61dada61e2df32930b4ae [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "api/optional.h"
Mirko Bonadei71207422017-09-15 13:58:09 +020022#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "media/base/cryptoparams.h"
24#include "media/base/h264_profile_level_id.h"
25#include "media/base/mediaconstants.h"
26#include "p2p/base/p2pconstants.h"
27#include "pc/channelmanager.h"
28#include "pc/srtpfilter.h"
29#include "rtc_base/base64.h"
30#include "rtc_base/checks.h"
31#include "rtc_base/helpers.h"
32#include "rtc_base/logging.h"
33#include "rtc_base/stringutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000034
35namespace {
36const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080037
deadbeef7914b8c2017-04-21 03:23:33 -070038void GetSupportedSdesCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
39 std::vector<int>*),
40 const rtc::CryptoOptions& crypto_options,
41 std::vector<std::string>* names) {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080042 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070043 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080044 for (const auto crypto : crypto_suites) {
45 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
46 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080047}
terelius8c011e52016-04-26 05:28:11 -070048} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000049
50namespace cricket {
51
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052// RTP Profile names
53// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
54// RFC4585
55const char kMediaProtocolAvpf[] = "RTP/AVPF";
56// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000057const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
58
deadbeeff3938292015-07-15 12:20:53 -070059// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
60// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061const char kMediaProtocolSavpf[] = "RTP/SAVPF";
62
63const char kMediaProtocolRtpPrefix[] = "RTP/";
64
65const char kMediaProtocolSctp[] = "SCTP";
66const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000067const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000068const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000069
deadbeef8b7e9ad2017-05-25 09:38:55 -070070// Note that the below functions support some protocol strings purely for
71// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
72// and Interoperability.
73
74static bool IsDtlsRtp(const std::string& protocol) {
75 // Most-likely values first.
76 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
77 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
78}
79
80static bool IsPlainRtp(const std::string& protocol) {
81 // Most-likely values first.
82 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
83 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
84}
85
86static bool IsDtlsSctp(const std::string& protocol) {
87 return protocol == kMediaProtocolDtlsSctp ||
88 protocol == kMediaProtocolUdpDtlsSctp ||
89 protocol == kMediaProtocolTcpDtlsSctp;
90}
91
92static bool IsPlainSctp(const std::string& protocol) {
93 return protocol == kMediaProtocolSctp;
94}
95
96static bool IsSctp(const std::string& protocol) {
97 return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
98}
99
ossu075af922016-06-14 03:29:38 -0700100RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
101 MediaContentDirection md) {
102 const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
103 const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
104 return RtpTransceiverDirection(send, recv);
105}
106
107MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
108 if (send && recv) {
109 return MD_SENDRECV;
110 } else if (send) {
111 return MD_SENDONLY;
112 } else if (recv) {
113 return MD_RECVONLY;
114 }
115
116 return MD_INACTIVE;
117}
118
119RtpTransceiverDirection
120NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
121 RtpTransceiverDirection wants) {
122 return RtpTransceiverDirection(offer.recv && wants.send,
123 offer.send && wants.recv);
124}
125
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126static bool IsMediaContentOfType(const ContentInfo* content,
127 MediaType media_type) {
128 if (!IsMediaContent(content)) {
129 return false;
130 }
131
132 const MediaContentDescription* mdesc =
133 static_cast<const MediaContentDescription*>(content->description);
134 return mdesc && mdesc->type() == media_type;
135}
136
137static bool CreateCryptoParams(int tag, const std::string& cipher,
138 CryptoParams *out) {
jbauchcb560652016-08-04 05:20:32 -0700139 int key_len;
140 int salt_len;
141 if (!rtc::GetSrtpKeyAndSaltLengths(
142 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143 return false;
144 }
jbauchcb560652016-08-04 05:20:32 -0700145
146 int master_key_len = key_len + salt_len;
147 std::string master_key;
148 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
149 return false;
150 }
151
kwiberg352444f2016-11-28 15:58:53 -0800152 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700153 std::string key = rtc::Base64::Encode(master_key);
154
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155 out->tag = tag;
156 out->cipher_suite = cipher;
157 out->key_params = kInline;
158 out->key_params += key;
159 return true;
160}
161
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162static bool AddCryptoParams(const std::string& cipher_suite,
163 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000164 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165
166 out->resize(size + 1);
167 return CreateCryptoParams(size, cipher_suite, &out->at(size));
168}
169
170void AddMediaCryptos(const CryptoParamsVec& cryptos,
171 MediaContentDescription* media) {
172 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
173 crypto != cryptos.end(); ++crypto) {
174 media->AddCrypto(*crypto);
175 }
176}
177
178bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
179 MediaContentDescription* media) {
180 CryptoParamsVec cryptos;
181 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
182 it != crypto_suites.end(); ++it) {
183 if (!AddCryptoParams(*it, &cryptos)) {
184 return false;
185 }
186 }
187 AddMediaCryptos(cryptos, media);
188 return true;
189}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190
zhihuang1c378ed2017-08-17 14:10:50 -0700191const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
192 if (!content) {
193 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194 }
zhihuang1c378ed2017-08-17 14:10:50 -0700195
196 RTC_DCHECK(IsMediaContent(content));
197 return &(static_cast<const MediaContentDescription*>(content->description)
198 ->cryptos());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199}
200
201bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
202 const CryptoParams& crypto,
203 CryptoParams* out) {
204 for (CryptoParamsVec::const_iterator it = cryptos.begin();
205 it != cryptos.end(); ++it) {
206 if (crypto.Matches(*it)) {
207 *out = *it;
208 return true;
209 }
210 }
211 return false;
212}
213
jbauchcb560652016-08-04 05:20:32 -0700214// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
deadbeef7914b8c2017-04-21 03:23:33 -0700215void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
216 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700217 if (crypto_options.enable_gcm_crypto_suites) {
218 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
219 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
220 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800221 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
222 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000223}
224
deadbeef7914b8c2017-04-21 03:23:33 -0700225void GetSupportedAudioSdesCryptoSuiteNames(
226 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800227 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700228 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
229 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000230}
231
deadbeef7914b8c2017-04-21 03:23:33 -0700232void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
233 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700234 if (crypto_options.enable_gcm_crypto_suites) {
235 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
236 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
237 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800238 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000239}
240
deadbeef7914b8c2017-04-21 03:23:33 -0700241void GetSupportedVideoSdesCryptoSuiteNames(
242 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800243 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700244 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
245 crypto_options, crypto_suite_names);
246}
247
248void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
249 std::vector<int>* crypto_suites) {
250 if (crypto_options.enable_gcm_crypto_suites) {
251 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
252 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
253 }
254 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
255}
256
257void GetSupportedDataSdesCryptoSuiteNames(
258 const rtc::CryptoOptions& crypto_options,
259 std::vector<std::string>* crypto_suite_names) {
260 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
261 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800262}
263
jbauchcb560652016-08-04 05:20:32 -0700264// Support any GCM cipher (if enabled through options). For video support only
265// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
266// because it is low overhead.
267// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268static bool SelectCrypto(const MediaContentDescription* offer,
269 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700270 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000271 CryptoParams *crypto) {
272 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
273 const CryptoParamsVec& cryptos = offer->cryptos();
274
275 for (CryptoParamsVec::const_iterator i = cryptos.begin();
276 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700277 if ((crypto_options.enable_gcm_crypto_suites &&
278 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
279 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700280 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
281 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
283 }
284 }
285 return false;
286}
287
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000289// The generated values are added to |ssrcs|.
290// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000292 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200293 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000294 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200295 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000297 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000298 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000299 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
300 ssrcs->push_back(candidate);
301 }
302}
303
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000304// Finds all StreamParams of all media types and attach them to stream_params.
305static void GetCurrentStreamParams(const SessionDescription* sdesc,
306 StreamParamsVec* stream_params) {
307 if (!sdesc)
308 return;
309
310 const ContentInfos& contents = sdesc->contents();
311 for (ContentInfos::const_iterator content = contents.begin();
312 content != contents.end(); ++content) {
313 if (!IsMediaContent(&*content)) {
314 continue;
315 }
316 const MediaContentDescription* media =
317 static_cast<const MediaContentDescription*>(
318 content->description);
319 const StreamParamsVec& streams = media->streams();
320 for (StreamParamsVec::const_iterator it = streams.begin();
321 it != streams.end(); ++it) {
322 stream_params->push_back(*it);
323 }
324 }
325}
326
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000327// Filters the data codecs for the data channel type.
328void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
329 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700330 const char* codec_name =
331 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000332 for (std::vector<DataCodec>::iterator iter = codecs->begin();
333 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700334 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000335 iter = codecs->erase(iter);
336 } else {
337 ++iter;
338 }
339 }
340}
341
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342template <typename IdStruct>
343class UsedIds {
344 public:
345 UsedIds(int min_allowed_id, int max_allowed_id)
346 : min_allowed_id_(min_allowed_id),
347 max_allowed_id_(max_allowed_id),
348 next_id_(max_allowed_id) {
349 }
350
351 // Loops through all Id in |ids| and changes its id if it is
352 // already in use by another IdStruct. Call this methods with all Id
353 // in a session description to make sure no duplicate ids exists.
354 // Note that typename Id must be a type of IdStruct.
355 template <typename Id>
356 void FindAndSetIdUsed(std::vector<Id>* ids) {
357 for (typename std::vector<Id>::iterator it = ids->begin();
358 it != ids->end(); ++it) {
359 FindAndSetIdUsed(&*it);
360 }
361 }
362
363 // Finds and sets an unused id if the |idstruct| id is already in use.
364 void FindAndSetIdUsed(IdStruct* idstruct) {
365 const int original_id = idstruct->id;
366 int new_id = idstruct->id;
367
368 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
369 // If the original id is not in range - this is an id that can't be
370 // dynamically changed.
371 return;
372 }
373
374 if (IsIdUsed(original_id)) {
375 new_id = FindUnusedId();
376 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
377 << " to " << new_id;
378 idstruct->id = new_id;
379 }
380 SetIdUsed(new_id);
381 }
382
383 private:
384 // Returns the first unused id in reverse order.
385 // This hopefully reduce the risk of more collisions. We want to change the
386 // default ids as little as possible.
387 int FindUnusedId() {
388 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
389 --next_id_;
390 }
nisseede5da42017-01-12 05:15:36 -0800391 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000392 return next_id_;
393 }
394
395 bool IsIdUsed(int new_id) {
396 return id_set_.find(new_id) != id_set_.end();
397 }
398
399 void SetIdUsed(int new_id) {
400 id_set_.insert(new_id);
401 }
402
403 const int min_allowed_id_;
404 const int max_allowed_id_;
405 int next_id_;
406 std::set<int> id_set_;
407};
408
409// Helper class used for finding duplicate RTP payload types among audio, video
410// and data codecs. When bundle is used the payload types may not collide.
411class UsedPayloadTypes : public UsedIds<Codec> {
412 public:
413 UsedPayloadTypes()
414 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
415 }
416
417
418 private:
419 static const int kDynamicPayloadTypeMin = 96;
420 static const int kDynamicPayloadTypeMax = 127;
421};
422
423// Helper class used for finding duplicate RTP Header extension ids among
424// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700425class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000426 public:
427 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-25 18:15:09 -0800428 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
429 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000430
431 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432};
433
zhihuang1c378ed2017-08-17 14:10:50 -0700434// Adds a StreamParams for each SenderOptions in |sender_options| to
435// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436// |current_params| - All currently known StreamParams of any media type.
437template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700438static bool AddStreamParams(
439 const std::vector<SenderOptions>& sender_options,
440 const std::string& rtcp_cname,
441 StreamParamsVec* current_streams,
442 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700443 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 09:38:55 -0700444 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700445 return true;
446 }
447
Noah Richards2e7a0982015-05-18 14:02:54 -0700448 const bool include_rtx_streams =
449 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000451
brandtr03d5fb12016-11-22 03:37:59 -0800452 const bool include_flexfec_stream =
453 ContainsFlexfecCodec(content_description->codecs());
454
zhihuang1c378ed2017-08-17 14:10:50 -0700455 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000456 // groupid is empty for StreamParams generated using
457 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700458 StreamParams* param =
459 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000460 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700461 // This is a new sender.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200462 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 14:10:50 -0700463 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464 StreamParams stream_param;
zhihuang1c378ed2017-08-17 14:10:50 -0700465 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000466 // Add the generated ssrc.
467 for (size_t i = 0; i < ssrcs.size(); ++i) {
468 stream_param.ssrcs.push_back(ssrcs[i]);
469 }
zhihuang1c378ed2017-08-17 14:10:50 -0700470 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000471 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
472 stream_param.ssrc_groups.push_back(group);
473 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700474 // Generate extra ssrcs for include_rtx_streams case.
475 if (include_rtx_streams) {
476 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200477 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700478 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
479 &rtx_ssrcs);
480 for (size_t i = 0; i < ssrcs.size(); ++i) {
481 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
482 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483 content_description->set_multistream(true);
484 }
brandtr03d5fb12016-11-22 03:37:59 -0800485 // Generate extra ssrc for include_flexfec_stream case.
486 if (include_flexfec_stream) {
487 // TODO(brandtr): Update when we support multistream protection.
488 if (ssrcs.size() == 1) {
489 std::vector<uint32_t> flexfec_ssrcs;
490 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
491 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
492 content_description->set_multistream(true);
493 } else if (!ssrcs.empty()) {
494 LOG(LS_WARNING)
495 << "Our FlexFEC implementation only supports protecting "
496 << "a single media streams. This session has multiple "
497 << "media streams however, so no FlexFEC SSRC will be generated.";
498 }
499 }
zhihuang1c378ed2017-08-17 14:10:50 -0700500 stream_param.cname = rtcp_cname;
Steve Anton8ffb9c32017-08-31 15:45:38 -0700501 // TODO(steveanton): Support any number of stream ids.
502 RTC_CHECK(sender.stream_ids.size() == 1U);
503 stream_param.sync_label = sender.stream_ids[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000504 content_description->AddStream(stream_param);
505
506 // Store the new StreamParams in current_streams.
507 // This is necessary so that we can use the CNAME for other media types.
508 current_streams->push_back(stream_param);
509 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700510 // Use existing generated SSRCs/groups, but update the sync_label if
511 // necessary. This may be needed if a MediaStreamTrack was moved from one
512 // MediaStream to another.
Steve Anton8ffb9c32017-08-31 15:45:38 -0700513 // TODO(steveanton): Support any number of stream ids.
514 RTC_CHECK(sender.stream_ids.size() == 1U);
515 param->sync_label = sender.stream_ids[0];
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000516 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 }
518 }
519 return true;
520}
521
522// Updates the transport infos of the |sdesc| according to the given
523// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800524// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
525// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
527 SessionDescription* sdesc) {
528 // The bundle should not be empty.
529 if (!sdesc || !bundle_group.FirstContentName()) {
530 return false;
531 }
532
533 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700534 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000535 const TransportInfo* selected_transport_info =
536 sdesc->GetTransportInfoByName(selected_content_name);
537 if (!selected_transport_info) {
538 return false;
539 }
540
541 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700542 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000543 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700544 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000545 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800546 ConnectionRole selected_connection_role =
547 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548 for (TransportInfos::iterator it =
549 sdesc->transport_infos().begin();
550 it != sdesc->transport_infos().end(); ++it) {
551 if (bundle_group.HasContentName(it->content_name) &&
552 it->content_name != selected_content_name) {
553 it->description.ice_ufrag = selected_ufrag;
554 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800555 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000556 }
557 }
558 return true;
559}
560
561// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
562// sets it to |cryptos|.
563static bool GetCryptosByName(const SessionDescription* sdesc,
564 const std::string& content_name,
565 CryptoParamsVec* cryptos) {
566 if (!sdesc || !cryptos) {
567 return false;
568 }
569
570 const ContentInfo* content = sdesc->GetContentByName(content_name);
571 if (!IsMediaContent(content) || !content->description) {
572 return false;
573 }
574
575 const MediaContentDescription* media_desc =
576 static_cast<const MediaContentDescription*>(content->description);
577 *cryptos = media_desc->cryptos();
578 return true;
579}
580
581// Predicate function used by the remove_if.
582// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
583static bool CryptoNotFound(const CryptoParams crypto,
584 const CryptoParamsVec* filter) {
585 if (filter == NULL) {
586 return true;
587 }
588 for (CryptoParamsVec::const_iterator it = filter->begin();
589 it != filter->end(); ++it) {
590 if (it->cipher_suite == crypto.cipher_suite) {
591 return false;
592 }
593 }
594 return true;
595}
596
597// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
598// which are not available in |filter|.
599static void PruneCryptos(const CryptoParamsVec& filter,
600 CryptoParamsVec* target_cryptos) {
601 if (!target_cryptos) {
602 return;
603 }
604 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
605 target_cryptos->end(),
606 bind2nd(ptr_fun(CryptoNotFound),
607 &filter)),
608 target_cryptos->end());
609}
610
deadbeefb5cb19b2015-11-23 16:39:12 -0800611static bool IsRtpProtocol(const std::string& protocol) {
612 return protocol.empty() ||
613 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
614}
615
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000616static bool IsRtpContent(SessionDescription* sdesc,
617 const std::string& content_name) {
618 bool is_rtp = false;
619 ContentInfo* content = sdesc->GetContentByName(content_name);
620 if (IsMediaContent(content)) {
621 MediaContentDescription* media_desc =
622 static_cast<MediaContentDescription*>(content->description);
623 if (!media_desc) {
624 return false;
625 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800626 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000627 }
628 return is_rtp;
629}
630
631// Updates the crypto parameters of the |sdesc| according to the given
632// |bundle_group|. The crypto parameters of all the contents within the
633// |bundle_group| should be updated to use the common subset of the
634// available cryptos.
635static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
636 SessionDescription* sdesc) {
637 // The bundle should not be empty.
638 if (!sdesc || !bundle_group.FirstContentName()) {
639 return false;
640 }
641
wu@webrtc.org78187522013-10-07 23:32:02 +0000642 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000643 // Get the common cryptos.
644 const ContentNames& content_names = bundle_group.content_names();
645 CryptoParamsVec common_cryptos;
646 for (ContentNames::const_iterator it = content_names.begin();
647 it != content_names.end(); ++it) {
648 if (!IsRtpContent(sdesc, *it)) {
649 continue;
650 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000651 // The common cryptos are needed if any of the content does not have DTLS
652 // enabled.
653 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
654 common_cryptos_needed = true;
655 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000656 if (it == content_names.begin()) {
657 // Initial the common_cryptos with the first content in the bundle group.
658 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
659 return false;
660 }
661 if (common_cryptos.empty()) {
662 // If there's no crypto params, we should just return.
663 return true;
664 }
665 } else {
666 CryptoParamsVec cryptos;
667 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
668 return false;
669 }
670 PruneCryptos(cryptos, &common_cryptos);
671 }
672 }
673
wu@webrtc.org78187522013-10-07 23:32:02 +0000674 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675 return false;
676 }
677
678 // Update to use the common cryptos.
679 for (ContentNames::const_iterator it = content_names.begin();
680 it != content_names.end(); ++it) {
681 if (!IsRtpContent(sdesc, *it)) {
682 continue;
683 }
684 ContentInfo* content = sdesc->GetContentByName(*it);
685 if (IsMediaContent(content)) {
686 MediaContentDescription* media_desc =
687 static_cast<MediaContentDescription*>(content->description);
688 if (!media_desc) {
689 return false;
690 }
691 media_desc->set_cryptos(common_cryptos);
692 }
693 }
694 return true;
695}
696
697template <class C>
698static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800699 for (const auto& codec : codecs) {
700 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000701 return true;
702 }
703 }
704 return false;
705}
706
707template <class C>
708static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800709 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000710}
711
brandtr03d5fb12016-11-22 03:37:59 -0800712template <class C>
713static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
714 for (const auto& codec : codecs) {
715 if (IsFlexfecCodec(codec)) {
716 return true;
717 }
718 }
719 return false;
720}
721
722template <class C>
723static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800724 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800725}
726
zhihuang1c378ed2017-08-17 14:10:50 -0700727// Create a media content to be offered for the given |sender_options|,
728// according to the given options.rtcp_mux, session_options.is_muc, codecs,
729// secure_transport, crypto, and current_streams. If we don't currently have
730// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
731// created (according to crypto_suites). The created content is added to the
732// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733template <class C>
734static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -0700735 const std::vector<SenderOptions>& sender_options,
736 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000737 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000738 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000739 const CryptoParamsVec* current_cryptos,
740 const std::vector<std::string>& crypto_suites,
741 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000742 StreamParamsVec* current_streams,
743 MediaContentDescriptionImpl<C>* offer) {
744 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745
zhihuang1c378ed2017-08-17 14:10:50 -0700746 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700747 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
748 offer->set_rtcp_reduced_size(true);
749 }
zhihuang1c378ed2017-08-17 14:10:50 -0700750 offer->set_multistream(session_options.is_muc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000751 offer->set_rtp_header_extensions(rtp_extensions);
752
zhihuang1c378ed2017-08-17 14:10:50 -0700753 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
754 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000755 return false;
756 }
757
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000758 if (secure_policy != SEC_DISABLED) {
759 if (current_cryptos) {
760 AddMediaCryptos(*current_cryptos, offer);
761 }
762 if (offer->cryptos().empty()) {
763 if (!CreateMediaCryptos(crypto_suites, offer)) {
764 return false;
765 }
766 }
767 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000768
deadbeef7af91dd2016-12-13 11:29:11 -0800769 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000770 return false;
771 }
772 return true;
773}
774
775template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000776static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800777 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000778 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800779 const int codec2_id) {
780 const C* codec1 = FindCodecById(codecs1, codec1_id);
781 const C* codec2 = FindCodecById(codecs2, codec2_id);
782 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000783}
784
785template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000786static void NegotiateCodecs(const std::vector<C>& local_codecs,
787 const std::vector<C>& offered_codecs,
788 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800789 for (const C& ours : local_codecs) {
790 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700791 // Note that we intentionally only find one matching codec for each of our
792 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800793 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
794 C negotiated = ours;
795 negotiated.IntersectFeedbackParams(theirs);
796 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800797 const auto apt_it =
798 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800799 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800800 RTC_DCHECK(apt_it != theirs.params.end());
801 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000802 }
magjedf823ede2016-11-12 09:53:04 -0800803 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
804 webrtc::H264::GenerateProfileLevelIdForAnswer(
805 ours.params, theirs.params, &negotiated.params);
806 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800807 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700808 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800809 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000810 }
811 }
deadbeef67cf2c12016-04-13 10:07:16 -0700812 // RFC3264: Although the answerer MAY list the formats in their desired
813 // order of preference, it is RECOMMENDED that unless there is a
814 // specific reason, the answerer list formats in the same relative order
815 // they were present in the offer.
816 std::unordered_map<int, int> payload_type_preferences;
817 int preference = static_cast<int>(offered_codecs.size() + 1);
818 for (const C& codec : offered_codecs) {
819 payload_type_preferences[codec.id] = preference--;
820 }
821 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
822 [&payload_type_preferences](const C& a, const C& b) {
823 return payload_type_preferences[a.id] >
824 payload_type_preferences[b.id];
825 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000826}
827
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800828// Finds a codec in |codecs2| that matches |codec_to_match|, which is
829// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
830// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000831template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800832static bool FindMatchingCodec(const std::vector<C>& codecs1,
833 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000834 const C& codec_to_match,
835 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700836 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
837 // codecs' associated codecs correctly. If not, that's a programming error.
838 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
839 [&codec_to_match](const C& codec) {
840 return &codec == &codec_to_match;
841 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800842 for (const C& potential_match : codecs2) {
843 if (potential_match.Matches(codec_to_match)) {
844 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800845 int apt_value_1 = 0;
846 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800847 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
848 &apt_value_1) ||
849 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
850 &apt_value_2)) {
851 LOG(LS_WARNING) << "RTX missing associated payload type.";
852 continue;
853 }
854 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
855 apt_value_2)) {
856 continue;
857 }
858 }
859 if (found_codec) {
860 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000861 }
862 return true;
863 }
864 }
865 return false;
866}
867
zhihuang1c378ed2017-08-17 14:10:50 -0700868// Find the codec in |codec_list| that |rtx_codec| is associated with.
869template <class C>
870static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
871 const C& rtx_codec) {
872 std::string associated_pt_str;
873 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
874 &associated_pt_str)) {
875 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
876 << " is missing an associated payload type.";
877 return nullptr;
878 }
879
880 int associated_pt;
881 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
882 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
883 << " of RTX codec " << rtx_codec.name << " to an integer.";
884 return nullptr;
885 }
886
887 // Find the associated reference codec for the reference RTX codec.
888 const C* associated_codec = FindCodecById(codec_list, associated_pt);
889 if (!associated_codec) {
890 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
891 << associated_pt << " for RTX codec " << rtx_codec.name
892 << ".";
893 }
894 return associated_codec;
895}
896
897// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000898// already exist in |offered_codecs| and ensure the payload types don't
899// collide.
900template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700901static void MergeCodecs(const std::vector<C>& reference_codecs,
902 std::vector<C>* offered_codecs,
903 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000904 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800905 for (const C& reference_codec : reference_codecs) {
906 if (!IsRtxCodec(reference_codec) &&
907 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
908 reference_codec, nullptr)) {
909 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 used_pltypes->FindAndSetIdUsed(&codec);
911 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000912 }
913 }
914
915 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800916 for (const C& reference_codec : reference_codecs) {
917 if (IsRtxCodec(reference_codec) &&
918 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
919 reference_codec, nullptr)) {
920 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700921 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700922 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700923 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700924 continue;
925 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800926 // Find a codec in the offered list that matches the reference codec.
927 // Its payload type may be different than the reference codec.
928 C matching_codec;
929 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800930 *associated_codec, &matching_codec)) {
931 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800932 << " codec.";
933 continue;
934 }
935
936 rtx_codec.params[kCodecParamAssociatedPayloadType] =
937 rtc::ToString(matching_codec.id);
938 used_pltypes->FindAndSetIdUsed(&rtx_codec);
939 offered_codecs->push_back(rtx_codec);
940 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000941 }
942}
943
zhihuang1c378ed2017-08-17 14:10:50 -0700944static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
945 const webrtc::RtpExtension& ext_to_match,
946 webrtc::RtpExtension* found_extension) {
947 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
948 it != extensions.end(); ++it) {
949 // We assume that all URIs are given in a canonical format.
950 if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
951 if (found_extension) {
952 *found_extension = *it;
953 }
954 return true;
955 }
956 }
957 return false;
958}
959
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000960static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700961 const webrtc::RtpExtension& ext_to_match,
962 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -0700963 // We assume that all URIs are given in a canonical format.
964 const webrtc::RtpExtension* found =
965 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
966 ext_to_match.uri);
967 if (!found) {
968 return false;
969 }
970 if (found_extension) {
971 *found_extension = *found;
972 }
973 return true;
974}
975
976static bool FindByUriWithEncryptionPreference(
977 const RtpHeaderExtensions& extensions,
978 const webrtc::RtpExtension& ext_to_match, bool encryption_preference,
979 webrtc::RtpExtension* found_extension) {
980 const webrtc::RtpExtension* unencrypted_extension = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000981 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
982 it != extensions.end(); ++it) {
983 // We assume that all URIs are given in a canonical format.
984 if (it->uri == ext_to_match.uri) {
jbauch5869f502017-06-29 12:31:36 -0700985 if (!encryption_preference || it->encrypt) {
986 if (found_extension) {
987 *found_extension = *it;
988 }
989 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000990 }
jbauch5869f502017-06-29 12:31:36 -0700991 unencrypted_extension = &(*it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 }
993 }
jbauch5869f502017-06-29 12:31:36 -0700994 if (unencrypted_extension) {
995 if (found_extension) {
996 *found_extension = *unencrypted_extension;
997 }
998 return true;
999 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001000 return false;
1001}
1002
zhihuang1c378ed2017-08-17 14:10:50 -07001003// Adds all extensions from |reference_extensions| to |offered_extensions| that
1004// don't already exist in |offered_extensions| and ensure the IDs don't
1005// collide. If an extension is added, it's also added to |regular_extensions| or
1006// |encrypted_extensions|, and if the extension is in |regular_extensions| or
1007// |encrypted_extensions|, its ID is marked as used in |used_ids|.
1008// |offered_extensions| is for either audio or video while |regular_extensions|
1009// and |encrypted_extensions| are used for both audio and video. There could be
1010// overlap between audio extensions and video extensions.
1011static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1012 RtpHeaderExtensions* offered_extensions,
1013 RtpHeaderExtensions* regular_extensions,
1014 RtpHeaderExtensions* encrypted_extensions,
1015 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -07001016 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -07001017 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
1018 nullptr)) {
olka3c747662017-08-17 06:50:32 -07001019 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -07001020 if (reference_extension.encrypt) {
1021 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
1022 &existing)) {
1023 offered_extensions->push_back(existing);
1024 } else {
1025 used_ids->FindAndSetIdUsed(&reference_extension);
1026 encrypted_extensions->push_back(reference_extension);
1027 offered_extensions->push_back(reference_extension);
1028 }
olka3c747662017-08-17 06:50:32 -07001029 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07001030 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
1031 &existing)) {
1032 offered_extensions->push_back(existing);
1033 } else {
1034 used_ids->FindAndSetIdUsed(&reference_extension);
1035 regular_extensions->push_back(reference_extension);
1036 offered_extensions->push_back(reference_extension);
1037 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001038 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039 }
1040 }
1041}
1042
jbauch5869f502017-06-29 12:31:36 -07001043static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1044 RtpHeaderExtensions* all_extensions,
1045 UsedRtpHeaderExtensionIds* used_ids) {
1046 RtpHeaderExtensions encrypted_extensions;
1047 for (const webrtc::RtpExtension& extension : *extensions) {
1048 webrtc::RtpExtension existing;
1049 // Don't add encrypted extensions again that were already included in a
1050 // previous offer or regular extensions that are also included as encrypted
1051 // extensions.
1052 if (extension.encrypt ||
1053 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1054 (FindByUriWithEncryptionPreference(*extensions, extension, true,
1055 &existing) && existing.encrypt)) {
1056 continue;
1057 }
1058
1059 if (FindByUri(*all_extensions, extension, &existing)) {
1060 encrypted_extensions.push_back(existing);
1061 } else {
1062 webrtc::RtpExtension encrypted(extension);
1063 encrypted.encrypt = true;
1064 used_ids->FindAndSetIdUsed(&encrypted);
1065 all_extensions->push_back(encrypted);
1066 encrypted_extensions.push_back(encrypted);
1067 }
1068 }
1069 extensions->insert(extensions->end(), encrypted_extensions.begin(),
1070 encrypted_extensions.end());
1071}
1072
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001073static void NegotiateRtpHeaderExtensions(
1074 const RtpHeaderExtensions& local_extensions,
1075 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001076 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001077 RtpHeaderExtensions* negotiated_extenstions) {
1078 RtpHeaderExtensions::const_iterator ours;
1079 for (ours = local_extensions.begin();
1080 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001081 webrtc::RtpExtension theirs;
jbauch5869f502017-06-29 12:31:36 -07001082 if (FindByUriWithEncryptionPreference(offered_extensions, *ours,
1083 enable_encrypted_rtp_header_extensions, &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001084 // We respond with their RTP header extension id.
1085 negotiated_extenstions->push_back(theirs);
1086 }
1087 }
1088}
1089
1090static void StripCNCodecs(AudioCodecs* audio_codecs) {
1091 AudioCodecs::iterator iter = audio_codecs->begin();
1092 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001093 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094 iter = audio_codecs->erase(iter);
1095 } else {
1096 ++iter;
1097 }
1098 }
1099}
1100
zhihuang1c378ed2017-08-17 14:10:50 -07001101// Create a media content to be answered for the given |sender_options|
1102// according to the given session_options.rtcp_mux, session_options.streams,
1103// codecs, crypto, and current_streams. If we don't currently have crypto (in
1104// current_cryptos) and it is enabled (in secure_policy), crypto is created
1105// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1106// negotiated with the offer. If the negotiation fails, this method returns
1107// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108template <class C>
1109static bool CreateMediaContentAnswer(
1110 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001111 const MediaDescriptionOptions& media_description_options,
1112 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001113 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001114 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001115 const CryptoParamsVec* current_cryptos,
1116 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 12:31:36 -07001117 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001118 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001119 bool bundle_enabled,
1120 MediaContentDescriptionImpl<C>* answer) {
1121 std::vector<C> negotiated_codecs;
1122 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1123 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001124 answer->set_protocol(offer->protocol());
1125 RtpHeaderExtensions negotiated_rtp_extensions;
1126 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1127 offer->rtp_header_extensions(),
jbauch5869f502017-06-29 12:31:36 -07001128 enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001129 &negotiated_rtp_extensions);
1130 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1131
zhihuang1c378ed2017-08-17 14:10:50 -07001132 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001133 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1134 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1135 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001136
1137 if (sdes_policy != SEC_DISABLED) {
1138 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001139 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1140 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141 if (current_cryptos) {
1142 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1143 }
1144 answer->AddCrypto(crypto);
1145 }
1146 }
1147
deadbeef7af91dd2016-12-13 11:29:11 -08001148 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001149 return false;
1150 }
1151
zhihuang1c378ed2017-08-17 14:10:50 -07001152 if (!AddStreamParams(media_description_options.sender_options,
1153 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001154 return false; // Something went seriously wrong.
1155 }
1156
ossu075af922016-06-14 03:29:38 -07001157 auto offer_rtd =
1158 RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
zhihuang1c378ed2017-08-17 14:10:50 -07001159
1160 answer->set_direction(NegotiateRtpTransceiverDirection(
1161 offer_rtd, media_description_options.direction)
1162 .ToMediaContentDirection());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001163 return true;
1164}
1165
1166static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001167 const std::string& protocol,
1168 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001169 // Since not all applications serialize and deserialize the media protocol,
1170 // we will have to accept |protocol| to be empty.
1171 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001172 return true;
1173 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001174
zhihuangcf5b37c2016-05-05 11:44:35 -07001175 if (type == MEDIA_TYPE_DATA) {
1176 // Check for SCTP, but also for RTP for RTP-based data channels.
1177 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1178 if (secure_transport) {
1179 // Most likely scenarios first.
1180 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1181 IsPlainRtp(protocol);
1182 } else {
1183 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1184 }
1185 }
1186
1187 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1188 // JSEP specifies.
1189 if (secure_transport) {
1190 // Most likely scenarios first.
1191 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1192 } else {
1193 return IsPlainRtp(protocol);
1194 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001195}
1196
1197static void SetMediaProtocol(bool secure_transport,
1198 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001199 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001200 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001201 else if (secure_transport)
1202 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001203 else
1204 desc->set_protocol(kMediaProtocolAvpf);
1205}
1206
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001207// Gets the TransportInfo of the given |content_name| from the
1208// |current_description|. If doesn't exist, returns a new one.
1209static const TransportDescription* GetTransportDescription(
1210 const std::string& content_name,
1211 const SessionDescription* current_description) {
1212 const TransportDescription* desc = NULL;
1213 if (current_description) {
1214 const TransportInfo* info =
1215 current_description->GetTransportInfoByName(content_name);
1216 if (info) {
1217 desc = &info->description;
1218 }
1219 }
1220 return desc;
1221}
1222
1223// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001224static bool IsDtlsActive(const ContentInfo* content,
1225 const SessionDescription* current_description) {
1226 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001227 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001228 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001229
zhihuang1c378ed2017-08-17 14:10:50 -07001230 size_t msection_index = content - &current_description->contents()[0];
1231
1232 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001233 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001234 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001235
zhihuang1c378ed2017-08-17 14:10:50 -07001236 return current_description->transport_infos()[msection_index]
1237 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001238}
1239
ossu075af922016-06-14 03:29:38 -07001240std::string MediaContentDirectionToString(MediaContentDirection direction) {
1241 std::string dir_str;
1242 switch (direction) {
1243 case MD_INACTIVE:
1244 dir_str = "inactive";
1245 break;
1246 case MD_SENDONLY:
1247 dir_str = "sendonly";
1248 break;
1249 case MD_RECVONLY:
1250 dir_str = "recvonly";
1251 break;
1252 case MD_SENDRECV:
1253 dir_str = "sendrecv";
1254 break;
1255 default:
nissec80e7412017-01-11 05:56:46 -08001256 RTC_NOTREACHED();
ossu075af922016-06-14 03:29:38 -07001257 break;
1258 }
1259
1260 return dir_str;
1261}
1262
Steve Anton8ffb9c32017-08-31 15:45:38 -07001263void MediaDescriptionOptions::AddAudioSender(
1264 const std::string& track_id,
1265 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001266 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001267 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001268}
1269
Steve Anton8ffb9c32017-08-31 15:45:38 -07001270void MediaDescriptionOptions::AddVideoSender(
1271 const std::string& track_id,
1272 const std::vector<std::string>& stream_ids,
1273 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001274 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001275 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001276}
1277
zhihuang1c378ed2017-08-17 14:10:50 -07001278void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1279 const std::string& stream_id) {
1280 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001281 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1282 // than one stream?
1283 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001284}
1285
Steve Anton8ffb9c32017-08-31 15:45:38 -07001286void MediaDescriptionOptions::AddSenderInternal(
1287 const std::string& track_id,
1288 const std::vector<std::string>& stream_ids,
1289 int num_sim_layers) {
1290 // TODO(steveanton): Support any number of stream ids.
1291 RTC_CHECK(stream_ids.size() == 1U);
1292 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293}
1294
zhihuang1c378ed2017-08-17 14:10:50 -07001295bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1296 return std::find_if(media_description_options.begin(),
1297 media_description_options.end(),
1298 [type](const MediaDescriptionOptions& t) {
1299 return t.type == type;
1300 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001301}
1302
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001303MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1304 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001305 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001306
1307MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1308 ChannelManager* channel_manager,
1309 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001310 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001311 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1312 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001313 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001314 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001315 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1316 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001317 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001318}
1319
ossudedfd282016-06-14 07:12:39 -07001320const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1321 const {
ossu075af922016-06-14 03:29:38 -07001322 return audio_sendrecv_codecs_;
1323}
1324
1325const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1326 return audio_send_codecs_;
1327}
1328
1329const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1330 return audio_recv_codecs_;
1331}
1332
1333void MediaSessionDescriptionFactory::set_audio_codecs(
1334 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1335 audio_send_codecs_ = send_codecs;
1336 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001337 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001338}
1339
1340SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001341 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001342 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001343 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001344
1345 StreamParamsVec current_streams;
1346 GetCurrentStreamParams(current_description, &current_streams);
1347
zhihuang1c378ed2017-08-17 14:10:50 -07001348 AudioCodecs offer_audio_codecs;
1349 VideoCodecs offer_video_codecs;
1350 DataCodecs offer_data_codecs;
1351 GetCodecsForOffer(current_description, &offer_audio_codecs,
1352 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001353
zhihuang1c378ed2017-08-17 14:10:50 -07001354 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001355 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001356 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001357 }
zhihuang1c378ed2017-08-17 14:10:50 -07001358 FilterDataCodecs(&offer_data_codecs,
1359 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001360
1361 RtpHeaderExtensions audio_rtp_extensions;
1362 RtpHeaderExtensions video_rtp_extensions;
1363 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1364 &video_rtp_extensions);
1365
zhihuang1c378ed2017-08-17 14:10:50 -07001366 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001367 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001368 RTC_DCHECK(current_description->contents().size() <=
1369 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001370 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001371
zhihuang1c378ed2017-08-17 14:10:50 -07001372 // Iterate through the media description options, matching with existing media
1373 // descriptions in |current_description|.
1374 int msection_index = 0;
1375 for (const MediaDescriptionOptions& media_description_options :
1376 session_options.media_description_options) {
1377 const ContentInfo* current_content = nullptr;
1378 if (current_description &&
1379 msection_index <
1380 static_cast<int>(current_description->contents().size())) {
1381 current_content = &current_description->contents()[msection_index];
1382 // Media type must match.
1383 RTC_DCHECK(IsMediaContentOfType(current_content,
1384 media_description_options.type));
1385 }
1386 switch (media_description_options.type) {
1387 case MEDIA_TYPE_AUDIO:
1388 if (!AddAudioContentForOffer(media_description_options, session_options,
1389 current_content, current_description,
1390 audio_rtp_extensions, offer_audio_codecs,
1391 &current_streams, offer.get())) {
1392 return nullptr;
1393 }
1394 break;
1395 case MEDIA_TYPE_VIDEO:
1396 if (!AddVideoContentForOffer(media_description_options, session_options,
1397 current_content, current_description,
1398 video_rtp_extensions, offer_video_codecs,
1399 &current_streams, offer.get())) {
1400 return nullptr;
1401 }
1402 break;
1403 case MEDIA_TYPE_DATA:
1404 if (!AddDataContentForOffer(media_description_options, session_options,
1405 current_content, current_description,
1406 offer_data_codecs, &current_streams,
1407 offer.get())) {
1408 return nullptr;
1409 }
1410 break;
1411 default:
1412 RTC_NOTREACHED();
1413 }
1414 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001415 }
1416
1417 // Bundle the contents together, if we've been asked to do so, and update any
1418 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 14:10:50 -07001419 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001420 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001421 for (const ContentInfo& content : offer->contents()) {
1422 // TODO(deadbeef): There are conditions that make bundling two media
1423 // descriptions together illegal. For example, they use the same payload
1424 // type to represent different codecs, or same IDs for different header
1425 // extensions. We need to detect this and not try to bundle those media
1426 // descriptions together.
1427 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001428 }
1429 offer->AddGroup(offer_bundle);
1430 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1431 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001432 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001433 }
1434 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1435 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001436 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001437 }
1438 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001439 return offer.release();
1440}
1441
1442SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001443 const SessionDescription* offer,
1444 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001445 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001446 if (!offer) {
1447 return nullptr;
1448 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001449 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001450 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1451 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001452 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001453
1454 StreamParamsVec current_streams;
1455 GetCurrentStreamParams(current_description, &current_streams);
1456
deadbeefb7892532017-02-22 19:35:18 -08001457 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1458 // group in the answer with the appropriate content names.
1459 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1460 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1461 // Transport info shared by the bundle group.
1462 std::unique_ptr<TransportInfo> bundle_transport;
1463
zhihuang1c378ed2017-08-17 14:10:50 -07001464 // Get list of all possible codecs that respects existing payload type
1465 // mappings and uses a single payload type space.
1466 //
1467 // Note that these lists may be further filtered for each m= section; this
1468 // step is done just to establish the payload type mappings shared by all
1469 // sections.
1470 AudioCodecs answer_audio_codecs;
1471 VideoCodecs answer_video_codecs;
1472 DataCodecs answer_data_codecs;
1473 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1474 &answer_video_codecs, &answer_data_codecs);
1475
1476 if (!session_options.vad_enabled) {
1477 // If application doesn't want CN codecs in answer.
1478 StripCNCodecs(&answer_audio_codecs);
1479 }
1480 FilterDataCodecs(&answer_data_codecs,
1481 session_options.data_channel_type == DCT_SCTP);
1482
1483 // Must have options for exactly as many sections as in the offer.
1484 RTC_DCHECK(offer->contents().size() ==
1485 session_options.media_description_options.size());
1486 // Iterate through the media description options, matching with existing
1487 // media descriptions in |current_description|.
1488 int msection_index = 0;
1489 for (const MediaDescriptionOptions& media_description_options :
1490 session_options.media_description_options) {
1491 const ContentInfo* offer_content = &offer->contents()[msection_index];
1492 // Media types and MIDs must match between the remote offer and the
1493 // MediaDescriptionOptions.
1494 RTC_DCHECK(
1495 IsMediaContentOfType(offer_content, media_description_options.type));
1496 RTC_DCHECK(media_description_options.mid == offer_content->name);
1497 const ContentInfo* current_content = nullptr;
1498 if (current_description &&
1499 msection_index <
1500 static_cast<int>(current_description->contents().size())) {
1501 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001502 }
zhihuang1c378ed2017-08-17 14:10:50 -07001503 switch (media_description_options.type) {
1504 case MEDIA_TYPE_AUDIO:
1505 if (!AddAudioContentForAnswer(
1506 media_description_options, session_options, offer_content,
1507 offer, current_content, current_description,
1508 bundle_transport.get(), answer_audio_codecs, &current_streams,
1509 answer.get())) {
1510 return nullptr;
1511 }
1512 break;
1513 case MEDIA_TYPE_VIDEO:
1514 if (!AddVideoContentForAnswer(
1515 media_description_options, session_options, offer_content,
1516 offer, current_content, current_description,
1517 bundle_transport.get(), answer_video_codecs, &current_streams,
1518 answer.get())) {
1519 return nullptr;
1520 }
1521 break;
1522 case MEDIA_TYPE_DATA:
1523 if (!AddDataContentForAnswer(media_description_options, session_options,
1524 offer_content, offer, current_content,
1525 current_description,
1526 bundle_transport.get(), answer_data_codecs,
1527 &current_streams, answer.get())) {
1528 return nullptr;
1529 }
1530 break;
1531 default:
1532 RTC_NOTREACHED();
1533 }
1534 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001535 // See if we can add the newly generated m= section to the BUNDLE group in
1536 // the answer.
1537 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001538 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001539 offer_bundle->HasContentName(added.name)) {
1540 answer_bundle.AddContentName(added.name);
1541 bundle_transport.reset(
1542 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001543 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001544 }
1545
deadbeefb7892532017-02-22 19:35:18 -08001546 // Only put BUNDLE group in answer if nonempty.
1547 if (answer_bundle.FirstContentName()) {
1548 answer->AddGroup(answer_bundle);
1549
1550 // Share the same ICE credentials and crypto params across all contents,
1551 // as BUNDLE requires.
1552 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1553 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1554 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001555 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001556
deadbeefb7892532017-02-22 19:35:18 -08001557 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1558 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1559 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001560 }
1561 }
1562
1563 return answer.release();
1564}
1565
ossu075af922016-06-14 03:29:38 -07001566const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1567 const RtpTransceiverDirection& direction) const {
1568 // If stream is inactive - generate list as if sendrecv.
1569 if (direction.send == direction.recv) {
1570 return audio_sendrecv_codecs_;
1571 } else if (direction.send) {
1572 return audio_send_codecs_;
1573 } else {
1574 return audio_recv_codecs_;
1575 }
1576}
1577
1578const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1579 const RtpTransceiverDirection& offer,
1580 const RtpTransceiverDirection& answer) const {
1581 // For inactive and sendrecv answers, generate lists as if we were to accept
1582 // the offer's direction. See RFC 3264 Section 6.1.
1583 if (answer.send == answer.recv) {
1584 if (offer.send == offer.recv) {
1585 return audio_sendrecv_codecs_;
1586 } else if (offer.send) {
1587 return audio_recv_codecs_;
1588 } else {
1589 return audio_send_codecs_;
1590 }
1591 } else if (answer.send) {
1592 return audio_send_codecs_;
1593 } else {
1594 return audio_recv_codecs_;
1595 }
1596}
1597
zhihuang1c378ed2017-08-17 14:10:50 -07001598void MergeCodecsFromDescription(const SessionDescription* description,
1599 AudioCodecs* audio_codecs,
1600 VideoCodecs* video_codecs,
1601 DataCodecs* data_codecs,
1602 UsedPayloadTypes* used_pltypes) {
1603 RTC_DCHECK(description);
1604 for (const ContentInfo& content : description->contents()) {
1605 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1606 const AudioContentDescription* audio =
1607 static_cast<AudioContentDescription*>(content.description);
1608 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1609 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1610 const VideoContentDescription* video =
1611 static_cast<VideoContentDescription*>(content.description);
1612 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1613 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1614 const DataContentDescription* data =
1615 static_cast<DataContentDescription*>(content.description);
1616 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1617 }
1618 }
1619}
1620
1621// Getting codecs for an offer involves these steps:
1622//
1623// 1. Construct payload type -> codec mappings for current description.
1624// 2. Add any reference codecs that weren't already present
1625// 3. For each individual media description (m= section), filter codecs based
1626// on the directional attribute (happens in another method).
1627void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001628 const SessionDescription* current_description,
1629 AudioCodecs* audio_codecs,
1630 VideoCodecs* video_codecs,
1631 DataCodecs* data_codecs) const {
1632 UsedPayloadTypes used_pltypes;
1633 audio_codecs->clear();
1634 video_codecs->clear();
1635 data_codecs->clear();
1636
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001637 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001638 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1639 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001640 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001641 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1642 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001643 }
1644
1645 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001646 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1647 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1648 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1649}
1650
1651// Getting codecs for an answer involves these steps:
1652//
1653// 1. Construct payload type -> codec mappings for current description.
1654// 2. Add any codecs from the offer that weren't already present.
1655// 3. Add any remaining codecs that weren't already present.
1656// 4. For each individual media description (m= section), filter codecs based
1657// on the directional attribute (happens in another method).
1658void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1659 const SessionDescription* current_description,
1660 const SessionDescription* remote_offer,
1661 AudioCodecs* audio_codecs,
1662 VideoCodecs* video_codecs,
1663 DataCodecs* data_codecs) const {
1664 UsedPayloadTypes used_pltypes;
1665 audio_codecs->clear();
1666 video_codecs->clear();
1667 data_codecs->clear();
1668
1669 // First - get all codecs from the current description if the media type
1670 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1671 // new media type is added.
1672 if (current_description) {
1673 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1674 data_codecs, &used_pltypes);
1675 }
1676
1677 // Second - filter out codecs that we don't support at all and should ignore.
1678 AudioCodecs filtered_offered_audio_codecs;
1679 VideoCodecs filtered_offered_video_codecs;
1680 DataCodecs filtered_offered_data_codecs;
1681 for (const ContentInfo& content : remote_offer->contents()) {
1682 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1683 const AudioContentDescription* audio =
1684 static_cast<AudioContentDescription*>(content.description);
1685 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1686 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1687 filtered_offered_audio_codecs,
1688 offered_audio_codec, nullptr) &&
1689 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1690 offered_audio_codec, nullptr)) {
1691 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1692 }
1693 }
1694 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1695 const VideoContentDescription* video =
1696 static_cast<VideoContentDescription*>(content.description);
1697 for (const VideoCodec& offered_video_codec : video->codecs()) {
1698 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1699 filtered_offered_video_codecs,
1700 offered_video_codec, nullptr) &&
1701 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1702 offered_video_codec, nullptr)) {
1703 filtered_offered_video_codecs.push_back(offered_video_codec);
1704 }
1705 }
1706 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1707 const DataContentDescription* data =
1708 static_cast<DataContentDescription*>(content.description);
1709 for (const DataCodec& offered_data_codec : data->codecs()) {
1710 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1711 filtered_offered_data_codecs,
1712 offered_data_codec, nullptr) &&
1713 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1714 offered_data_codec, nullptr)) {
1715 filtered_offered_data_codecs.push_back(offered_data_codec);
1716 }
1717 }
1718 }
1719 }
1720
1721 // Add codecs that are not in |current_description| but were in
1722 // |remote_offer|.
1723 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1724 &used_pltypes);
1725 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1726 &used_pltypes);
1727 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1728 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001729}
1730
1731void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1732 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001733 RtpHeaderExtensions* offer_audio_extensions,
1734 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001735 // All header extensions allocated from the same range to avoid potential
1736 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001737 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001738 RtpHeaderExtensions all_regular_extensions;
1739 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 14:10:50 -07001740 offer_audio_extensions->clear();
1741 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001742
1743 // First - get all extensions from the current description if the media type
1744 // is used.
1745 // Add them to |used_ids| so the local ids are not reused if a new media
1746 // type is added.
1747 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001748 for (const ContentInfo& content : current_description->contents()) {
1749 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1750 const AudioContentDescription* audio =
1751 static_cast<const AudioContentDescription*>(content.description);
1752 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1753 &all_regular_extensions, &all_encrypted_extensions,
1754 &used_ids);
1755 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1756 const VideoContentDescription* video =
1757 static_cast<const VideoContentDescription*>(content.description);
1758 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1759 &all_regular_extensions, &all_encrypted_extensions,
1760 &used_ids);
1761 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001762 }
1763 }
1764
1765 // Add our default RTP header extensions that are not in
1766 // |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001767 MergeRtpHdrExts(audio_rtp_header_extensions(), offer_audio_extensions,
1768 &all_regular_extensions, &all_encrypted_extensions,
1769 &used_ids);
1770 MergeRtpHdrExts(video_rtp_header_extensions(), offer_video_extensions,
1771 &all_regular_extensions, &all_encrypted_extensions,
1772 &used_ids);
1773
jbauch5869f502017-06-29 12:31:36 -07001774 // TODO(jbauch): Support adding encrypted header extensions to existing
1775 // sessions.
1776 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001777 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1778 &all_encrypted_extensions, &used_ids);
1779 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1780 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001781 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001782}
1783
1784bool MediaSessionDescriptionFactory::AddTransportOffer(
1785 const std::string& content_name,
1786 const TransportOptions& transport_options,
1787 const SessionDescription* current_desc,
1788 SessionDescription* offer_desc) const {
1789 if (!transport_desc_factory_)
1790 return false;
1791 const TransportDescription* current_tdesc =
1792 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001793 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001794 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1795 bool ret = (new_tdesc.get() != NULL &&
1796 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1797 if (!ret) {
1798 LOG(LS_ERROR)
1799 << "Failed to AddTransportOffer, content name=" << content_name;
1800 }
1801 return ret;
1802}
1803
1804TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1805 const std::string& content_name,
1806 const SessionDescription* offer_desc,
1807 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08001808 const SessionDescription* current_desc,
1809 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001810 if (!transport_desc_factory_)
1811 return NULL;
1812 const TransportDescription* offer_tdesc =
1813 GetTransportDescription(content_name, offer_desc);
1814 const TransportDescription* current_tdesc =
1815 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08001816 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1817 require_transport_attributes,
1818 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001819}
1820
1821bool MediaSessionDescriptionFactory::AddTransportAnswer(
1822 const std::string& content_name,
1823 const TransportDescription& transport_desc,
1824 SessionDescription* answer_desc) const {
1825 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1826 transport_desc))) {
1827 LOG(LS_ERROR)
1828 << "Failed to AddTransportAnswer, content name=" << content_name;
1829 return false;
1830 }
1831 return true;
1832}
1833
zhihuang1c378ed2017-08-17 14:10:50 -07001834// |audio_codecs| = set of all possible codecs that can be used, with correct
1835// payload type mappings
1836//
1837// |supported_audio_codecs| = set of codecs that are supported for the direction
1838// of this m= section
1839//
1840// acd->codecs() = set of previously negotiated codecs for this m= section
1841//
1842// The payload types should come from audio_codecs, but the order should come
1843// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1844// change existing codec priority, and that new codecs are added with the right
1845// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001846bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001847 const MediaDescriptionOptions& media_description_options,
1848 const MediaSessionOptions& session_options,
1849 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001850 const SessionDescription* current_description,
1851 const RtpHeaderExtensions& audio_rtp_extensions,
1852 const AudioCodecs& audio_codecs,
1853 StreamParamsVec* current_streams,
1854 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001855 // Filter audio_codecs (which includes all codecs, with correctly remapped
1856 // payload types) based on transceiver direction.
1857 const AudioCodecs& supported_audio_codecs =
1858 GetAudioCodecsForOffer(media_description_options.direction);
1859
1860 AudioCodecs filtered_codecs;
1861 // Add the codecs from current content if exists.
1862 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001863 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07001864 const AudioContentDescription* acd =
1865 static_cast<const AudioContentDescription*>(
1866 current_content->description);
1867 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001868 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1869 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001870 filtered_codecs.push_back(codec);
1871 }
1872 }
1873 }
1874 // Add other supported audio codecs.
1875 AudioCodec found_codec;
1876 for (const AudioCodec& codec : supported_audio_codecs) {
1877 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1878 codec, &found_codec) &&
1879 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1880 codec, nullptr)) {
1881 // Use the |found_codec| from |audio_codecs| because it has the correctly
1882 // mapped payload type.
1883 filtered_codecs.push_back(found_codec);
1884 }
1885 }
deadbeef44f08192015-12-15 16:20:09 -08001886
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001887 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001888 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1889 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001890
kwiberg31022942016-03-11 14:18:21 -08001891 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001892 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001893 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1894 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001895 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001896 media_description_options.sender_options, session_options,
1897 filtered_codecs, sdes_policy, GetCryptos(current_content),
1898 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001899 return false;
1900 }
1901 audio->set_lang(lang_);
1902
1903 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1904 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001905
zhihuang1c378ed2017-08-17 14:10:50 -07001906 audio->set_direction(
1907 media_description_options.direction.ToMediaContentDirection());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001908
zhihuang1c378ed2017-08-17 14:10:50 -07001909 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
1910 media_description_options.stopped, audio.release());
1911 if (!AddTransportOffer(media_description_options.mid,
1912 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001913 current_description, desc)) {
1914 return false;
1915 }
1916
1917 return true;
1918}
1919
1920bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001921 const MediaDescriptionOptions& media_description_options,
1922 const MediaSessionOptions& session_options,
1923 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001924 const SessionDescription* current_description,
1925 const RtpHeaderExtensions& video_rtp_extensions,
1926 const VideoCodecs& video_codecs,
1927 StreamParamsVec* current_streams,
1928 SessionDescription* desc) const {
1929 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001930 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1931 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001932
kwiberg31022942016-03-11 14:18:21 -08001933 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001934 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001935 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1936 &crypto_suites);
1937
1938 VideoCodecs filtered_codecs;
1939 // Add the codecs from current content if exists.
1940 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001941 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07001942 const VideoContentDescription* vcd =
1943 static_cast<const VideoContentDescription*>(
1944 current_content->description);
1945 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001946 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07001947 nullptr)) {
1948 filtered_codecs.push_back(codec);
1949 }
1950 }
1951 }
1952 // Add other supported video codecs.
1953 VideoCodec found_codec;
1954 for (const VideoCodec& codec : video_codecs_) {
1955 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1956 &found_codec) &&
1957 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1958 nullptr)) {
1959 // Use the |found_codec| from |video_codecs| because it has the correctly
1960 // mapped payload type.
1961 filtered_codecs.push_back(found_codec);
1962 }
1963 }
1964
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001965 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001966 media_description_options.sender_options, session_options,
1967 filtered_codecs, sdes_policy, GetCryptos(current_content),
1968 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001969 return false;
1970 }
1971
zhihuang1c378ed2017-08-17 14:10:50 -07001972 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001973
1974 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1975 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001976
zhihuang1c378ed2017-08-17 14:10:50 -07001977 video->set_direction(
1978 media_description_options.direction.ToMediaContentDirection());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001979
zhihuang1c378ed2017-08-17 14:10:50 -07001980 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
1981 media_description_options.stopped, video.release());
1982 if (!AddTransportOffer(media_description_options.mid,
1983 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001984 current_description, desc)) {
1985 return false;
1986 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001987 return true;
1988}
1989
1990bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001991 const MediaDescriptionOptions& media_description_options,
1992 const MediaSessionOptions& session_options,
1993 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001994 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001995 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001996 StreamParamsVec* current_streams,
1997 SessionDescription* desc) const {
1998 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1999
kwiberg31022942016-03-11 14:18:21 -08002000 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07002001 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
2002 // If the DataChannel type is not specified, use the DataChannel type in
2003 // the current description.
2004 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002005 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
zhihuang1c378ed2017-08-17 14:10:50 -07002006 is_sctp = (static_cast<const DataContentDescription*>(
2007 current_content->description)
2008 ->protocol() == kMediaProtocolSctp);
2009 }
deadbeef44f08192015-12-15 16:20:09 -08002010
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002011 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07002012 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2013 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002014 std::vector<std::string> crypto_suites;
2015 if (is_sctp) {
2016 // SDES doesn't make sense for SCTP, so we disable it, and we only
2017 // get SDES crypto suites for RTP-based data channels.
2018 sdes_policy = cricket::SEC_DISABLED;
2019 // Unlike SetMediaProtocol below, we need to set the protocol
2020 // before we call CreateMediaContentOffer. Otherwise,
2021 // CreateMediaContentOffer won't know this is SCTP and will
2022 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 09:38:55 -07002023 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2024 // it's safe to do so. Older versions of webrtc would reject these
2025 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002026 data->set_protocol(
2027 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
2028 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002029 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -07002030 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002031 }
2032
zhihuang1c378ed2017-08-17 14:10:50 -07002033 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002034 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002035 media_description_options.sender_options, session_options,
2036 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2037 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002038 return false;
2039 }
2040
2041 if (is_sctp) {
zhihuang1c378ed2017-08-17 14:10:50 -07002042 desc->AddContent(media_description_options.mid, NS_JINGLE_DRAFT_SCTP,
2043 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002044 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002045 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002046 SetMediaProtocol(secure_transport, data.get());
zhihuang1c378ed2017-08-17 14:10:50 -07002047 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
2048 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002049 }
zhihuang1c378ed2017-08-17 14:10:50 -07002050 if (!AddTransportOffer(media_description_options.mid,
2051 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002052 current_description, desc)) {
2053 return false;
2054 }
2055 return true;
2056}
2057
zhihuang1c378ed2017-08-17 14:10:50 -07002058// |audio_codecs| = set of all possible codecs that can be used, with correct
2059// payload type mappings
2060//
2061// |supported_audio_codecs| = set of codecs that are supported for the direction
2062// of this m= section
2063//
2064// acd->codecs() = set of previously negotiated codecs for this m= section
2065//
2066// The payload types should come from audio_codecs, but the order should come
2067// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2068// change existing codec priority, and that new codecs are added with the right
2069// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002070bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002071 const MediaDescriptionOptions& media_description_options,
2072 const MediaSessionOptions& session_options,
2073 const ContentInfo* offer_content,
2074 const SessionDescription* offer_description,
2075 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002076 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002077 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002078 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002079 StreamParamsVec* current_streams,
2080 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002081 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002082 const AudioContentDescription* offer_audio_description =
2083 static_cast<const AudioContentDescription*>(offer_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002084
deadbeefb7892532017-02-22 19:35:18 -08002085 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002086 CreateTransportAnswer(media_description_options.mid, offer_description,
2087 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002088 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002089 if (!audio_transport) {
2090 return false;
2091 }
2092
zhihuang1c378ed2017-08-17 14:10:50 -07002093 // Pick codecs based on the requested communications direction in the offer
2094 // and the selected direction in the answer.
2095 // Note these will be filtered one final time in CreateMediaContentAnswer.
2096 auto wants_rtd = media_description_options.direction;
2097 auto offer_rtd = RtpTransceiverDirection::FromMediaContentDirection(
2098 offer_audio_description->direction());
ossu075af922016-06-14 03:29:38 -07002099 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002100 AudioCodecs supported_audio_codecs =
2101 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2102
2103 AudioCodecs filtered_codecs;
2104 // Add the codecs from current content if exists.
2105 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002106 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002107 const AudioContentDescription* acd =
2108 static_cast<const AudioContentDescription*>(
2109 current_content->description);
2110 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002111 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2112 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07002113 filtered_codecs.push_back(codec);
2114 }
2115 }
2116 }
2117 // Add other supported audio codecs.
2118 AudioCodec found_codec;
2119 for (const AudioCodec& codec : supported_audio_codecs) {
2120 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2121 codec, &found_codec) &&
2122 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2123 codec, nullptr)) {
2124 // Use the |found_codec| from |audio_codecs| because it has the correctly
2125 // mapped payload type.
2126 filtered_codecs.push_back(found_codec);
2127 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002128 }
2129
zhihuang1c378ed2017-08-17 14:10:50 -07002130 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2131 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002132 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002133 new AudioContentDescription());
2134 // Do not require or create SDES cryptos if DTLS is used.
2135 cricket::SecurePolicy sdes_policy =
2136 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2137 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002138 offer_audio_description, media_description_options, session_options,
2139 filtered_codecs, sdes_policy, GetCryptos(current_content),
2140 audio_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2141 current_streams, bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002142 return false; // Fails the session setup.
2143 }
2144
deadbeefb7892532017-02-22 19:35:18 -08002145 bool secure = bundle_transport ? bundle_transport->description.secure()
2146 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002147 bool rejected = media_description_options.stopped ||
2148 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002149 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2150 audio_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002151 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002152 AddTransportAnswer(media_description_options.mid, *(audio_transport.get()),
2153 answer);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002154 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002155 LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2156 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002157 }
2158
zhihuang1c378ed2017-08-17 14:10:50 -07002159 answer->AddContent(media_description_options.mid, offer_content->type,
2160 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002161 return true;
2162}
2163
2164bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002165 const MediaDescriptionOptions& media_description_options,
2166 const MediaSessionOptions& session_options,
2167 const ContentInfo* offer_content,
2168 const SessionDescription* offer_description,
2169 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002170 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002171 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002172 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002173 StreamParamsVec* current_streams,
2174 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002175 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002176 const VideoContentDescription* offer_video_description =
2177 static_cast<const VideoContentDescription*>(offer_content->description);
2178
deadbeefb7892532017-02-22 19:35:18 -08002179 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002180 CreateTransportAnswer(media_description_options.mid, offer_description,
2181 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002182 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002183 if (!video_transport) {
2184 return false;
2185 }
2186
zhihuang1c378ed2017-08-17 14:10:50 -07002187 VideoCodecs filtered_codecs;
2188 // Add the codecs from current content if exists.
2189 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002190 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002191 const VideoContentDescription* vcd =
2192 static_cast<const VideoContentDescription*>(
2193 current_content->description);
2194 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002195 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07002196 nullptr)) {
2197 filtered_codecs.push_back(codec);
2198 }
2199 }
2200 }
2201 // Add other supported video codecs.
2202 VideoCodec found_codec;
2203 for (const VideoCodec& codec : video_codecs_) {
2204 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
2205 &found_codec) &&
2206 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2207 nullptr)) {
2208 // Use the |found_codec| from |video_codecs| because it has the correctly
2209 // mapped payload type.
2210 filtered_codecs.push_back(found_codec);
2211 }
2212 }
2213
2214 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2215 session_options.bundle_enabled;
2216
kwiberg31022942016-03-11 14:18:21 -08002217 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002218 new VideoContentDescription());
2219 // Do not require or create SDES cryptos if DTLS is used.
2220 cricket::SecurePolicy sdes_policy =
2221 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002222 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002223 offer_video_description, media_description_options, session_options,
2224 filtered_codecs, sdes_policy, GetCryptos(current_content),
2225 video_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2226 current_streams, bundle_enabled, video_answer.get())) {
2227 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002228 }
deadbeefb7892532017-02-22 19:35:18 -08002229 bool secure = bundle_transport ? bundle_transport->description.secure()
2230 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002231 bool rejected = media_description_options.stopped ||
2232 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002233 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2234 video_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002235 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002236 if (!AddTransportAnswer(media_description_options.mid,
2237 *(video_transport.get()), answer)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002238 return false;
2239 }
zhihuang1c378ed2017-08-17 14:10:50 -07002240 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002241 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002242 LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2243 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002244 }
zhihuang1c378ed2017-08-17 14:10:50 -07002245 answer->AddContent(media_description_options.mid, offer_content->type,
2246 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247 return true;
2248}
2249
2250bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002251 const MediaDescriptionOptions& media_description_options,
2252 const MediaSessionOptions& session_options,
2253 const ContentInfo* offer_content,
2254 const SessionDescription* offer_description,
2255 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002256 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002257 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002258 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002259 StreamParamsVec* current_streams,
2260 SessionDescription* answer) const {
deadbeefb7892532017-02-22 19:35:18 -08002261 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002262 CreateTransportAnswer(media_description_options.mid, offer_description,
2263 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002264 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002265 if (!data_transport) {
2266 return false;
2267 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002268
kwiberg31022942016-03-11 14:18:21 -08002269 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002270 new DataContentDescription());
2271 // Do not require or create SDES cryptos if DTLS is used.
2272 cricket::SecurePolicy sdes_policy =
2273 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002274 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2275 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002276 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2277 const DataContentDescription* offer_data_description =
2278 static_cast<const DataContentDescription*>(offer_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002279 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002280 offer_data_description, media_description_options, session_options,
2281 data_codecs, sdes_policy, GetCryptos(current_content),
2282 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2283 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002284 return false; // Fails the session setup.
2285 }
2286
zstein4b2e0822017-02-17 19:48:38 -08002287 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-17 19:48:38 -08002288 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2289 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2290
deadbeefb7892532017-02-22 19:35:18 -08002291 bool secure = bundle_transport ? bundle_transport->description.secure()
2292 : data_transport->secure();
2293
zhihuang1c378ed2017-08-17 14:10:50 -07002294 bool rejected = session_options.data_channel_type == DCT_NONE ||
2295 media_description_options.stopped ||
2296 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002297 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2298 data_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002299 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002300 data_answer->set_bandwidth(kDataMaxBandwidth);
2301 if (!AddTransportAnswer(media_description_options.mid,
2302 *(data_transport.get()), answer)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002303 return false;
2304 }
2305 } else {
2306 // RFC 3264
2307 // The answer MUST contain the same number of m-lines as the offer.
2308 LOG(LS_INFO) << "Data is not supported in the answer.";
2309 }
zhihuang1c378ed2017-08-17 14:10:50 -07002310 answer->AddContent(media_description_options.mid, offer_content->type,
2311 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002312 return true;
2313}
2314
zhihuang1c378ed2017-08-17 14:10:50 -07002315void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2316 audio_sendrecv_codecs_.clear();
2317 all_audio_codecs_.clear();
2318 // Compute the audio codecs union.
2319 for (const AudioCodec& send : audio_send_codecs_) {
2320 all_audio_codecs_.push_back(send);
2321 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2322 send, nullptr)) {
2323 // It doesn't make sense to have an RTX codec we support sending but not
2324 // receiving.
2325 RTC_DCHECK(!IsRtxCodec(send));
2326 }
2327 }
2328 for (const AudioCodec& recv : audio_recv_codecs_) {
2329 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2330 recv, nullptr)) {
2331 all_audio_codecs_.push_back(recv);
2332 }
2333 }
2334 // Use NegotiateCodecs to merge our codec lists, since the operation is
2335 // essentially the same. Put send_codecs as the offered_codecs, which is the
2336 // order we'd like to follow. The reasoning is that encoding is usually more
2337 // expensive than decoding, and prioritizing a codec in the send list probably
2338 // means it's a codec we can handle efficiently.
2339 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2340 &audio_sendrecv_codecs_);
2341}
2342
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002343bool IsMediaContent(const ContentInfo* content) {
2344 return (content &&
2345 (content->type == NS_JINGLE_RTP ||
2346 content->type == NS_JINGLE_DRAFT_SCTP));
2347}
2348
2349bool IsAudioContent(const ContentInfo* content) {
2350 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2351}
2352
2353bool IsVideoContent(const ContentInfo* content) {
2354 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2355}
2356
2357bool IsDataContent(const ContentInfo* content) {
2358 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2359}
2360
deadbeef0ed85b22016-02-23 17:24:52 -08002361const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2362 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002363 for (const ContentInfo& content : contents) {
2364 if (IsMediaContentOfType(&content, media_type)) {
2365 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002366 }
2367 }
deadbeef0ed85b22016-02-23 17:24:52 -08002368 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002369}
2370
2371const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2372 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2373}
2374
2375const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2376 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2377}
2378
2379const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2380 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2381}
2382
2383static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2384 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002385 if (sdesc == nullptr) {
2386 return nullptr;
2387 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002388
2389 return GetFirstMediaContent(sdesc->contents(), media_type);
2390}
2391
2392const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2393 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2394}
2395
2396const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2397 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2398}
2399
2400const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2401 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2402}
2403
2404const MediaContentDescription* GetFirstMediaContentDescription(
2405 const SessionDescription* sdesc, MediaType media_type) {
2406 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2407 const ContentDescription* description = content ? content->description : NULL;
2408 return static_cast<const MediaContentDescription*>(description);
2409}
2410
2411const AudioContentDescription* GetFirstAudioContentDescription(
2412 const SessionDescription* sdesc) {
2413 return static_cast<const AudioContentDescription*>(
2414 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2415}
2416
2417const VideoContentDescription* GetFirstVideoContentDescription(
2418 const SessionDescription* sdesc) {
2419 return static_cast<const VideoContentDescription*>(
2420 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2421}
2422
2423const DataContentDescription* GetFirstDataContentDescription(
2424 const SessionDescription* sdesc) {
2425 return static_cast<const DataContentDescription*>(
2426 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2427}
2428
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002429//
2430// Non-const versions of the above functions.
2431//
2432
Steve Anton36b29d12017-10-30 09:57:42 -07002433ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002434 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002435 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002436 if (IsMediaContentOfType(&content, media_type)) {
2437 return &content;
2438 }
2439 }
2440 return nullptr;
2441}
2442
Steve Anton36b29d12017-10-30 09:57:42 -07002443ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002444 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2445}
2446
Steve Anton36b29d12017-10-30 09:57:42 -07002447ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002448 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2449}
2450
Steve Anton36b29d12017-10-30 09:57:42 -07002451ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002452 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2453}
2454
2455static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2456 MediaType media_type) {
2457 if (sdesc == nullptr) {
2458 return nullptr;
2459 }
2460
Steve Anton36b29d12017-10-30 09:57:42 -07002461 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002462}
2463
2464ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2465 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2466}
2467
2468ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2469 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2470}
2471
2472ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2473 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2474}
2475
2476MediaContentDescription* GetFirstMediaContentDescription(
2477 SessionDescription* sdesc,
2478 MediaType media_type) {
2479 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2480 ContentDescription* description = content ? content->description : NULL;
2481 return static_cast<MediaContentDescription*>(description);
2482}
2483
2484AudioContentDescription* GetFirstAudioContentDescription(
2485 SessionDescription* sdesc) {
2486 return static_cast<AudioContentDescription*>(
2487 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2488}
2489
2490VideoContentDescription* GetFirstVideoContentDescription(
2491 SessionDescription* sdesc) {
2492 return static_cast<VideoContentDescription*>(
2493 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2494}
2495
2496DataContentDescription* GetFirstDataContentDescription(
2497 SessionDescription* sdesc) {
2498 return static_cast<DataContentDescription*>(
2499 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2500}
2501
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002502} // namespace cricket