blob: 79aa975d96f368f2de8452865e4a147015de6bd0 [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
Patrik Höglund7aee3d52017-11-15 13:15:17 +010021#include "api/cryptoparams.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "api/optional.h"
Mirko Bonadei71207422017-09-15 13:58:09 +020023#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#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"
Steve Anton1d03a752017-11-27 14:30:09 -080028#include "pc/rtpmediautils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "pc/srtpfilter.h"
30#include "rtc_base/base64.h"
31#include "rtc_base/checks.h"
32#include "rtc_base/helpers.h"
33#include "rtc_base/logging.h"
34#include "rtc_base/stringutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035
36namespace {
Steve Anton1d03a752017-11-27 14:30:09 -080037
38using webrtc::RtpTransceiverDirection;
39
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080041
deadbeef7914b8c2017-04-21 03:23:33 -070042void GetSupportedSdesCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
43 std::vector<int>*),
44 const rtc::CryptoOptions& crypto_options,
45 std::vector<std::string>* names) {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080046 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070047 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080048 for (const auto crypto : crypto_suites) {
49 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
50 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080051}
terelius8c011e52016-04-26 05:28:11 -070052} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053
54namespace cricket {
55
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056// RTP Profile names
57// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
58// RFC4585
59const char kMediaProtocolAvpf[] = "RTP/AVPF";
60// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000061const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
62
deadbeeff3938292015-07-15 12:20:53 -070063// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
64// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065const char kMediaProtocolSavpf[] = "RTP/SAVPF";
66
67const char kMediaProtocolRtpPrefix[] = "RTP/";
68
69const char kMediaProtocolSctp[] = "SCTP";
70const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000071const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000072const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073
deadbeef8b7e9ad2017-05-25 09:38:55 -070074// Note that the below functions support some protocol strings purely for
75// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
76// and Interoperability.
77
78static bool IsDtlsRtp(const std::string& protocol) {
79 // Most-likely values first.
80 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
81 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
82}
83
84static bool IsPlainRtp(const std::string& protocol) {
85 // Most-likely values first.
86 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
87 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
88}
89
90static bool IsDtlsSctp(const std::string& protocol) {
91 return protocol == kMediaProtocolDtlsSctp ||
92 protocol == kMediaProtocolUdpDtlsSctp ||
93 protocol == kMediaProtocolTcpDtlsSctp;
94}
95
96static bool IsPlainSctp(const std::string& protocol) {
97 return protocol == kMediaProtocolSctp;
98}
99
100static bool IsSctp(const std::string& protocol) {
101 return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
102}
103
Steve Anton1d03a752017-11-27 14:30:09 -0800104static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
105 RtpTransceiverDirection offer,
106 RtpTransceiverDirection wants) {
107 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
108 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
109 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
110 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
111 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
112 offer_send && wants_recv);
ossu075af922016-06-14 03:29:38 -0700113}
114
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115static bool IsMediaContentOfType(const ContentInfo* content,
116 MediaType media_type) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800117 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118 return false;
119 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800120 return content->media_description()->type() == media_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121}
122
Yves Gerey665174f2018-06-19 15:03:05 +0200123static bool CreateCryptoParams(int tag,
124 const std::string& cipher,
125 CryptoParams* out) {
jbauchcb560652016-08-04 05:20:32 -0700126 int key_len;
127 int salt_len;
Yves Gerey665174f2018-06-19 15:03:05 +0200128 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
129 &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130 return false;
131 }
jbauchcb560652016-08-04 05:20:32 -0700132
133 int master_key_len = key_len + salt_len;
134 std::string master_key;
135 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
136 return false;
137 }
138
kwiberg352444f2016-11-28 15:58:53 -0800139 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700140 std::string key = rtc::Base64::Encode(master_key);
141
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142 out->tag = tag;
143 out->cipher_suite = cipher;
144 out->key_params = kInline;
145 out->key_params += key;
146 return true;
147}
148
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149static bool AddCryptoParams(const std::string& cipher_suite,
Yves Gerey665174f2018-06-19 15:03:05 +0200150 CryptoParamsVec* out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000151 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000152
153 out->resize(size + 1);
154 return CreateCryptoParams(size, cipher_suite, &out->at(size));
155}
156
157void AddMediaCryptos(const CryptoParamsVec& cryptos,
158 MediaContentDescription* media) {
159 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
160 crypto != cryptos.end(); ++crypto) {
161 media->AddCrypto(*crypto);
162 }
163}
164
165bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
166 MediaContentDescription* media) {
167 CryptoParamsVec cryptos;
168 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
169 it != crypto_suites.end(); ++it) {
170 if (!AddCryptoParams(*it, &cryptos)) {
171 return false;
172 }
173 }
174 AddMediaCryptos(cryptos, media);
175 return true;
176}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000177
zhihuang1c378ed2017-08-17 14:10:50 -0700178const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800179 if (!content || !content->media_description()) {
zhihuang1c378ed2017-08-17 14:10:50 -0700180 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800182 return &content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183}
184
185bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
186 const CryptoParams& crypto,
187 CryptoParams* out) {
188 for (CryptoParamsVec::const_iterator it = cryptos.begin();
189 it != cryptos.end(); ++it) {
190 if (crypto.Matches(*it)) {
191 *out = *it;
192 return true;
193 }
194 }
195 return false;
196}
197
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700198// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
199// low overhead.
deadbeef7914b8c2017-04-21 03:23:33 -0700200void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
201 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700202 if (crypto_options.enable_gcm_crypto_suites) {
203 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
204 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
205 }
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700206 if (crypto_options.enable_aes128_sha1_32_crypto_cipher) {
207 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
208 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800209 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000210}
211
deadbeef7914b8c2017-04-21 03:23:33 -0700212void GetSupportedAudioSdesCryptoSuiteNames(
213 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800214 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700215 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
216 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217}
218
deadbeef7914b8c2017-04-21 03:23:33 -0700219void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
220 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700221 if (crypto_options.enable_gcm_crypto_suites) {
222 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
223 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
224 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800225 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000226}
227
deadbeef7914b8c2017-04-21 03:23:33 -0700228void GetSupportedVideoSdesCryptoSuiteNames(
229 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800230 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700231 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
232 crypto_options, crypto_suite_names);
233}
234
235void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
236 std::vector<int>* crypto_suites) {
237 if (crypto_options.enable_gcm_crypto_suites) {
238 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
239 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
240 }
241 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
242}
243
244void GetSupportedDataSdesCryptoSuiteNames(
245 const rtc::CryptoOptions& crypto_options,
246 std::vector<std::string>* crypto_suite_names) {
247 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
248 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800249}
250
jbauchcb560652016-08-04 05:20:32 -0700251// Support any GCM cipher (if enabled through options). For video support only
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700252// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
253// bundle is enabled because it is low overhead.
jbauchcb560652016-08-04 05:20:32 -0700254// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255static bool SelectCrypto(const MediaContentDescription* offer,
256 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700257 const rtc::CryptoOptions& crypto_options,
Yves Gerey665174f2018-06-19 15:03:05 +0200258 CryptoParams* crypto) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
260 const CryptoParamsVec& cryptos = offer->cryptos();
261
Yves Gerey665174f2018-06-19 15:03:05 +0200262 for (CryptoParamsVec::const_iterator i = cryptos.begin(); i != cryptos.end();
263 ++i) {
jbauchcb560652016-08-04 05:20:32 -0700264 if ((crypto_options.enable_gcm_crypto_suites &&
265 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
266 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700267 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700268 !bundle && crypto_options.enable_aes128_sha1_32_crypto_cipher)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
270 }
271 }
272 return false;
273}
274
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000276// The generated values are added to |ssrcs|.
277// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000279 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200280 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000281 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200282 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000284 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000285 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000286 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
287 ssrcs->push_back(candidate);
288 }
289}
290
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291// Finds all StreamParams of all media types and attach them to stream_params.
292static void GetCurrentStreamParams(const SessionDescription* sdesc,
293 StreamParamsVec* stream_params) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800294 RTC_DCHECK(stream_params);
295 if (!sdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 return;
Steve Antonb1c1de12017-12-21 15:14:30 -0800297 }
298 for (const ContentInfo& content : sdesc->contents()) {
299 if (!content.media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000300 continue;
301 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800302 for (const StreamParams& params : content.media_description()->streams()) {
303 stream_params->push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000304 }
305 }
306}
307
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000308// Filters the data codecs for the data channel type.
309void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
310 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700311 const char* codec_name =
312 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000313 for (std::vector<DataCodec>::iterator iter = codecs->begin();
314 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700315 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000316 iter = codecs->erase(iter);
317 } else {
318 ++iter;
319 }
320 }
321}
322
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000323template <typename IdStruct>
324class UsedIds {
325 public:
326 UsedIds(int min_allowed_id, int max_allowed_id)
327 : min_allowed_id_(min_allowed_id),
328 max_allowed_id_(max_allowed_id),
Yves Gerey665174f2018-06-19 15:03:05 +0200329 next_id_(max_allowed_id) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330
331 // Loops through all Id in |ids| and changes its id if it is
332 // already in use by another IdStruct. Call this methods with all Id
333 // in a session description to make sure no duplicate ids exists.
334 // Note that typename Id must be a type of IdStruct.
335 template <typename Id>
336 void FindAndSetIdUsed(std::vector<Id>* ids) {
Yves Gerey665174f2018-06-19 15:03:05 +0200337 for (typename std::vector<Id>::iterator it = ids->begin(); it != ids->end();
338 ++it) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000339 FindAndSetIdUsed(&*it);
340 }
341 }
342
343 // Finds and sets an unused id if the |idstruct| id is already in use.
344 void FindAndSetIdUsed(IdStruct* idstruct) {
345 const int original_id = idstruct->id;
346 int new_id = idstruct->id;
347
348 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
349 // If the original id is not in range - this is an id that can't be
350 // dynamically changed.
351 return;
352 }
353
354 if (IsIdUsed(original_id)) {
355 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100356 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
357 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000358 idstruct->id = new_id;
359 }
360 SetIdUsed(new_id);
361 }
362
363 private:
364 // Returns the first unused id in reverse order.
365 // This hopefully reduce the risk of more collisions. We want to change the
366 // default ids as little as possible.
367 int FindUnusedId() {
368 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
369 --next_id_;
370 }
nisseede5da42017-01-12 05:15:36 -0800371 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000372 return next_id_;
373 }
374
Yves Gerey665174f2018-06-19 15:03:05 +0200375 bool IsIdUsed(int new_id) { return id_set_.find(new_id) != id_set_.end(); }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000376
Yves Gerey665174f2018-06-19 15:03:05 +0200377 void SetIdUsed(int new_id) { id_set_.insert(new_id); }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378
379 const int min_allowed_id_;
380 const int max_allowed_id_;
381 int next_id_;
382 std::set<int> id_set_;
383};
384
385// Helper class used for finding duplicate RTP payload types among audio, video
386// and data codecs. When bundle is used the payload types may not collide.
387class UsedPayloadTypes : public UsedIds<Codec> {
388 public:
389 UsedPayloadTypes()
Yves Gerey665174f2018-06-19 15:03:05 +0200390 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000391
392 private:
393 static const int kDynamicPayloadTypeMin = 96;
394 static const int kDynamicPayloadTypeMax = 127;
395};
396
397// Helper class used for finding duplicate RTP Header extension ids among
398// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700399class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000400 public:
401 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-25 18:15:09 -0800402 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
403 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404
405 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000406};
407
zhihuang1c378ed2017-08-17 14:10:50 -0700408// Adds a StreamParams for each SenderOptions in |sender_options| to
409// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410// |current_params| - All currently known StreamParams of any media type.
411template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700412static bool AddStreamParams(
413 const std::vector<SenderOptions>& sender_options,
414 const std::string& rtcp_cname,
415 StreamParamsVec* current_streams,
416 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700417 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 09:38:55 -0700418 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700419 return true;
420 }
421
Noah Richards2e7a0982015-05-18 14:02:54 -0700422 const bool include_rtx_streams =
423 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000424
brandtr03d5fb12016-11-22 03:37:59 -0800425 const bool include_flexfec_stream =
426 ContainsFlexfecCodec(content_description->codecs());
427
zhihuang1c378ed2017-08-17 14:10:50 -0700428 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000429 // groupid is empty for StreamParams generated using
430 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700431 StreamParams* param =
432 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000433 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700434 // This is a new sender.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200435 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 14:10:50 -0700436 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 StreamParams stream_param;
zhihuang1c378ed2017-08-17 14:10:50 -0700438 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000439 // Add the generated ssrc.
440 for (size_t i = 0; i < ssrcs.size(); ++i) {
441 stream_param.ssrcs.push_back(ssrcs[i]);
442 }
zhihuang1c378ed2017-08-17 14:10:50 -0700443 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000444 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
445 stream_param.ssrc_groups.push_back(group);
446 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700447 // Generate extra ssrcs for include_rtx_streams case.
448 if (include_rtx_streams) {
449 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200450 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700451 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
452 &rtx_ssrcs);
453 for (size_t i = 0; i < ssrcs.size(); ++i) {
454 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
455 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000456 }
brandtr03d5fb12016-11-22 03:37:59 -0800457 // Generate extra ssrc for include_flexfec_stream case.
458 if (include_flexfec_stream) {
459 // TODO(brandtr): Update when we support multistream protection.
460 if (ssrcs.size() == 1) {
461 std::vector<uint32_t> flexfec_ssrcs;
462 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
463 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
brandtr03d5fb12016-11-22 03:37:59 -0800464 } else if (!ssrcs.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100465 RTC_LOG(LS_WARNING)
brandtr03d5fb12016-11-22 03:37:59 -0800466 << "Our FlexFEC implementation only supports protecting "
Jonas Olsson45cc8902018-02-13 10:37:07 +0100467 "a single media streams. This session has multiple "
468 "media streams however, so no FlexFEC SSRC will be generated.";
brandtr03d5fb12016-11-22 03:37:59 -0800469 }
470 }
zhihuang1c378ed2017-08-17 14:10:50 -0700471 stream_param.cname = rtcp_cname;
Seth Hampson845e8782018-03-02 11:34:10 -0800472 stream_param.set_stream_ids(sender.stream_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000473 content_description->AddStream(stream_param);
474
475 // Store the new StreamParams in current_streams.
476 // This is necessary so that we can use the CNAME for other media types.
477 current_streams->push_back(stream_param);
478 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700479 // Use existing generated SSRCs/groups, but update the sync_label if
480 // necessary. This may be needed if a MediaStreamTrack was moved from one
481 // MediaStream to another.
Seth Hampson845e8782018-03-02 11:34:10 -0800482 param->set_stream_ids(sender.stream_ids);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000483 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484 }
485 }
486 return true;
487}
488
489// Updates the transport infos of the |sdesc| according to the given
490// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800491// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
492// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
494 SessionDescription* sdesc) {
495 // The bundle should not be empty.
496 if (!sdesc || !bundle_group.FirstContentName()) {
497 return false;
498 }
499
500 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700501 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502 const TransportInfo* selected_transport_info =
503 sdesc->GetTransportInfoByName(selected_content_name);
504 if (!selected_transport_info) {
505 return false;
506 }
507
508 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700509 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000510 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700511 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000512 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800513 ConnectionRole selected_connection_role =
514 selected_transport_info->description.connection_role;
Yves Gerey665174f2018-06-19 15:03:05 +0200515 for (TransportInfos::iterator it = sdesc->transport_infos().begin();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000516 it != sdesc->transport_infos().end(); ++it) {
517 if (bundle_group.HasContentName(it->content_name) &&
518 it->content_name != selected_content_name) {
519 it->description.ice_ufrag = selected_ufrag;
520 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800521 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522 }
523 }
524 return true;
525}
526
527// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
528// sets it to |cryptos|.
529static bool GetCryptosByName(const SessionDescription* sdesc,
530 const std::string& content_name,
531 CryptoParamsVec* cryptos) {
532 if (!sdesc || !cryptos) {
533 return false;
534 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000535 const ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800536 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537 return false;
538 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800539 *cryptos = content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000540 return true;
541}
542
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000543// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
544// which are not available in |filter|.
545static void PruneCryptos(const CryptoParamsVec& filter,
546 CryptoParamsVec* target_cryptos) {
547 if (!target_cryptos) {
548 return;
549 }
tzik21995802018-04-26 17:38:28 +0900550
551 target_cryptos->erase(
552 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
553 // Returns true if the |crypto|'s cipher_suite is not
554 // found in |filter|.
555 [&filter](const CryptoParams& crypto) {
556 for (const CryptoParams& entry : filter) {
557 if (entry.cipher_suite == crypto.cipher_suite)
558 return false;
559 }
560 return true;
561 }),
562 target_cryptos->end());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563}
564
Steve Antonfa2260d2017-12-28 16:38:23 -0800565bool IsRtpProtocol(const std::string& protocol) {
deadbeefb5cb19b2015-11-23 16:39:12 -0800566 return protocol.empty() ||
567 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
568}
569
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570static bool IsRtpContent(SessionDescription* sdesc,
571 const std::string& content_name) {
572 bool is_rtp = false;
573 ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800574 if (content && content->media_description()) {
575 is_rtp = IsRtpProtocol(content->media_description()->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000576 }
577 return is_rtp;
578}
579
580// Updates the crypto parameters of the |sdesc| according to the given
581// |bundle_group|. The crypto parameters of all the contents within the
582// |bundle_group| should be updated to use the common subset of the
583// available cryptos.
584static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
585 SessionDescription* sdesc) {
586 // The bundle should not be empty.
587 if (!sdesc || !bundle_group.FirstContentName()) {
588 return false;
589 }
590
wu@webrtc.org78187522013-10-07 23:32:02 +0000591 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 // Get the common cryptos.
593 const ContentNames& content_names = bundle_group.content_names();
594 CryptoParamsVec common_cryptos;
595 for (ContentNames::const_iterator it = content_names.begin();
596 it != content_names.end(); ++it) {
597 if (!IsRtpContent(sdesc, *it)) {
598 continue;
599 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000600 // The common cryptos are needed if any of the content does not have DTLS
601 // enabled.
602 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
603 common_cryptos_needed = true;
604 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 if (it == content_names.begin()) {
606 // Initial the common_cryptos with the first content in the bundle group.
607 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
608 return false;
609 }
610 if (common_cryptos.empty()) {
611 // If there's no crypto params, we should just return.
612 return true;
613 }
614 } else {
615 CryptoParamsVec cryptos;
616 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
617 return false;
618 }
619 PruneCryptos(cryptos, &common_cryptos);
620 }
621 }
622
wu@webrtc.org78187522013-10-07 23:32:02 +0000623 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000624 return false;
625 }
626
627 // Update to use the common cryptos.
628 for (ContentNames::const_iterator it = content_names.begin();
629 it != content_names.end(); ++it) {
630 if (!IsRtpContent(sdesc, *it)) {
631 continue;
632 }
633 ContentInfo* content = sdesc->GetContentByName(*it);
634 if (IsMediaContent(content)) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800635 MediaContentDescription* media_desc = content->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636 if (!media_desc) {
637 return false;
638 }
639 media_desc->set_cryptos(common_cryptos);
640 }
641 }
642 return true;
643}
644
645template <class C>
646static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800647 for (const auto& codec : codecs) {
648 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000649 return true;
650 }
651 }
652 return false;
653}
654
655template <class C>
656static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800657 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000658}
659
brandtr03d5fb12016-11-22 03:37:59 -0800660template <class C>
661static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
662 for (const auto& codec : codecs) {
663 if (IsFlexfecCodec(codec)) {
664 return true;
665 }
666 }
667 return false;
668}
669
670template <class C>
671static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800672 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800673}
674
zhihuang1c378ed2017-08-17 14:10:50 -0700675// Create a media content to be offered for the given |sender_options|,
676// according to the given options.rtcp_mux, session_options.is_muc, codecs,
677// secure_transport, crypto, and current_streams. If we don't currently have
678// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
679// created (according to crypto_suites). The created content is added to the
680// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000681template <class C>
682static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -0700683 const std::vector<SenderOptions>& sender_options,
684 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000685 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000686 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000687 const CryptoParamsVec* current_cryptos,
688 const std::vector<std::string>& crypto_suites,
689 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 StreamParamsVec* current_streams,
691 MediaContentDescriptionImpl<C>* offer) {
692 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693
zhihuang1c378ed2017-08-17 14:10:50 -0700694 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700695 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
696 offer->set_rtcp_reduced_size(true);
697 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000698 offer->set_rtp_header_extensions(rtp_extensions);
699
zhihuang1c378ed2017-08-17 14:10:50 -0700700 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
701 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000702 return false;
703 }
704
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000705 if (secure_policy != SEC_DISABLED) {
706 if (current_cryptos) {
707 AddMediaCryptos(*current_cryptos, offer);
708 }
709 if (offer->cryptos().empty()) {
710 if (!CreateMediaCryptos(crypto_suites, offer)) {
711 return false;
712 }
713 }
714 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715
deadbeef7af91dd2016-12-13 11:29:11 -0800716 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000717 return false;
718 }
719 return true;
720}
721
722template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000723static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800724 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000725 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800726 const int codec2_id) {
727 const C* codec1 = FindCodecById(codecs1, codec1_id);
728 const C* codec2 = FindCodecById(codecs2, codec2_id);
729 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000730}
731
732template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733static void NegotiateCodecs(const std::vector<C>& local_codecs,
734 const std::vector<C>& offered_codecs,
735 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800736 for (const C& ours : local_codecs) {
737 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700738 // Note that we intentionally only find one matching codec for each of our
739 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800740 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
741 C negotiated = ours;
742 negotiated.IntersectFeedbackParams(theirs);
743 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800744 const auto apt_it =
745 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800746 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800747 RTC_DCHECK(apt_it != theirs.params.end());
748 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000749 }
magjedf823ede2016-11-12 09:53:04 -0800750 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
751 webrtc::H264::GenerateProfileLevelIdForAnswer(
752 ours.params, theirs.params, &negotiated.params);
753 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800754 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700755 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800756 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757 }
758 }
deadbeef67cf2c12016-04-13 10:07:16 -0700759 // RFC3264: Although the answerer MAY list the formats in their desired
760 // order of preference, it is RECOMMENDED that unless there is a
761 // specific reason, the answerer list formats in the same relative order
762 // they were present in the offer.
763 std::unordered_map<int, int> payload_type_preferences;
764 int preference = static_cast<int>(offered_codecs.size() + 1);
765 for (const C& codec : offered_codecs) {
766 payload_type_preferences[codec.id] = preference--;
767 }
768 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
769 [&payload_type_preferences](const C& a, const C& b) {
770 return payload_type_preferences[a.id] >
771 payload_type_preferences[b.id];
772 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000773}
774
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800775// Finds a codec in |codecs2| that matches |codec_to_match|, which is
776// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
777// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800779static bool FindMatchingCodec(const std::vector<C>& codecs1,
780 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000781 const C& codec_to_match,
782 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700783 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
784 // codecs' associated codecs correctly. If not, that's a programming error.
785 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
786 [&codec_to_match](const C& codec) {
787 return &codec == &codec_to_match;
788 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800789 for (const C& potential_match : codecs2) {
790 if (potential_match.Matches(codec_to_match)) {
791 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800792 int apt_value_1 = 0;
793 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800794 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
795 &apt_value_1) ||
796 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
797 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100798 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800799 continue;
800 }
801 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
802 apt_value_2)) {
803 continue;
804 }
805 }
806 if (found_codec) {
807 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000808 }
809 return true;
810 }
811 }
812 return false;
813}
814
zhihuang1c378ed2017-08-17 14:10:50 -0700815// Find the codec in |codec_list| that |rtx_codec| is associated with.
816template <class C>
817static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
818 const C& rtx_codec) {
819 std::string associated_pt_str;
820 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
821 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100822 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
823 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 14:10:50 -0700824 return nullptr;
825 }
826
827 int associated_pt;
828 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100829 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
830 << " of RTX codec " << rtx_codec.name
831 << " to an integer.";
zhihuang1c378ed2017-08-17 14:10:50 -0700832 return nullptr;
833 }
834
835 // Find the associated reference codec for the reference RTX codec.
836 const C* associated_codec = FindCodecById(codec_list, associated_pt);
837 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100838 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
839 << associated_pt << " for RTX codec " << rtx_codec.name
840 << ".";
zhihuang1c378ed2017-08-17 14:10:50 -0700841 }
842 return associated_codec;
843}
844
845// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000846// already exist in |offered_codecs| and ensure the payload types don't
847// collide.
848template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700849static void MergeCodecs(const std::vector<C>& reference_codecs,
850 std::vector<C>* offered_codecs,
851 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800853 for (const C& reference_codec : reference_codecs) {
854 if (!IsRtxCodec(reference_codec) &&
855 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
856 reference_codec, nullptr)) {
857 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000858 used_pltypes->FindAndSetIdUsed(&codec);
859 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000860 }
861 }
862
863 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800864 for (const C& reference_codec : reference_codecs) {
865 if (IsRtxCodec(reference_codec) &&
866 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
867 reference_codec, nullptr)) {
868 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700869 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700870 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700871 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700872 continue;
873 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800874 // Find a codec in the offered list that matches the reference codec.
875 // Its payload type may be different than the reference codec.
876 C matching_codec;
877 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800878 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100879 RTC_LOG(LS_WARNING)
880 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800881 continue;
882 }
883
884 rtx_codec.params[kCodecParamAssociatedPayloadType] =
885 rtc::ToString(matching_codec.id);
886 used_pltypes->FindAndSetIdUsed(&rtx_codec);
887 offered_codecs->push_back(rtx_codec);
888 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 }
890}
891
zhihuang1c378ed2017-08-17 14:10:50 -0700892static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
893 const webrtc::RtpExtension& ext_to_match,
894 webrtc::RtpExtension* found_extension) {
895 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
896 it != extensions.end(); ++it) {
897 // We assume that all URIs are given in a canonical format.
898 if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
899 if (found_extension) {
900 *found_extension = *it;
901 }
902 return true;
903 }
904 }
905 return false;
906}
907
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000908static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700909 const webrtc::RtpExtension& ext_to_match,
910 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -0700911 // We assume that all URIs are given in a canonical format.
912 const webrtc::RtpExtension* found =
913 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
914 ext_to_match.uri);
915 if (!found) {
916 return false;
917 }
918 if (found_extension) {
919 *found_extension = *found;
920 }
921 return true;
922}
923
924static bool FindByUriWithEncryptionPreference(
925 const RtpHeaderExtensions& extensions,
Yves Gerey665174f2018-06-19 15:03:05 +0200926 const webrtc::RtpExtension& ext_to_match,
927 bool encryption_preference,
jbauch5869f502017-06-29 12:31:36 -0700928 webrtc::RtpExtension* found_extension) {
929 const webrtc::RtpExtension* unencrypted_extension = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000930 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
Yves Gerey665174f2018-06-19 15:03:05 +0200931 it != extensions.end(); ++it) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000932 // We assume that all URIs are given in a canonical format.
933 if (it->uri == ext_to_match.uri) {
jbauch5869f502017-06-29 12:31:36 -0700934 if (!encryption_preference || it->encrypt) {
935 if (found_extension) {
936 *found_extension = *it;
937 }
938 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000939 }
jbauch5869f502017-06-29 12:31:36 -0700940 unencrypted_extension = &(*it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000941 }
942 }
jbauch5869f502017-06-29 12:31:36 -0700943 if (unencrypted_extension) {
944 if (found_extension) {
945 *found_extension = *unencrypted_extension;
946 }
947 return true;
948 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000949 return false;
950}
951
zhihuang1c378ed2017-08-17 14:10:50 -0700952// Adds all extensions from |reference_extensions| to |offered_extensions| that
953// don't already exist in |offered_extensions| and ensure the IDs don't
954// collide. If an extension is added, it's also added to |regular_extensions| or
955// |encrypted_extensions|, and if the extension is in |regular_extensions| or
956// |encrypted_extensions|, its ID is marked as used in |used_ids|.
957// |offered_extensions| is for either audio or video while |regular_extensions|
958// and |encrypted_extensions| are used for both audio and video. There could be
959// overlap between audio extensions and video extensions.
960static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
961 RtpHeaderExtensions* offered_extensions,
962 RtpHeaderExtensions* regular_extensions,
963 RtpHeaderExtensions* encrypted_extensions,
964 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -0700965 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -0700966 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
967 nullptr)) {
olka3c747662017-08-17 06:50:32 -0700968 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -0700969 if (reference_extension.encrypt) {
970 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
971 &existing)) {
972 offered_extensions->push_back(existing);
973 } else {
974 used_ids->FindAndSetIdUsed(&reference_extension);
975 encrypted_extensions->push_back(reference_extension);
976 offered_extensions->push_back(reference_extension);
977 }
olka3c747662017-08-17 06:50:32 -0700978 } else {
zhihuang1c378ed2017-08-17 14:10:50 -0700979 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
980 &existing)) {
981 offered_extensions->push_back(existing);
982 } else {
983 used_ids->FindAndSetIdUsed(&reference_extension);
984 regular_extensions->push_back(reference_extension);
985 offered_extensions->push_back(reference_extension);
986 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000987 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000988 }
989 }
990}
991
jbauch5869f502017-06-29 12:31:36 -0700992static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
993 RtpHeaderExtensions* all_extensions,
994 UsedRtpHeaderExtensionIds* used_ids) {
995 RtpHeaderExtensions encrypted_extensions;
996 for (const webrtc::RtpExtension& extension : *extensions) {
997 webrtc::RtpExtension existing;
998 // Don't add encrypted extensions again that were already included in a
999 // previous offer or regular extensions that are also included as encrypted
1000 // extensions.
1001 if (extension.encrypt ||
1002 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1003 (FindByUriWithEncryptionPreference(*extensions, extension, true,
Yves Gerey665174f2018-06-19 15:03:05 +02001004 &existing) &&
1005 existing.encrypt)) {
jbauch5869f502017-06-29 12:31:36 -07001006 continue;
1007 }
1008
1009 if (FindByUri(*all_extensions, extension, &existing)) {
1010 encrypted_extensions.push_back(existing);
1011 } else {
1012 webrtc::RtpExtension encrypted(extension);
1013 encrypted.encrypt = true;
1014 used_ids->FindAndSetIdUsed(&encrypted);
1015 all_extensions->push_back(encrypted);
1016 encrypted_extensions.push_back(encrypted);
1017 }
1018 }
1019 extensions->insert(extensions->end(), encrypted_extensions.begin(),
Yves Gerey665174f2018-06-19 15:03:05 +02001020 encrypted_extensions.end());
jbauch5869f502017-06-29 12:31:36 -07001021}
1022
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001023static void NegotiateRtpHeaderExtensions(
1024 const RtpHeaderExtensions& local_extensions,
1025 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001026 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001027 RtpHeaderExtensions* negotiated_extenstions) {
1028 RtpHeaderExtensions::const_iterator ours;
Yves Gerey665174f2018-06-19 15:03:05 +02001029 for (ours = local_extensions.begin(); ours != local_extensions.end();
1030 ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001031 webrtc::RtpExtension theirs;
Yves Gerey665174f2018-06-19 15:03:05 +02001032 if (FindByUriWithEncryptionPreference(
1033 offered_extensions, *ours, enable_encrypted_rtp_header_extensions,
1034 &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001035 // We respond with their RTP header extension id.
1036 negotiated_extenstions->push_back(theirs);
1037 }
1038 }
1039}
1040
1041static void StripCNCodecs(AudioCodecs* audio_codecs) {
1042 AudioCodecs::iterator iter = audio_codecs->begin();
1043 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001044 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001045 iter = audio_codecs->erase(iter);
1046 } else {
1047 ++iter;
1048 }
1049 }
1050}
1051
zhihuang1c378ed2017-08-17 14:10:50 -07001052// Create a media content to be answered for the given |sender_options|
1053// according to the given session_options.rtcp_mux, session_options.streams,
1054// codecs, crypto, and current_streams. If we don't currently have crypto (in
1055// current_cryptos) and it is enabled (in secure_policy), crypto is created
1056// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1057// negotiated with the offer. If the negotiation fails, this method returns
1058// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001059template <class C>
1060static bool CreateMediaContentAnswer(
1061 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001062 const MediaDescriptionOptions& media_description_options,
1063 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001064 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001065 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001066 const CryptoParamsVec* current_cryptos,
1067 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 12:31:36 -07001068 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001069 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001070 bool bundle_enabled,
1071 MediaContentDescriptionImpl<C>* answer) {
1072 std::vector<C> negotiated_codecs;
1073 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1074 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001075 answer->set_protocol(offer->protocol());
1076 RtpHeaderExtensions negotiated_rtp_extensions;
Yves Gerey665174f2018-06-19 15:03:05 +02001077 NegotiateRtpHeaderExtensions(
1078 local_rtp_extenstions, offer->rtp_header_extensions(),
1079 enable_encrypted_rtp_header_extensions, &negotiated_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001080 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1081
zhihuang1c378ed2017-08-17 14:10:50 -07001082 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001083 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1084 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1085 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001086
1087 if (sdes_policy != SEC_DISABLED) {
1088 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001089 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1090 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001091 if (current_cryptos) {
1092 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1093 }
1094 answer->AddCrypto(crypto);
1095 }
1096 }
1097
deadbeef7af91dd2016-12-13 11:29:11 -08001098 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 return false;
1100 }
1101
zhihuang1c378ed2017-08-17 14:10:50 -07001102 if (!AddStreamParams(media_description_options.sender_options,
1103 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001104 return false; // Something went seriously wrong.
1105 }
1106
Steve Anton4e70a722017-11-28 14:57:10 -08001107 answer->set_direction(NegotiateRtpTransceiverDirection(
1108 offer->direction(), media_description_options.direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001109 return true;
1110}
1111
1112static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001113 const std::string& protocol,
1114 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001115 // Since not all applications serialize and deserialize the media protocol,
1116 // we will have to accept |protocol| to be empty.
1117 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001118 return true;
1119 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001120
zhihuangcf5b37c2016-05-05 11:44:35 -07001121 if (type == MEDIA_TYPE_DATA) {
1122 // Check for SCTP, but also for RTP for RTP-based data channels.
1123 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1124 if (secure_transport) {
1125 // Most likely scenarios first.
1126 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1127 IsPlainRtp(protocol);
1128 } else {
1129 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1130 }
1131 }
1132
1133 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1134 // JSEP specifies.
1135 if (secure_transport) {
1136 // Most likely scenarios first.
1137 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1138 } else {
1139 return IsPlainRtp(protocol);
1140 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141}
1142
1143static void SetMediaProtocol(bool secure_transport,
1144 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001145 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001146 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001147 else if (secure_transport)
1148 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001149 else
1150 desc->set_protocol(kMediaProtocolAvpf);
1151}
1152
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001153// Gets the TransportInfo of the given |content_name| from the
1154// |current_description|. If doesn't exist, returns a new one.
1155static const TransportDescription* GetTransportDescription(
1156 const std::string& content_name,
1157 const SessionDescription* current_description) {
1158 const TransportDescription* desc = NULL;
1159 if (current_description) {
1160 const TransportInfo* info =
1161 current_description->GetTransportInfoByName(content_name);
1162 if (info) {
1163 desc = &info->description;
1164 }
1165 }
1166 return desc;
1167}
1168
1169// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001170static bool IsDtlsActive(const ContentInfo* content,
1171 const SessionDescription* current_description) {
1172 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001173 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001174 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001175
zhihuang1c378ed2017-08-17 14:10:50 -07001176 size_t msection_index = content - &current_description->contents()[0];
1177
1178 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001179 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001180 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001181
zhihuang1c378ed2017-08-17 14:10:50 -07001182 return current_description->transport_infos()[msection_index]
1183 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001184}
1185
Steve Anton8ffb9c32017-08-31 15:45:38 -07001186void MediaDescriptionOptions::AddAudioSender(
1187 const std::string& track_id,
1188 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001189 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001190 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001191}
1192
Steve Anton8ffb9c32017-08-31 15:45:38 -07001193void MediaDescriptionOptions::AddVideoSender(
1194 const std::string& track_id,
1195 const std::vector<std::string>& stream_ids,
1196 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001197 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001198 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001199}
1200
zhihuang1c378ed2017-08-17 14:10:50 -07001201void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1202 const std::string& stream_id) {
1203 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001204 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1205 // than one stream?
1206 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207}
1208
Steve Anton8ffb9c32017-08-31 15:45:38 -07001209void MediaDescriptionOptions::AddSenderInternal(
1210 const std::string& track_id,
1211 const std::vector<std::string>& stream_ids,
1212 int num_sim_layers) {
1213 // TODO(steveanton): Support any number of stream ids.
1214 RTC_CHECK(stream_ids.size() == 1U);
1215 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001216}
1217
zhihuang1c378ed2017-08-17 14:10:50 -07001218bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1219 return std::find_if(media_description_options.begin(),
1220 media_description_options.end(),
1221 [type](const MediaDescriptionOptions& t) {
1222 return t.type == type;
1223 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001224}
1225
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001226MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1227 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001228 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001229
1230MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1231 ChannelManager* channel_manager,
1232 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001233 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001234 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1235 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001236 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001237 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001238 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1239 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001240 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001241}
1242
ossudedfd282016-06-14 07:12:39 -07001243const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1244 const {
ossu075af922016-06-14 03:29:38 -07001245 return audio_sendrecv_codecs_;
1246}
1247
1248const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1249 return audio_send_codecs_;
1250}
1251
1252const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1253 return audio_recv_codecs_;
1254}
1255
1256void MediaSessionDescriptionFactory::set_audio_codecs(
Yves Gerey665174f2018-06-19 15:03:05 +02001257 const AudioCodecs& send_codecs,
1258 const AudioCodecs& recv_codecs) {
ossu075af922016-06-14 03:29:38 -07001259 audio_send_codecs_ = send_codecs;
1260 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001261 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001262}
1263
1264SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001265 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001266 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001267 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001268
1269 StreamParamsVec current_streams;
1270 GetCurrentStreamParams(current_description, &current_streams);
1271
zhihuang1c378ed2017-08-17 14:10:50 -07001272 AudioCodecs offer_audio_codecs;
1273 VideoCodecs offer_video_codecs;
1274 DataCodecs offer_data_codecs;
1275 GetCodecsForOffer(current_description, &offer_audio_codecs,
1276 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001277
zhihuang1c378ed2017-08-17 14:10:50 -07001278 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001279 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001280 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001281 }
zhihuang1c378ed2017-08-17 14:10:50 -07001282 FilterDataCodecs(&offer_data_codecs,
1283 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001284
1285 RtpHeaderExtensions audio_rtp_extensions;
1286 RtpHeaderExtensions video_rtp_extensions;
Steve Anton1b8773d2018-04-06 11:13:34 -07001287 GetRtpHdrExtsToOffer(session_options, current_description,
1288 &audio_rtp_extensions, &video_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001289
zhihuang1c378ed2017-08-17 14:10:50 -07001290 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001291 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001292 RTC_DCHECK(current_description->contents().size() <=
1293 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001294 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001295
zhihuang1c378ed2017-08-17 14:10:50 -07001296 // Iterate through the media description options, matching with existing media
1297 // descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001298 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001299 for (const MediaDescriptionOptions& media_description_options :
1300 session_options.media_description_options) {
1301 const ContentInfo* current_content = nullptr;
1302 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001303 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001304 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-22 16:02:54 -08001305 // Media type must match unless this media section is being recycled.
1306 RTC_DCHECK(current_content->rejected ||
1307 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 14:10:50 -07001308 media_description_options.type));
1309 }
1310 switch (media_description_options.type) {
1311 case MEDIA_TYPE_AUDIO:
1312 if (!AddAudioContentForOffer(media_description_options, session_options,
1313 current_content, current_description,
1314 audio_rtp_extensions, offer_audio_codecs,
1315 &current_streams, offer.get())) {
1316 return nullptr;
1317 }
1318 break;
1319 case MEDIA_TYPE_VIDEO:
1320 if (!AddVideoContentForOffer(media_description_options, session_options,
1321 current_content, current_description,
1322 video_rtp_extensions, offer_video_codecs,
1323 &current_streams, offer.get())) {
1324 return nullptr;
1325 }
1326 break;
1327 case MEDIA_TYPE_DATA:
1328 if (!AddDataContentForOffer(media_description_options, session_options,
1329 current_content, current_description,
1330 offer_data_codecs, &current_streams,
1331 offer.get())) {
1332 return nullptr;
1333 }
1334 break;
1335 default:
1336 RTC_NOTREACHED();
1337 }
1338 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339 }
1340
1341 // Bundle the contents together, if we've been asked to do so, and update any
1342 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 14:10:50 -07001343 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001344 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001345 for (const ContentInfo& content : offer->contents()) {
1346 // TODO(deadbeef): There are conditions that make bundling two media
1347 // descriptions together illegal. For example, they use the same payload
1348 // type to represent different codecs, or same IDs for different header
1349 // extensions. We need to detect this and not try to bundle those media
1350 // descriptions together.
1351 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001352 }
1353 offer->AddGroup(offer_bundle);
1354 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001355 RTC_LOG(LS_ERROR)
1356 << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001357 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001358 }
1359 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001360 RTC_LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001361 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001362 }
1363 }
Steve Antone831b8c2018-02-01 12:22:16 -08001364
1365 // The following determines how to signal MSIDs to ensure compatibility with
1366 // older endpoints (in particular, older Plan B endpoints).
1367 if (session_options.is_unified_plan) {
1368 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1369 // Plan answerers will look at a=msid and Plan B answerers will look at the
1370 // a=ssrc MSID line.
1371 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1372 cricket::kMsidSignalingSsrcAttribute);
1373 } else {
1374 // Plan B always signals MSID using a=ssrc lines.
1375 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1376 }
1377
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001378 return offer.release();
1379}
1380
1381SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001382 const SessionDescription* offer,
1383 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001384 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001385 if (!offer) {
1386 return nullptr;
1387 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001388 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001389 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1390 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001391 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001392
1393 StreamParamsVec current_streams;
1394 GetCurrentStreamParams(current_description, &current_streams);
1395
deadbeefb7892532017-02-22 19:35:18 -08001396 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1397 // group in the answer with the appropriate content names.
1398 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1399 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1400 // Transport info shared by the bundle group.
1401 std::unique_ptr<TransportInfo> bundle_transport;
1402
zhihuang1c378ed2017-08-17 14:10:50 -07001403 // Get list of all possible codecs that respects existing payload type
1404 // mappings and uses a single payload type space.
1405 //
1406 // Note that these lists may be further filtered for each m= section; this
1407 // step is done just to establish the payload type mappings shared by all
1408 // sections.
1409 AudioCodecs answer_audio_codecs;
1410 VideoCodecs answer_video_codecs;
1411 DataCodecs answer_data_codecs;
1412 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1413 &answer_video_codecs, &answer_data_codecs);
1414
1415 if (!session_options.vad_enabled) {
1416 // If application doesn't want CN codecs in answer.
1417 StripCNCodecs(&answer_audio_codecs);
1418 }
1419 FilterDataCodecs(&answer_data_codecs,
1420 session_options.data_channel_type == DCT_SCTP);
1421
1422 // Must have options for exactly as many sections as in the offer.
1423 RTC_DCHECK(offer->contents().size() ==
1424 session_options.media_description_options.size());
1425 // Iterate through the media description options, matching with existing
1426 // media descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001427 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001428 for (const MediaDescriptionOptions& media_description_options :
1429 session_options.media_description_options) {
1430 const ContentInfo* offer_content = &offer->contents()[msection_index];
1431 // Media types and MIDs must match between the remote offer and the
1432 // MediaDescriptionOptions.
1433 RTC_DCHECK(
1434 IsMediaContentOfType(offer_content, media_description_options.type));
1435 RTC_DCHECK(media_description_options.mid == offer_content->name);
1436 const ContentInfo* current_content = nullptr;
1437 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001438 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001439 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001440 }
zhihuang1c378ed2017-08-17 14:10:50 -07001441 switch (media_description_options.type) {
1442 case MEDIA_TYPE_AUDIO:
1443 if (!AddAudioContentForAnswer(
1444 media_description_options, session_options, offer_content,
1445 offer, current_content, current_description,
1446 bundle_transport.get(), answer_audio_codecs, &current_streams,
1447 answer.get())) {
1448 return nullptr;
1449 }
1450 break;
1451 case MEDIA_TYPE_VIDEO:
1452 if (!AddVideoContentForAnswer(
1453 media_description_options, session_options, offer_content,
1454 offer, current_content, current_description,
1455 bundle_transport.get(), answer_video_codecs, &current_streams,
1456 answer.get())) {
1457 return nullptr;
1458 }
1459 break;
1460 case MEDIA_TYPE_DATA:
1461 if (!AddDataContentForAnswer(media_description_options, session_options,
1462 offer_content, offer, current_content,
1463 current_description,
1464 bundle_transport.get(), answer_data_codecs,
1465 &current_streams, answer.get())) {
1466 return nullptr;
1467 }
1468 break;
1469 default:
1470 RTC_NOTREACHED();
1471 }
1472 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001473 // See if we can add the newly generated m= section to the BUNDLE group in
1474 // the answer.
1475 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001476 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001477 offer_bundle->HasContentName(added.name)) {
1478 answer_bundle.AddContentName(added.name);
1479 bundle_transport.reset(
1480 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001481 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001482 }
1483
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001484 // If a BUNDLE group was offered, put a BUNDLE group in the answer even if
1485 // it's empty. RFC5888 says:
1486 //
1487 // A SIP entity that receives an offer that contains an "a=group" line
1488 // with semantics that are understood MUST return an answer that
1489 // contains an "a=group" line with the same semantics.
1490 if (offer_bundle) {
deadbeefb7892532017-02-22 19:35:18 -08001491 answer->AddGroup(answer_bundle);
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001492 }
deadbeefb7892532017-02-22 19:35:18 -08001493
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001494 if (answer_bundle.FirstContentName()) {
deadbeefb7892532017-02-22 19:35:18 -08001495 // Share the same ICE credentials and crypto params across all contents,
1496 // as BUNDLE requires.
1497 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001498 RTC_LOG(LS_ERROR)
1499 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001500 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001501 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001502
deadbeefb7892532017-02-22 19:35:18 -08001503 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001504 RTC_LOG(LS_ERROR)
1505 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001506 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001507 }
1508 }
1509
Steve Antone831b8c2018-02-01 12:22:16 -08001510 // The following determines how to signal MSIDs to ensure compatibility with
1511 // older endpoints (in particular, older Plan B endpoints).
1512 if (session_options.is_unified_plan) {
1513 // Unified Plan needs to look at what the offer included to find the most
1514 // compatible answer.
1515 if (offer->msid_signaling() == 0) {
1516 // We end up here in one of three cases:
1517 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1518 // matter what we pick here.
1519 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1520 // it also doesn't matter what we pick here.
1521 // 3. Media that's either sendonly or inactive from the remote endpoint.
1522 // We don't have any information to say whether the endpoint is Plan B
1523 // or Unified Plan, so be conservative and send both.
1524 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1525 cricket::kMsidSignalingSsrcAttribute);
1526 } else if (offer->msid_signaling() ==
1527 (cricket::kMsidSignalingMediaSection |
1528 cricket::kMsidSignalingSsrcAttribute)) {
1529 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1530 // probably talking to a Unified Plan endpoint so respond with just
1531 // a=msid.
1532 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1533 } else {
1534 // Otherwise, it's clear which method the offerer is using so repeat that
1535 // back to them.
1536 answer->set_msid_signaling(offer->msid_signaling());
1537 }
1538 } else {
1539 // Plan B always signals MSID using a=ssrc lines.
1540 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1541 }
1542
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001543 return answer.release();
1544}
1545
ossu075af922016-06-14 03:29:38 -07001546const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1547 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001548 switch (direction) {
1549 // If stream is inactive - generate list as if sendrecv.
1550 case RtpTransceiverDirection::kSendRecv:
1551 case RtpTransceiverDirection::kInactive:
1552 return audio_sendrecv_codecs_;
1553 case RtpTransceiverDirection::kSendOnly:
1554 return audio_send_codecs_;
1555 case RtpTransceiverDirection::kRecvOnly:
1556 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001557 }
Steve Anton1d03a752017-11-27 14:30:09 -08001558 RTC_NOTREACHED();
1559 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001560}
1561
1562const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1563 const RtpTransceiverDirection& offer,
1564 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001565 switch (answer) {
1566 // For inactive and sendrecv answers, generate lists as if we were to accept
1567 // the offer's direction. See RFC 3264 Section 6.1.
1568 case RtpTransceiverDirection::kSendRecv:
1569 case RtpTransceiverDirection::kInactive:
1570 return GetAudioCodecsForOffer(
1571 webrtc::RtpTransceiverDirectionReversed(offer));
1572 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 03:29:38 -07001573 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 14:30:09 -08001574 case RtpTransceiverDirection::kRecvOnly:
1575 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001576 }
Steve Anton1d03a752017-11-27 14:30:09 -08001577 RTC_NOTREACHED();
1578 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001579}
1580
zhihuang1c378ed2017-08-17 14:10:50 -07001581void MergeCodecsFromDescription(const SessionDescription* description,
1582 AudioCodecs* audio_codecs,
1583 VideoCodecs* video_codecs,
1584 DataCodecs* data_codecs,
1585 UsedPayloadTypes* used_pltypes) {
1586 RTC_DCHECK(description);
1587 for (const ContentInfo& content : description->contents()) {
1588 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1589 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001590 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001591 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1592 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1593 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001594 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001595 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1596 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1597 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001598 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001599 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1600 }
1601 }
1602}
1603
1604// Getting codecs for an offer involves these steps:
1605//
1606// 1. Construct payload type -> codec mappings for current description.
1607// 2. Add any reference codecs that weren't already present
1608// 3. For each individual media description (m= section), filter codecs based
1609// on the directional attribute (happens in another method).
1610void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001611 const SessionDescription* current_description,
1612 AudioCodecs* audio_codecs,
1613 VideoCodecs* video_codecs,
1614 DataCodecs* data_codecs) const {
1615 UsedPayloadTypes used_pltypes;
1616 audio_codecs->clear();
1617 video_codecs->clear();
1618 data_codecs->clear();
1619
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001620 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001621 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1622 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001623 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001624 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1625 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001626 }
1627
1628 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001629 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1630 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1631 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1632}
1633
1634// Getting codecs for an answer involves these steps:
1635//
1636// 1. Construct payload type -> codec mappings for current description.
1637// 2. Add any codecs from the offer that weren't already present.
1638// 3. Add any remaining codecs that weren't already present.
1639// 4. For each individual media description (m= section), filter codecs based
1640// on the directional attribute (happens in another method).
1641void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1642 const SessionDescription* current_description,
1643 const SessionDescription* remote_offer,
1644 AudioCodecs* audio_codecs,
1645 VideoCodecs* video_codecs,
1646 DataCodecs* data_codecs) const {
1647 UsedPayloadTypes used_pltypes;
1648 audio_codecs->clear();
1649 video_codecs->clear();
1650 data_codecs->clear();
1651
1652 // First - get all codecs from the current description if the media type
1653 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1654 // new media type is added.
1655 if (current_description) {
1656 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1657 data_codecs, &used_pltypes);
1658 }
1659
1660 // Second - filter out codecs that we don't support at all and should ignore.
1661 AudioCodecs filtered_offered_audio_codecs;
1662 VideoCodecs filtered_offered_video_codecs;
1663 DataCodecs filtered_offered_data_codecs;
1664 for (const ContentInfo& content : remote_offer->contents()) {
1665 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1666 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001667 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001668 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1669 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1670 filtered_offered_audio_codecs,
1671 offered_audio_codec, nullptr) &&
1672 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1673 offered_audio_codec, nullptr)) {
1674 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1675 }
1676 }
1677 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1678 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001679 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001680 for (const VideoCodec& offered_video_codec : video->codecs()) {
1681 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1682 filtered_offered_video_codecs,
1683 offered_video_codec, nullptr) &&
1684 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1685 offered_video_codec, nullptr)) {
1686 filtered_offered_video_codecs.push_back(offered_video_codec);
1687 }
1688 }
1689 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1690 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001691 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001692 for (const DataCodec& offered_data_codec : data->codecs()) {
1693 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1694 filtered_offered_data_codecs,
1695 offered_data_codec, nullptr) &&
1696 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1697 offered_data_codec, nullptr)) {
1698 filtered_offered_data_codecs.push_back(offered_data_codec);
1699 }
1700 }
1701 }
1702 }
1703
1704 // Add codecs that are not in |current_description| but were in
1705 // |remote_offer|.
1706 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1707 &used_pltypes);
1708 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1709 &used_pltypes);
1710 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1711 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001712}
1713
1714void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
Steve Anton1b8773d2018-04-06 11:13:34 -07001715 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001716 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001717 RtpHeaderExtensions* offer_audio_extensions,
1718 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001719 // All header extensions allocated from the same range to avoid potential
1720 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001721 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001722 RtpHeaderExtensions all_regular_extensions;
1723 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 14:10:50 -07001724 offer_audio_extensions->clear();
1725 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001726
1727 // First - get all extensions from the current description if the media type
1728 // is used.
1729 // Add them to |used_ids| so the local ids are not reused if a new media
1730 // type is added.
1731 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001732 for (const ContentInfo& content : current_description->contents()) {
1733 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1734 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001735 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001736 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1737 &all_regular_extensions, &all_encrypted_extensions,
1738 &used_ids);
1739 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1740 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001741 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001742 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1743 &all_regular_extensions, &all_encrypted_extensions,
1744 &used_ids);
1745 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001746 }
1747 }
1748
1749 // Add our default RTP header extensions that are not in
1750 // |current_description|.
Steve Anton1b8773d2018-04-06 11:13:34 -07001751 MergeRtpHdrExts(audio_rtp_header_extensions(session_options.is_unified_plan),
1752 offer_audio_extensions, &all_regular_extensions,
1753 &all_encrypted_extensions, &used_ids);
1754 MergeRtpHdrExts(video_rtp_header_extensions(session_options.is_unified_plan),
1755 offer_video_extensions, &all_regular_extensions,
1756 &all_encrypted_extensions, &used_ids);
zhihuang1c378ed2017-08-17 14:10:50 -07001757
jbauch5869f502017-06-29 12:31:36 -07001758 // TODO(jbauch): Support adding encrypted header extensions to existing
1759 // sessions.
1760 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001761 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1762 &all_encrypted_extensions, &used_ids);
1763 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1764 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001765 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001766}
1767
1768bool MediaSessionDescriptionFactory::AddTransportOffer(
Yves Gerey665174f2018-06-19 15:03:05 +02001769 const std::string& content_name,
1770 const TransportOptions& transport_options,
1771 const SessionDescription* current_desc,
1772 SessionDescription* offer_desc) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001773 if (!transport_desc_factory_)
Yves Gerey665174f2018-06-19 15:03:05 +02001774 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001775 const TransportDescription* current_tdesc =
1776 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001777 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001778 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
Yves Gerey665174f2018-06-19 15:03:05 +02001779 bool ret =
1780 (new_tdesc.get() != NULL &&
1781 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001782 if (!ret) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001783 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
1784 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001785 }
1786 return ret;
1787}
1788
1789TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1790 const std::string& content_name,
1791 const SessionDescription* offer_desc,
1792 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08001793 const SessionDescription* current_desc,
1794 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001795 if (!transport_desc_factory_)
1796 return NULL;
1797 const TransportDescription* offer_tdesc =
1798 GetTransportDescription(content_name, offer_desc);
1799 const TransportDescription* current_tdesc =
1800 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08001801 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1802 require_transport_attributes,
1803 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001804}
1805
1806bool MediaSessionDescriptionFactory::AddTransportAnswer(
1807 const std::string& content_name,
1808 const TransportDescription& transport_desc,
1809 SessionDescription* answer_desc) const {
Yves Gerey665174f2018-06-19 15:03:05 +02001810 if (!answer_desc->AddTransportInfo(
1811 TransportInfo(content_name, transport_desc))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001812 RTC_LOG(LS_ERROR) << "Failed to AddTransportAnswer, content name="
1813 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001814 return false;
1815 }
1816 return true;
1817}
1818
zhihuang1c378ed2017-08-17 14:10:50 -07001819// |audio_codecs| = set of all possible codecs that can be used, with correct
1820// payload type mappings
1821//
1822// |supported_audio_codecs| = set of codecs that are supported for the direction
1823// of this m= section
1824//
1825// acd->codecs() = set of previously negotiated codecs for this m= section
1826//
1827// The payload types should come from audio_codecs, but the order should come
1828// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1829// change existing codec priority, and that new codecs are added with the right
1830// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001831bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001832 const MediaDescriptionOptions& media_description_options,
1833 const MediaSessionOptions& session_options,
1834 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001835 const SessionDescription* current_description,
1836 const RtpHeaderExtensions& audio_rtp_extensions,
1837 const AudioCodecs& audio_codecs,
1838 StreamParamsVec* current_streams,
1839 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001840 // Filter audio_codecs (which includes all codecs, with correctly remapped
1841 // payload types) based on transceiver direction.
1842 const AudioCodecs& supported_audio_codecs =
1843 GetAudioCodecsForOffer(media_description_options.direction);
1844
1845 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001846 // Add the codecs from current content if it exists and is not being recycled.
1847 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001848 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07001849 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001850 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001851 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001852 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1853 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001854 filtered_codecs.push_back(codec);
1855 }
1856 }
1857 }
1858 // Add other supported audio codecs.
1859 AudioCodec found_codec;
1860 for (const AudioCodec& codec : supported_audio_codecs) {
1861 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1862 codec, &found_codec) &&
1863 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1864 codec, nullptr)) {
1865 // Use the |found_codec| from |audio_codecs| because it has the correctly
1866 // mapped payload type.
1867 filtered_codecs.push_back(found_codec);
1868 }
1869 }
deadbeef44f08192015-12-15 16:20:09 -08001870
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001871 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001872 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1873 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001874
kwiberg31022942016-03-11 14:18:21 -08001875 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001876 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001877 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1878 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001879 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001880 media_description_options.sender_options, session_options,
1881 filtered_codecs, sdes_policy, GetCryptos(current_content),
1882 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001883 return false;
1884 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001885
1886 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1887 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001888
Steve Anton4e70a722017-11-28 14:57:10 -08001889 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001890
Steve Anton5adfafd2017-12-20 16:34:00 -08001891 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001892 media_description_options.stopped, audio.release());
1893 if (!AddTransportOffer(media_description_options.mid,
1894 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001895 current_description, desc)) {
1896 return false;
1897 }
1898
1899 return true;
1900}
1901
1902bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001903 const MediaDescriptionOptions& media_description_options,
1904 const MediaSessionOptions& session_options,
1905 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001906 const SessionDescription* current_description,
1907 const RtpHeaderExtensions& video_rtp_extensions,
1908 const VideoCodecs& video_codecs,
1909 StreamParamsVec* current_streams,
1910 SessionDescription* desc) const {
1911 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001912 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1913 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001914
kwiberg31022942016-03-11 14:18:21 -08001915 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001916 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001917 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1918 &crypto_suites);
1919
1920 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001921 // Add the codecs from current content if it exists and is not being recycled.
1922 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001923 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07001924 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001925 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001926 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001927 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07001928 nullptr)) {
1929 filtered_codecs.push_back(codec);
1930 }
1931 }
1932 }
1933 // Add other supported video codecs.
1934 VideoCodec found_codec;
1935 for (const VideoCodec& codec : video_codecs_) {
1936 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1937 &found_codec) &&
1938 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1939 nullptr)) {
1940 // Use the |found_codec| from |video_codecs| because it has the correctly
1941 // mapped payload type.
1942 filtered_codecs.push_back(found_codec);
1943 }
1944 }
1945
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001946 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001947 media_description_options.sender_options, session_options,
1948 filtered_codecs, sdes_policy, GetCryptos(current_content),
1949 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001950 return false;
1951 }
1952
zhihuang1c378ed2017-08-17 14:10:50 -07001953 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001954
1955 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1956 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001957
Steve Anton4e70a722017-11-28 14:57:10 -08001958 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001959
Steve Anton5adfafd2017-12-20 16:34:00 -08001960 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001961 media_description_options.stopped, video.release());
1962 if (!AddTransportOffer(media_description_options.mid,
1963 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001964 current_description, desc)) {
1965 return false;
1966 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001967 return true;
1968}
1969
1970bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001971 const MediaDescriptionOptions& media_description_options,
1972 const MediaSessionOptions& session_options,
1973 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001974 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001975 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001976 StreamParamsVec* current_streams,
1977 SessionDescription* desc) const {
1978 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1979
kwiberg31022942016-03-11 14:18:21 -08001980 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07001981 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
1982 // If the DataChannel type is not specified, use the DataChannel type in
1983 // the current description.
1984 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001985 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
Steve Antonb1c1de12017-12-21 15:14:30 -08001986 is_sctp = (current_content->media_description()->protocol() ==
1987 kMediaProtocolSctp);
zhihuang1c378ed2017-08-17 14:10:50 -07001988 }
deadbeef44f08192015-12-15 16:20:09 -08001989
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001990 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001991 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1992 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001993 std::vector<std::string> crypto_suites;
1994 if (is_sctp) {
1995 // SDES doesn't make sense for SCTP, so we disable it, and we only
1996 // get SDES crypto suites for RTP-based data channels.
1997 sdes_policy = cricket::SEC_DISABLED;
1998 // Unlike SetMediaProtocol below, we need to set the protocol
1999 // before we call CreateMediaContentOffer. Otherwise,
2000 // CreateMediaContentOffer won't know this is SCTP and will
2001 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 09:38:55 -07002002 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2003 // it's safe to do so. Older versions of webrtc would reject these
2004 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
Yves Gerey665174f2018-06-19 15:03:05 +02002005 data->set_protocol(secure_transport ? kMediaProtocolDtlsSctp
2006 : kMediaProtocolSctp);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002007 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002008 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -07002009 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002010 }
2011
zhihuang1c378ed2017-08-17 14:10:50 -07002012 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002013 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002014 media_description_options.sender_options, session_options,
2015 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2016 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002017 return false;
2018 }
2019
2020 if (is_sctp) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002021 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
zhihuang1c378ed2017-08-17 14:10:50 -07002022 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002023 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002024 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002025 SetMediaProtocol(secure_transport, data.get());
Steve Anton5adfafd2017-12-20 16:34:00 -08002026 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07002027 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002028 }
zhihuang1c378ed2017-08-17 14:10:50 -07002029 if (!AddTransportOffer(media_description_options.mid,
2030 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002031 current_description, desc)) {
2032 return false;
2033 }
2034 return true;
2035}
2036
zhihuang1c378ed2017-08-17 14:10:50 -07002037// |audio_codecs| = set of all possible codecs that can be used, with correct
2038// payload type mappings
2039//
2040// |supported_audio_codecs| = set of codecs that are supported for the direction
2041// of this m= section
2042//
2043// acd->codecs() = set of previously negotiated codecs for this m= section
2044//
2045// The payload types should come from audio_codecs, but the order should come
2046// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2047// change existing codec priority, and that new codecs are added with the right
2048// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002049bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002050 const MediaDescriptionOptions& media_description_options,
2051 const MediaSessionOptions& session_options,
2052 const ContentInfo* offer_content,
2053 const SessionDescription* offer_description,
2054 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002055 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002056 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002057 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002058 StreamParamsVec* current_streams,
2059 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002060 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002061 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002062 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002063
deadbeefb7892532017-02-22 19:35:18 -08002064 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002065 CreateTransportAnswer(media_description_options.mid, offer_description,
2066 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002067 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002068 if (!audio_transport) {
2069 return false;
2070 }
2071
zhihuang1c378ed2017-08-17 14:10:50 -07002072 // Pick codecs based on the requested communications direction in the offer
2073 // and the selected direction in the answer.
2074 // Note these will be filtered one final time in CreateMediaContentAnswer.
2075 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 14:57:10 -08002076 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 03:29:38 -07002077 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002078 AudioCodecs supported_audio_codecs =
2079 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2080
2081 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002082 // Add the codecs from current content if it exists and is not being recycled.
2083 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002084 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002085 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002086 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07002087 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002088 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2089 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07002090 filtered_codecs.push_back(codec);
2091 }
2092 }
2093 }
2094 // Add other supported audio codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002095 for (const AudioCodec& codec : supported_audio_codecs) {
2096 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Zhi Huang6f367472017-11-22 13:20:02 -08002097 codec, nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002098 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2099 codec, nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002100 // We should use the local codec with local parameters and the codec id
2101 // would be correctly mapped in |NegotiateCodecs|.
2102 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002103 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002104 }
2105
zhihuang1c378ed2017-08-17 14:10:50 -07002106 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2107 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002108 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002109 new AudioContentDescription());
2110 // Do not require or create SDES cryptos if DTLS is used.
2111 cricket::SecurePolicy sdes_policy =
2112 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2113 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002114 offer_audio_description, media_description_options, session_options,
2115 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 11:13:34 -07002116 audio_rtp_header_extensions(session_options.is_unified_plan),
2117 enable_encrypted_rtp_header_extensions_, current_streams,
2118 bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002119 return false; // Fails the session setup.
2120 }
2121
deadbeefb7892532017-02-22 19:35:18 -08002122 bool secure = bundle_transport ? bundle_transport->description.secure()
2123 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002124 bool rejected = media_description_options.stopped ||
2125 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002126 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2127 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002128 if (!AddTransportAnswer(media_description_options.mid,
2129 *(audio_transport.get()), answer)) {
2130 return false;
2131 }
2132
2133 if (rejected) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002134 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2135 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002136 }
2137
zhihuang1c378ed2017-08-17 14:10:50 -07002138 answer->AddContent(media_description_options.mid, offer_content->type,
2139 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002140 return true;
2141}
2142
2143bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002144 const MediaDescriptionOptions& media_description_options,
2145 const MediaSessionOptions& session_options,
2146 const ContentInfo* offer_content,
2147 const SessionDescription* offer_description,
2148 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002149 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002150 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002151 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002152 StreamParamsVec* current_streams,
2153 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002154 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002155 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002156 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002157
deadbeefb7892532017-02-22 19:35:18 -08002158 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002159 CreateTransportAnswer(media_description_options.mid, offer_description,
2160 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002161 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002162 if (!video_transport) {
2163 return false;
2164 }
2165
zhihuang1c378ed2017-08-17 14:10:50 -07002166 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002167 // Add the codecs from current content if it exists and is not being recycled.
2168 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002169 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002170 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002171 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002172 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002173 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07002174 nullptr)) {
2175 filtered_codecs.push_back(codec);
2176 }
2177 }
2178 }
2179 // Add other supported video codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002180 for (const VideoCodec& codec : video_codecs_) {
2181 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
Zhi Huang6f367472017-11-22 13:20:02 -08002182 nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002183 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2184 nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002185 // We should use the local codec with local parameters and the codec id
2186 // would be correctly mapped in |NegotiateCodecs|.
2187 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002188 }
2189 }
2190
2191 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2192 session_options.bundle_enabled;
2193
kwiberg31022942016-03-11 14:18:21 -08002194 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002195 new VideoContentDescription());
2196 // Do not require or create SDES cryptos if DTLS is used.
2197 cricket::SecurePolicy sdes_policy =
2198 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002199 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002200 offer_video_description, media_description_options, session_options,
2201 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 11:13:34 -07002202 video_rtp_header_extensions(session_options.is_unified_plan),
2203 enable_encrypted_rtp_header_extensions_, current_streams,
2204 bundle_enabled, video_answer.get())) {
zhihuang1c378ed2017-08-17 14:10:50 -07002205 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002206 }
deadbeefb7892532017-02-22 19:35:18 -08002207 bool secure = bundle_transport ? bundle_transport->description.secure()
2208 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002209 bool rejected = media_description_options.stopped ||
2210 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002211 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2212 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002213 if (!AddTransportAnswer(media_description_options.mid,
2214 *(video_transport.get()), answer)) {
2215 return false;
2216 }
2217
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002218 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002219 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002220 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002221 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2222 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002223 }
zhihuang1c378ed2017-08-17 14:10:50 -07002224 answer->AddContent(media_description_options.mid, offer_content->type,
2225 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002226 return true;
2227}
2228
2229bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002230 const MediaDescriptionOptions& media_description_options,
2231 const MediaSessionOptions& session_options,
2232 const ContentInfo* offer_content,
2233 const SessionDescription* offer_description,
2234 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002235 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002236 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002237 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002238 StreamParamsVec* current_streams,
2239 SessionDescription* answer) const {
deadbeefb7892532017-02-22 19:35:18 -08002240 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002241 CreateTransportAnswer(media_description_options.mid, offer_description,
2242 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002243 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002244 if (!data_transport) {
2245 return false;
2246 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247
kwiberg31022942016-03-11 14:18:21 -08002248 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002249 new DataContentDescription());
2250 // Do not require or create SDES cryptos if DTLS is used.
2251 cricket::SecurePolicy sdes_policy =
2252 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002253 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2254 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002255 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2256 const DataContentDescription* offer_data_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002257 offer_content->media_description()->as_data();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002258 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002259 offer_data_description, media_description_options, session_options,
2260 data_codecs, sdes_policy, GetCryptos(current_content),
2261 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2262 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002263 return false; // Fails the session setup.
2264 }
2265
zstein4b2e0822017-02-17 19:48:38 -08002266 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-17 19:48:38 -08002267 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2268 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2269
deadbeefb7892532017-02-22 19:35:18 -08002270 bool secure = bundle_transport ? bundle_transport->description.secure()
2271 : data_transport->secure();
2272
zhihuang1c378ed2017-08-17 14:10:50 -07002273 bool rejected = session_options.data_channel_type == DCT_NONE ||
2274 media_description_options.stopped ||
2275 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002276 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2277 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002278 if (!AddTransportAnswer(media_description_options.mid,
2279 *(data_transport.get()), answer)) {
2280 return false;
2281 }
2282
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002283 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002284 data_answer->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002285 } else {
2286 // RFC 3264
2287 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002288 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002289 }
zhihuang1c378ed2017-08-17 14:10:50 -07002290 answer->AddContent(media_description_options.mid, offer_content->type,
2291 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002292 return true;
2293}
2294
zhihuang1c378ed2017-08-17 14:10:50 -07002295void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2296 audio_sendrecv_codecs_.clear();
2297 all_audio_codecs_.clear();
2298 // Compute the audio codecs union.
2299 for (const AudioCodec& send : audio_send_codecs_) {
2300 all_audio_codecs_.push_back(send);
2301 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2302 send, nullptr)) {
2303 // It doesn't make sense to have an RTX codec we support sending but not
2304 // receiving.
2305 RTC_DCHECK(!IsRtxCodec(send));
2306 }
2307 }
2308 for (const AudioCodec& recv : audio_recv_codecs_) {
2309 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2310 recv, nullptr)) {
2311 all_audio_codecs_.push_back(recv);
2312 }
2313 }
2314 // Use NegotiateCodecs to merge our codec lists, since the operation is
2315 // essentially the same. Put send_codecs as the offered_codecs, which is the
2316 // order we'd like to follow. The reasoning is that encoding is usually more
2317 // expensive than decoding, and prioritizing a codec in the send list probably
2318 // means it's a codec we can handle efficiently.
2319 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2320 &audio_sendrecv_codecs_);
2321}
2322
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002323bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002324 return (content && (content->type == MediaProtocolType::kRtp ||
2325 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002326}
2327
2328bool IsAudioContent(const ContentInfo* content) {
2329 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2330}
2331
2332bool IsVideoContent(const ContentInfo* content) {
2333 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2334}
2335
2336bool IsDataContent(const ContentInfo* content) {
2337 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2338}
2339
deadbeef0ed85b22016-02-23 17:24:52 -08002340const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2341 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002342 for (const ContentInfo& content : contents) {
2343 if (IsMediaContentOfType(&content, media_type)) {
2344 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002345 }
2346 }
deadbeef0ed85b22016-02-23 17:24:52 -08002347 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002348}
2349
2350const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2351 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2352}
2353
2354const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2355 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2356}
2357
2358const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2359 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2360}
2361
Steve Antonad7bffc2018-01-22 10:21:56 -08002362const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2363 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002364 if (sdesc == nullptr) {
2365 return nullptr;
2366 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002367
2368 return GetFirstMediaContent(sdesc->contents(), media_type);
2369}
2370
2371const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2372 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2373}
2374
2375const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2376 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2377}
2378
2379const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2380 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2381}
2382
2383const MediaContentDescription* GetFirstMediaContentDescription(
Yves Gerey665174f2018-06-19 15:03:05 +02002384 const SessionDescription* sdesc,
2385 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002386 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002387 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002388}
2389
2390const AudioContentDescription* GetFirstAudioContentDescription(
2391 const SessionDescription* sdesc) {
2392 return static_cast<const AudioContentDescription*>(
2393 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2394}
2395
2396const VideoContentDescription* GetFirstVideoContentDescription(
2397 const SessionDescription* sdesc) {
2398 return static_cast<const VideoContentDescription*>(
2399 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2400}
2401
2402const DataContentDescription* GetFirstDataContentDescription(
2403 const SessionDescription* sdesc) {
2404 return static_cast<const DataContentDescription*>(
2405 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2406}
2407
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002408//
2409// Non-const versions of the above functions.
2410//
2411
Steve Anton36b29d12017-10-30 09:57:42 -07002412ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002413 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002414 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002415 if (IsMediaContentOfType(&content, media_type)) {
2416 return &content;
2417 }
2418 }
2419 return nullptr;
2420}
2421
Steve Anton36b29d12017-10-30 09:57:42 -07002422ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002423 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2424}
2425
Steve Anton36b29d12017-10-30 09:57:42 -07002426ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002427 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2428}
2429
Steve Anton36b29d12017-10-30 09:57:42 -07002430ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002431 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2432}
2433
Steve Antonad7bffc2018-01-22 10:21:56 -08002434ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2435 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002436 if (sdesc == nullptr) {
2437 return nullptr;
2438 }
2439
Steve Anton36b29d12017-10-30 09:57:42 -07002440 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002441}
2442
2443ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2444 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2445}
2446
2447ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2448 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2449}
2450
2451ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2452 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2453}
2454
2455MediaContentDescription* GetFirstMediaContentDescription(
2456 SessionDescription* sdesc,
2457 MediaType media_type) {
2458 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002459 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002460}
2461
2462AudioContentDescription* GetFirstAudioContentDescription(
2463 SessionDescription* sdesc) {
2464 return static_cast<AudioContentDescription*>(
2465 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2466}
2467
2468VideoContentDescription* GetFirstVideoContentDescription(
2469 SessionDescription* sdesc) {
2470 return static_cast<VideoContentDescription*>(
2471 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2472}
2473
2474DataContentDescription* GetFirstDataContentDescription(
2475 SessionDescription* sdesc) {
2476 return static_cast<DataContentDescription*>(
2477 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2478}
2479
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002480} // namespace cricket