blob: 0eace22c6ac7102ddb3a291c3467cd37742c706d [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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/media_session.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
Elad Alon157540a2019-02-08 23:37:52 +010013#include <algorithm>
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
Steve Anton64b626b2019-01-28 17:25:26 -080021#include "absl/algorithm/container.h"
Steve Anton5c72e712018-12-10 14:25:30 -080022#include "absl/memory/memory.h"
Niels Möller2edab4c2018-10-22 09:48:08 +020023#include "absl/strings/match.h"
Danil Chapovalov66cadcc2018-06-19 16:47:43 +020024#include "absl/types/optional.h"
Steve Anton10542f22019-01-11 09:11:00 -080025#include "api/crypto_params.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "media/base/h264_profile_level_id.h"
Steve Anton10542f22019-01-11 09:11:00 -080027#include "media/base/media_constants.h"
28#include "p2p/base/p2p_constants.h"
29#include "pc/channel_manager.h"
30#include "pc/rtp_media_utils.h"
31#include "pc/srtp_filter.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020032#include "rtc_base/checks.h"
33#include "rtc_base/helpers.h"
34#include "rtc_base/logging.h"
Artem Titova76af0c2018-07-23 17:38:12 +020035#include "rtc_base/third_party/base64/base64.h"
Amit Hilbuchdbb49df2019-01-23 14:54:24 -080036#include "rtc_base/unique_id_generator.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037
38namespace {
Steve Anton1d03a752017-11-27 14:30:09 -080039
Amit Hilbuchdbb49df2019-01-23 14:54:24 -080040using rtc::UniqueRandomIdGenerator;
Steve Anton1d03a752017-11-27 14:30:09 -080041using webrtc::RtpTransceiverDirection;
42
henrike@webrtc.org28e20752013-07-10 00:45:36 +000043const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080044
Benjamin Wrighta54daf12018-10-11 15:33:17 -070045void GetSupportedSdesCryptoSuiteNames(
46 void (*func)(const webrtc::CryptoOptions&, std::vector<int>*),
47 const webrtc::CryptoOptions& crypto_options,
48 std::vector<std::string>* names) {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080049 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070050 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080051 for (const auto crypto : crypto_suites) {
52 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
53 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080054}
Elad Alon157540a2019-02-08 23:37:52 +010055
terelius8c011e52016-04-26 05:28:11 -070056} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000057
58namespace cricket {
59
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060// RTP Profile names
61// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
62// RFC4585
63const char kMediaProtocolAvpf[] = "RTP/AVPF";
64// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000065const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
66
deadbeeff3938292015-07-15 12:20:53 -070067// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
68// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000069const char kMediaProtocolSavpf[] = "RTP/SAVPF";
70
Harald Alvestrand141c0ad2019-05-05 19:00:00 +000071const char kMediaProtocolRtpPrefix[] = "RTP/";
72
73const char kMediaProtocolSctp[] = "SCTP";
74const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
75const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
76const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
77
deadbeef8b7e9ad2017-05-25 09:38:55 -070078// Note that the below functions support some protocol strings purely for
79// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
80// and Interoperability.
81
82static bool IsDtlsRtp(const std::string& protocol) {
83 // Most-likely values first.
84 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
85 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
86}
87
88static bool IsPlainRtp(const std::string& protocol) {
89 // Most-likely values first.
90 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
91 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
92}
93
Harald Alvestrand141c0ad2019-05-05 19:00:00 +000094static bool IsDtlsSctp(const std::string& protocol) {
95 return protocol == kMediaProtocolDtlsSctp ||
96 protocol == kMediaProtocolUdpDtlsSctp ||
97 protocol == kMediaProtocolTcpDtlsSctp;
98}
99
100static bool IsPlainSctp(const std::string& protocol) {
101 return protocol == kMediaProtocolSctp;
102}
103
104static bool IsSctp(const std::string& protocol) {
105 return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
106}
107
Steve Anton1d03a752017-11-27 14:30:09 -0800108static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
109 RtpTransceiverDirection offer,
110 RtpTransceiverDirection wants) {
111 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
112 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
113 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
114 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
115 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
116 offer_send && wants_recv);
ossu075af922016-06-14 03:29:38 -0700117}
118
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119static bool IsMediaContentOfType(const ContentInfo* content,
120 MediaType media_type) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800121 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122 return false;
123 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800124 return content->media_description()->type() == media_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000125}
126
Yves Gerey665174f2018-06-19 15:03:05 +0200127static bool CreateCryptoParams(int tag,
128 const std::string& cipher,
Steve Anton3a66edf2018-09-10 12:57:37 -0700129 CryptoParams* crypto_out) {
jbauchcb560652016-08-04 05:20:32 -0700130 int key_len;
131 int salt_len;
Yves Gerey665174f2018-06-19 15:03:05 +0200132 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
133 &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000134 return false;
135 }
jbauchcb560652016-08-04 05:20:32 -0700136
137 int master_key_len = key_len + salt_len;
138 std::string master_key;
139 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
140 return false;
141 }
142
kwiberg352444f2016-11-28 15:58:53 -0800143 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700144 std::string key = rtc::Base64::Encode(master_key);
145
Steve Anton3a66edf2018-09-10 12:57:37 -0700146 crypto_out->tag = tag;
147 crypto_out->cipher_suite = cipher;
148 crypto_out->key_params = kInline;
149 crypto_out->key_params += key;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150 return true;
151}
152
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153static bool AddCryptoParams(const std::string& cipher_suite,
Steve Anton3a66edf2018-09-10 12:57:37 -0700154 CryptoParamsVec* cryptos_out) {
155 int size = static_cast<int>(cryptos_out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000156
Steve Anton3a66edf2018-09-10 12:57:37 -0700157 cryptos_out->resize(size + 1);
158 return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159}
160
161void AddMediaCryptos(const CryptoParamsVec& cryptos,
162 MediaContentDescription* media) {
Steve Anton3a66edf2018-09-10 12:57:37 -0700163 for (const CryptoParams& crypto : cryptos) {
164 media->AddCrypto(crypto);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165 }
166}
167
168bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
169 MediaContentDescription* media) {
170 CryptoParamsVec cryptos;
Steve Anton3a66edf2018-09-10 12:57:37 -0700171 for (const std::string& crypto_suite : crypto_suites) {
172 if (!AddCryptoParams(crypto_suite, &cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173 return false;
174 }
175 }
176 AddMediaCryptos(cryptos, media);
177 return true;
178}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179
zhihuang1c378ed2017-08-17 14:10:50 -0700180const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800181 if (!content || !content->media_description()) {
zhihuang1c378ed2017-08-17 14:10:50 -0700182 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800184 return &content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185}
186
187bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
188 const CryptoParams& crypto,
Steve Anton3a66edf2018-09-10 12:57:37 -0700189 CryptoParams* crypto_out) {
Steve Anton64b626b2019-01-28 17:25:26 -0800190 auto it = absl::c_find_if(
191 cryptos, [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
Steve Anton3a66edf2018-09-10 12:57:37 -0700192 if (it == cryptos.end()) {
193 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194 }
Steve Anton3a66edf2018-09-10 12:57:37 -0700195 *crypto_out = *it;
196 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197}
198
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700199// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
200// low overhead.
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700201void GetSupportedAudioSdesCryptoSuites(
202 const webrtc::CryptoOptions& crypto_options,
203 std::vector<int>* crypto_suites) {
204 if (crypto_options.srtp.enable_gcm_crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700205 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
206 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
207 }
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700208 if (crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher) {
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700209 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
210 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800211 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212}
213
deadbeef7914b8c2017-04-21 03:23:33 -0700214void GetSupportedAudioSdesCryptoSuiteNames(
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700215 const webrtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800216 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700217 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
218 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219}
220
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700221void GetSupportedVideoSdesCryptoSuites(
222 const webrtc::CryptoOptions& crypto_options,
223 std::vector<int>* crypto_suites) {
224 if (crypto_options.srtp.enable_gcm_crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700225 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
226 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
227 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800228 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229}
230
deadbeef7914b8c2017-04-21 03:23:33 -0700231void GetSupportedVideoSdesCryptoSuiteNames(
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700232 const webrtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800233 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700234 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
235 crypto_options, crypto_suite_names);
236}
237
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700238void GetSupportedDataSdesCryptoSuites(
239 const webrtc::CryptoOptions& crypto_options,
240 std::vector<int>* crypto_suites) {
241 if (crypto_options.srtp.enable_gcm_crypto_suites) {
deadbeef7914b8c2017-04-21 03:23:33 -0700242 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
243 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
244 }
245 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
246}
247
248void GetSupportedDataSdesCryptoSuiteNames(
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700249 const webrtc::CryptoOptions& crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -0700250 std::vector<std::string>* crypto_suite_names) {
251 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
252 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800253}
254
jbauchcb560652016-08-04 05:20:32 -0700255// Support any GCM cipher (if enabled through options). For video support only
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700256// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
257// bundle is enabled because it is low overhead.
jbauchcb560652016-08-04 05:20:32 -0700258// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259static bool SelectCrypto(const MediaContentDescription* offer,
260 bool bundle,
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700261 const webrtc::CryptoOptions& crypto_options,
Steve Anton3a66edf2018-09-10 12:57:37 -0700262 CryptoParams* crypto_out) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
264 const CryptoParamsVec& cryptos = offer->cryptos();
265
Steve Anton3a66edf2018-09-10 12:57:37 -0700266 for (const CryptoParams& crypto : cryptos) {
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700267 if ((crypto_options.srtp.enable_gcm_crypto_suites &&
Steve Anton3a66edf2018-09-10 12:57:37 -0700268 rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
269 rtc::CS_AES_CM_128_HMAC_SHA1_80 == crypto.cipher_suite ||
270 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == crypto.cipher_suite && audio &&
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700271 !bundle && crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher)) {
Steve Anton3a66edf2018-09-10 12:57:37 -0700272 return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000273 }
274 }
275 return false;
276}
277
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278// Finds all StreamParams of all media types and attach them to stream_params.
Steve Anton5c72e712018-12-10 14:25:30 -0800279static StreamParamsVec GetCurrentStreamParams(
280 const std::vector<const ContentInfo*>& active_local_contents) {
281 StreamParamsVec stream_params;
282 for (const ContentInfo* content : active_local_contents) {
283 for (const StreamParams& params : content->media_description()->streams()) {
284 stream_params.push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 }
286 }
Steve Anton5c72e712018-12-10 14:25:30 -0800287 return stream_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288}
289
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000290// Filters the data codecs for the data channel type.
291void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
292 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700293 const char* codec_name =
294 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
Steve Anton3a66edf2018-09-10 12:57:37 -0700295 codecs->erase(std::remove_if(codecs->begin(), codecs->end(),
296 [&codec_name](const DataCodec& codec) {
Niels Möller039743e2018-10-23 10:07:25 +0200297 return absl::EqualsIgnoreCase(codec.name,
298 codec_name);
Steve Anton3a66edf2018-09-10 12:57:37 -0700299 }),
300 codecs->end());
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000301}
302
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303template <typename IdStruct>
304class UsedIds {
305 public:
306 UsedIds(int min_allowed_id, int max_allowed_id)
307 : min_allowed_id_(min_allowed_id),
308 max_allowed_id_(max_allowed_id),
Yves Gerey665174f2018-06-19 15:03:05 +0200309 next_id_(max_allowed_id) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310
311 // Loops through all Id in |ids| and changes its id if it is
312 // already in use by another IdStruct. Call this methods with all Id
313 // in a session description to make sure no duplicate ids exists.
314 // Note that typename Id must be a type of IdStruct.
315 template <typename Id>
316 void FindAndSetIdUsed(std::vector<Id>* ids) {
Steve Anton3a66edf2018-09-10 12:57:37 -0700317 for (const Id& id : *ids) {
318 FindAndSetIdUsed(&id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319 }
320 }
321
322 // Finds and sets an unused id if the |idstruct| id is already in use.
323 void FindAndSetIdUsed(IdStruct* idstruct) {
324 const int original_id = idstruct->id;
325 int new_id = idstruct->id;
326
327 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
328 // If the original id is not in range - this is an id that can't be
329 // dynamically changed.
330 return;
331 }
332
333 if (IsIdUsed(original_id)) {
334 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100335 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
336 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337 idstruct->id = new_id;
338 }
339 SetIdUsed(new_id);
340 }
341
342 private:
343 // Returns the first unused id in reverse order.
344 // This hopefully reduce the risk of more collisions. We want to change the
345 // default ids as little as possible.
346 int FindUnusedId() {
347 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
348 --next_id_;
349 }
nisseede5da42017-01-12 05:15:36 -0800350 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000351 return next_id_;
352 }
353
Yves Gerey665174f2018-06-19 15:03:05 +0200354 bool IsIdUsed(int new_id) { return id_set_.find(new_id) != id_set_.end(); }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000355
Yves Gerey665174f2018-06-19 15:03:05 +0200356 void SetIdUsed(int new_id) { id_set_.insert(new_id); }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000357
358 const int min_allowed_id_;
359 const int max_allowed_id_;
360 int next_id_;
361 std::set<int> id_set_;
362};
363
364// Helper class used for finding duplicate RTP payload types among audio, video
365// and data codecs. When bundle is used the payload types may not collide.
366class UsedPayloadTypes : public UsedIds<Codec> {
367 public:
368 UsedPayloadTypes()
Yves Gerey665174f2018-06-19 15:03:05 +0200369 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000370
371 private:
372 static const int kDynamicPayloadTypeMin = 96;
373 static const int kDynamicPayloadTypeMax = 127;
374};
375
376// Helper class used for finding duplicate RTP Header extension ids among
Johannes Kron07ba2b92018-09-26 13:33:35 +0200377// audio and video extensions. Only applies to one-byte header extensions at the
378// moment. ids > 14 will always be reported as available.
379// TODO(kron): This class needs to be refactored when we start to send two-byte
380// header extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700381class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000382 public:
383 UsedRtpHeaderExtensionIds()
Johannes Kron07ba2b92018-09-26 13:33:35 +0200384 : UsedIds<webrtc::RtpExtension>(
385 webrtc::RtpExtension::kMinId,
386 webrtc::RtpExtension::kOneByteHeaderExtensionMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387
388 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000389};
390
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800391static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
392 const SenderOptions& sender,
393 const std::string& rtcp_cname,
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800394 bool include_rtx_streams,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800395 bool include_flexfec_stream,
396 UniqueRandomIdGenerator* ssrc_generator) {
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800397 StreamParams result;
398 result.id = sender.track_id;
399
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800400 // TODO(brandtr): Update when we support multistream protection.
401 if (include_flexfec_stream && sender.num_sim_layers > 1) {
402 include_flexfec_stream = false;
403 RTC_LOG(LS_WARNING)
404 << "Our FlexFEC implementation only supports protecting "
405 "a single media streams. This session has multiple "
406 "media streams however, so no FlexFEC SSRC will be generated.";
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800407 }
408
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800409 result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
410 include_flexfec_stream, ssrc_generator);
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800411
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800412 result.cname = rtcp_cname;
413 result.set_stream_ids(sender.stream_ids);
414
415 return result;
416}
417
418static bool ValidateSimulcastLayers(
419 const std::vector<RidDescription>& rids,
420 const SimulcastLayerList& simulcast_layers) {
Steve Anton64b626b2019-01-28 17:25:26 -0800421 return absl::c_all_of(
422 simulcast_layers.GetAllLayers(), [&rids](const SimulcastLayer& layer) {
423 return absl::c_any_of(rids, [&layer](const RidDescription& rid) {
424 return rid.rid == layer.rid;
425 });
426 });
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800427}
428
429static StreamParams CreateStreamParamsForNewSenderWithRids(
430 const SenderOptions& sender,
431 const std::string& rtcp_cname) {
432 RTC_DCHECK(!sender.rids.empty());
433 RTC_DCHECK_EQ(sender.num_sim_layers, 0)
434 << "RIDs are the compliant way to indicate simulcast.";
435 RTC_DCHECK(ValidateSimulcastLayers(sender.rids, sender.simulcast_layers));
436 StreamParams result;
437 result.id = sender.track_id;
438 result.cname = rtcp_cname;
439 result.set_stream_ids(sender.stream_ids);
440
441 // More than one rid should be signaled.
442 if (sender.rids.size() > 1) {
443 result.set_rids(sender.rids);
444 }
445
446 return result;
447}
448
449// Adds SimulcastDescription if indicated by the media description options.
450// MediaContentDescription should already be set up with the send rids.
451static void AddSimulcastToMediaDescription(
452 const MediaDescriptionOptions& media_description_options,
453 MediaContentDescription* description) {
454 RTC_DCHECK(description);
455
456 // Check if we are using RIDs in this scenario.
Steve Anton64b626b2019-01-28 17:25:26 -0800457 if (absl::c_all_of(description->streams(), [](const StreamParams& params) {
458 return !params.has_rids();
459 })) {
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800460 return;
461 }
462
463 RTC_DCHECK_EQ(1, description->streams().size())
464 << "RIDs are only supported in Unified Plan semantics.";
465 RTC_DCHECK_EQ(1, media_description_options.sender_options.size());
466 RTC_DCHECK(description->type() == MediaType::MEDIA_TYPE_AUDIO ||
467 description->type() == MediaType::MEDIA_TYPE_VIDEO);
468
469 // One RID or less indicates that simulcast is not needed.
470 if (description->streams()[0].rids().size() <= 1) {
471 return;
472 }
473
Amit Hilbuchb7446ed2019-01-28 12:25:25 -0800474 // Only negotiate the send layers.
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800475 SimulcastDescription simulcast;
476 simulcast.send_layers() =
477 media_description_options.sender_options[0].simulcast_layers;
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800478 description->set_simulcast_description(simulcast);
479}
480
zhihuang1c378ed2017-08-17 14:10:50 -0700481// Adds a StreamParams for each SenderOptions in |sender_options| to
482// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483// |current_params| - All currently known StreamParams of any media type.
484template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700485static bool AddStreamParams(
486 const std::vector<SenderOptions>& sender_options,
487 const std::string& rtcp_cname,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800488 UniqueRandomIdGenerator* ssrc_generator,
zhihuang1c378ed2017-08-17 14:10:50 -0700489 StreamParamsVec* current_streams,
490 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700491 // SCTP streams are not negotiated using SDP/ContentDescriptions.
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000492 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700493 return true;
494 }
495
Noah Richards2e7a0982015-05-18 14:02:54 -0700496 const bool include_rtx_streams =
497 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498
brandtr03d5fb12016-11-22 03:37:59 -0800499 const bool include_flexfec_stream =
500 ContainsFlexfecCodec(content_description->codecs());
501
zhihuang1c378ed2017-08-17 14:10:50 -0700502 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 // groupid is empty for StreamParams generated using
504 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700505 StreamParams* param =
506 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000507 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700508 // This is a new sender.
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800509 StreamParams stream_param =
510 sender.rids.empty()
511 ?
512 // Signal SSRCs and legacy simulcast (if requested).
513 CreateStreamParamsForNewSenderWithSsrcs(
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800514 sender, rtcp_cname, include_rtx_streams,
515 include_flexfec_stream, ssrc_generator)
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800516 :
517 // Signal RIDs and spec-compliant simulcast (if requested).
518 CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
519
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000520 content_description->AddStream(stream_param);
521
522 // Store the new StreamParams in current_streams.
523 // This is necessary so that we can use the CNAME for other media types.
524 current_streams->push_back(stream_param);
525 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700526 // Use existing generated SSRCs/groups, but update the sync_label if
527 // necessary. This may be needed if a MediaStreamTrack was moved from one
528 // MediaStream to another.
Seth Hampson845e8782018-03-02 11:34:10 -0800529 param->set_stream_ids(sender.stream_ids);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000530 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 }
532 }
533 return true;
534}
535
536// Updates the transport infos of the |sdesc| according to the given
537// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800538// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
539// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000540static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
541 SessionDescription* sdesc) {
542 // The bundle should not be empty.
543 if (!sdesc || !bundle_group.FirstContentName()) {
544 return false;
545 }
546
547 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700548 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549 const TransportInfo* selected_transport_info =
550 sdesc->GetTransportInfoByName(selected_content_name);
551 if (!selected_transport_info) {
552 return false;
553 }
554
555 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700556 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000557 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700558 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000559 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800560 ConnectionRole selected_connection_role =
561 selected_transport_info->description.connection_role;
Steve Anton3a66edf2018-09-10 12:57:37 -0700562 for (TransportInfo& transport_info : sdesc->transport_infos()) {
563 if (bundle_group.HasContentName(transport_info.content_name) &&
564 transport_info.content_name != selected_content_name) {
565 transport_info.description.ice_ufrag = selected_ufrag;
566 transport_info.description.ice_pwd = selected_pwd;
567 transport_info.description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568 }
569 }
570 return true;
571}
572
573// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
574// sets it to |cryptos|.
575static bool GetCryptosByName(const SessionDescription* sdesc,
576 const std::string& content_name,
577 CryptoParamsVec* cryptos) {
578 if (!sdesc || !cryptos) {
579 return false;
580 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581 const ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800582 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583 return false;
584 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800585 *cryptos = content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586 return true;
587}
588
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
590// which are not available in |filter|.
591static void PruneCryptos(const CryptoParamsVec& filter,
592 CryptoParamsVec* target_cryptos) {
593 if (!target_cryptos) {
594 return;
595 }
tzik21995802018-04-26 17:38:28 +0900596
597 target_cryptos->erase(
598 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
599 // Returns true if the |crypto|'s cipher_suite is not
600 // found in |filter|.
601 [&filter](const CryptoParams& crypto) {
602 for (const CryptoParams& entry : filter) {
603 if (entry.cipher_suite == crypto.cipher_suite)
604 return false;
605 }
606 return true;
607 }),
608 target_cryptos->end());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000609}
610
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000611bool IsRtpProtocol(const std::string& protocol) {
612 return protocol.empty() ||
613 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
614}
615
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000616static bool IsRtpContent(SessionDescription* sdesc,
617 const std::string& content_name) {
618 bool is_rtp = false;
619 ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800620 if (content && content->media_description()) {
621 is_rtp = IsRtpProtocol(content->media_description()->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000622 }
623 return is_rtp;
624}
625
626// Updates the crypto parameters of the |sdesc| according to the given
627// |bundle_group|. The crypto parameters of all the contents within the
628// |bundle_group| should be updated to use the common subset of the
629// available cryptos.
630static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
631 SessionDescription* sdesc) {
632 // The bundle should not be empty.
633 if (!sdesc || !bundle_group.FirstContentName()) {
634 return false;
635 }
636
wu@webrtc.org78187522013-10-07 23:32:02 +0000637 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000638 // Get the common cryptos.
639 const ContentNames& content_names = bundle_group.content_names();
640 CryptoParamsVec common_cryptos;
Steve Anton3a66edf2018-09-10 12:57:37 -0700641 bool first = true;
642 for (const std::string& content_name : content_names) {
643 if (!IsRtpContent(sdesc, content_name)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000644 continue;
645 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000646 // The common cryptos are needed if any of the content does not have DTLS
647 // enabled.
Steve Anton3a66edf2018-09-10 12:57:37 -0700648 if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
wu@webrtc.org78187522013-10-07 23:32:02 +0000649 common_cryptos_needed = true;
650 }
Steve Anton3a66edf2018-09-10 12:57:37 -0700651 if (first) {
652 first = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000653 // Initial the common_cryptos with the first content in the bundle group.
Steve Anton3a66edf2018-09-10 12:57:37 -0700654 if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 return false;
656 }
657 if (common_cryptos.empty()) {
658 // If there's no crypto params, we should just return.
659 return true;
660 }
661 } else {
662 CryptoParamsVec cryptos;
Steve Anton3a66edf2018-09-10 12:57:37 -0700663 if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000664 return false;
665 }
666 PruneCryptos(cryptos, &common_cryptos);
667 }
668 }
669
wu@webrtc.org78187522013-10-07 23:32:02 +0000670 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 return false;
672 }
673
674 // Update to use the common cryptos.
Steve Anton3a66edf2018-09-10 12:57:37 -0700675 for (const std::string& content_name : content_names) {
676 if (!IsRtpContent(sdesc, content_name)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677 continue;
678 }
Steve Anton3a66edf2018-09-10 12:57:37 -0700679 ContentInfo* content = sdesc->GetContentByName(content_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000680 if (IsMediaContent(content)) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800681 MediaContentDescription* media_desc = content->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000682 if (!media_desc) {
683 return false;
684 }
685 media_desc->set_cryptos(common_cryptos);
686 }
687 }
688 return true;
689}
690
Steve Anton5c72e712018-12-10 14:25:30 -0800691static std::vector<const ContentInfo*> GetActiveContents(
692 const SessionDescription& description,
693 const MediaSessionOptions& session_options) {
694 std::vector<const ContentInfo*> active_contents;
695 for (size_t i = 0; i < description.contents().size(); ++i) {
696 RTC_DCHECK_LT(i, session_options.media_description_options.size());
697 const ContentInfo& content = description.contents()[i];
698 const MediaDescriptionOptions& media_options =
699 session_options.media_description_options[i];
700 if (!content.rejected && !media_options.stopped &&
701 content.name == media_options.mid) {
702 active_contents.push_back(&content);
703 }
704 }
705 return active_contents;
706}
707
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000708template <class C>
709static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800710 for (const auto& codec : codecs) {
711 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 return true;
713 }
714 }
715 return false;
716}
717
718template <class C>
719static bool IsRtxCodec(const C& codec) {
Niels Möller2edab4c2018-10-22 09:48:08 +0200720 return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721}
722
brandtr03d5fb12016-11-22 03:37:59 -0800723template <class C>
724static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
725 for (const auto& codec : codecs) {
726 if (IsFlexfecCodec(codec)) {
727 return true;
728 }
729 }
730 return false;
731}
732
733template <class C>
734static bool IsFlexfecCodec(const C& codec) {
Niels Möller2edab4c2018-10-22 09:48:08 +0200735 return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName);
brandtr03d5fb12016-11-22 03:37:59 -0800736}
737
zhihuang1c378ed2017-08-17 14:10:50 -0700738// Create a media content to be offered for the given |sender_options|,
739// according to the given options.rtcp_mux, session_options.is_muc, codecs,
740// secure_transport, crypto, and current_streams. If we don't currently have
741// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
742// created (according to crypto_suites). The created content is added to the
743// offer.
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000744template <class C>
745static bool CreateMediaContentOffer(
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800746 const MediaDescriptionOptions& media_description_options,
zhihuang1c378ed2017-08-17 14:10:50 -0700747 const MediaSessionOptions& session_options,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000748 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000749 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750 const CryptoParamsVec* current_cryptos,
751 const std::vector<std::string>& crypto_suites,
752 const RtpHeaderExtensions& rtp_extensions,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -0800753 UniqueRandomIdGenerator* ssrc_generator,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754 StreamParamsVec* current_streams,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000755 MediaContentDescriptionImpl<C>* offer) {
756 offer->AddCodecs(codecs);
757
zhihuang1c378ed2017-08-17 14:10:50 -0700758 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700759 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
760 offer->set_rtcp_reduced_size(true);
761 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000762 offer->set_rtp_header_extensions(rtp_extensions);
763
Harald Alvestrand141c0ad2019-05-05 19:00:00 +0000764 if (!AddStreamParams(media_description_options.sender_options,
765 session_options.rtcp_cname, ssrc_generator,
766 current_streams, offer)) {
767 return false;
768 }
769
Amit Hilbuchc63ddb22019-01-02 10:13:58 -0800770 AddSimulcastToMediaDescription(media_description_options, offer);
771
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000772 if (secure_policy != SEC_DISABLED) {
773 if (current_cryptos) {
774 AddMediaCryptos(*current_cryptos, offer);
775 }
776 if (offer->cryptos().empty()) {
777 if (!CreateMediaCryptos(crypto_suites, offer)) {
778 return false;
779 }
780 }
781 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000782
deadbeef7af91dd2016-12-13 11:29:11 -0800783 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000784 return false;
785 }
786 return true;
787}
788
789template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000790static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800791 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000792 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800793 const int codec2_id) {
794 const C* codec1 = FindCodecById(codecs1, codec1_id);
795 const C* codec2 = FindCodecById(codecs2, codec2_id);
796 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000797}
798
799template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000800static void NegotiateCodecs(const std::vector<C>& local_codecs,
801 const std::vector<C>& offered_codecs,
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200802 std::vector<C>* negotiated_codecs,
803 bool keep_offer_order) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800804 for (const C& ours : local_codecs) {
805 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700806 // Note that we intentionally only find one matching codec for each of our
807 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800808 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
809 C negotiated = ours;
810 negotiated.IntersectFeedbackParams(theirs);
811 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800812 const auto apt_it =
813 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800814 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800815 RTC_DCHECK(apt_it != theirs.params.end());
816 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000817 }
Niels Möller039743e2018-10-23 10:07:25 +0200818 if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) {
magjedf823ede2016-11-12 09:53:04 -0800819 webrtc::H264::GenerateProfileLevelIdForAnswer(
820 ours.params, theirs.params, &negotiated.params);
821 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800822 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700823 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800824 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 }
826 }
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200827 if (keep_offer_order) {
828 // RFC3264: Although the answerer MAY list the formats in their desired
829 // order of preference, it is RECOMMENDED that unless there is a
830 // specific reason, the answerer list formats in the same relative order
831 // they were present in the offer.
832 // This can be skipped when the transceiver has any codec preferences.
833 std::unordered_map<int, int> payload_type_preferences;
834 int preference = static_cast<int>(offered_codecs.size() + 1);
835 for (const C& codec : offered_codecs) {
836 payload_type_preferences[codec.id] = preference--;
837 }
838 absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
839 const C& b) {
840 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
841 });
deadbeef67cf2c12016-04-13 10:07:16 -0700842 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000843}
844
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800845// Finds a codec in |codecs2| that matches |codec_to_match|, which is
846// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
847// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000848template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800849static bool FindMatchingCodec(const std::vector<C>& codecs1,
850 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000851 const C& codec_to_match,
852 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700853 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
854 // codecs' associated codecs correctly. If not, that's a programming error.
Steve Anton64b626b2019-01-28 17:25:26 -0800855 RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
856 return &codec == &codec_to_match;
857 }));
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800858 for (const C& potential_match : codecs2) {
859 if (potential_match.Matches(codec_to_match)) {
860 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800861 int apt_value_1 = 0;
862 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800863 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
864 &apt_value_1) ||
865 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
866 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100867 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800868 continue;
869 }
870 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
871 apt_value_2)) {
872 continue;
873 }
874 }
875 if (found_codec) {
876 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000877 }
878 return true;
879 }
880 }
881 return false;
882}
883
zhihuang1c378ed2017-08-17 14:10:50 -0700884// Find the codec in |codec_list| that |rtx_codec| is associated with.
885template <class C>
886static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
887 const C& rtx_codec) {
888 std::string associated_pt_str;
889 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
890 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100891 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
892 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 14:10:50 -0700893 return nullptr;
894 }
895
896 int associated_pt;
897 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100898 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
899 << " of RTX codec " << rtx_codec.name
900 << " to an integer.";
zhihuang1c378ed2017-08-17 14:10:50 -0700901 return nullptr;
902 }
903
904 // Find the associated reference codec for the reference RTX codec.
905 const C* associated_codec = FindCodecById(codec_list, associated_pt);
906 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100907 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
908 << associated_pt << " for RTX codec " << rtx_codec.name
909 << ".";
zhihuang1c378ed2017-08-17 14:10:50 -0700910 }
911 return associated_codec;
912}
913
914// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915// already exist in |offered_codecs| and ensure the payload types don't
916// collide.
917template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700918static void MergeCodecs(const std::vector<C>& reference_codecs,
919 std::vector<C>* offered_codecs,
920 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000921 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800922 for (const C& reference_codec : reference_codecs) {
923 if (!IsRtxCodec(reference_codec) &&
924 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
925 reference_codec, nullptr)) {
926 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000927 used_pltypes->FindAndSetIdUsed(&codec);
928 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000929 }
930 }
931
932 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800933 for (const C& reference_codec : reference_codecs) {
934 if (IsRtxCodec(reference_codec) &&
935 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
936 reference_codec, nullptr)) {
937 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700938 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700939 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700940 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700941 continue;
942 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800943 // Find a codec in the offered list that matches the reference codec.
944 // Its payload type may be different than the reference codec.
945 C matching_codec;
946 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800947 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100948 RTC_LOG(LS_WARNING)
949 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800950 continue;
951 }
952
953 rtx_codec.params[kCodecParamAssociatedPayloadType] =
954 rtc::ToString(matching_codec.id);
955 used_pltypes->FindAndSetIdUsed(&rtx_codec);
956 offered_codecs->push_back(rtx_codec);
957 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000958 }
959}
960
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200961template <typename Codecs>
962static Codecs MatchCodecPreference(
963 const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
964 const Codecs& codecs) {
965 Codecs filtered_codecs;
966 std::set<std::string> kept_codecs_ids;
967 bool want_rtx = false;
968
969 for (const auto& codec_preference : codec_preferences) {
970 auto found_codec = absl::c_find_if(
971 codecs, [&codec_preference](const typename Codecs::value_type& codec) {
972 webrtc::RtpCodecParameters codec_parameters =
973 codec.ToCodecParameters();
974 return codec_parameters.name == codec_preference.name &&
975 codec_parameters.kind == codec_preference.kind &&
976 codec_parameters.num_channels ==
977 codec_preference.num_channels &&
978 codec_parameters.clock_rate == codec_preference.clock_rate &&
979 codec_parameters.parameters == codec_preference.parameters;
980 });
981
982 if (found_codec != codecs.end()) {
983 filtered_codecs.push_back(*found_codec);
984 kept_codecs_ids.insert(std::to_string(found_codec->id));
985 } else if (IsRtxCodec(codec_preference)) {
986 want_rtx = true;
987 }
988 }
989
990 if (want_rtx) {
991 for (const auto& codec : codecs) {
992 if (IsRtxCodec(codec)) {
993 const auto apt =
994 codec.params.find(cricket::kCodecParamAssociatedPayloadType);
995 if (apt != codec.params.end() &&
996 kept_codecs_ids.count(apt->second) > 0) {
997 filtered_codecs.push_back(codec);
998 }
999 }
1000 }
1001 }
1002
1003 return filtered_codecs;
1004}
1005
zhihuang1c378ed2017-08-17 14:10:50 -07001006static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
1007 const webrtc::RtpExtension& ext_to_match,
1008 webrtc::RtpExtension* found_extension) {
Steve Anton64b626b2019-01-28 17:25:26 -08001009 auto it = absl::c_find_if(
1010 extensions, [&ext_to_match](const webrtc::RtpExtension& extension) {
1011 // We assume that all URIs are given in a canonical
1012 // format.
1013 return extension.uri == ext_to_match.uri &&
1014 extension.encrypt == ext_to_match.encrypt;
1015 });
Steve Anton3a66edf2018-09-10 12:57:37 -07001016 if (it == extensions.end()) {
1017 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001018 }
Steve Anton3a66edf2018-09-10 12:57:37 -07001019 if (found_extension) {
1020 *found_extension = *it;
1021 }
1022 return true;
zhihuang1c378ed2017-08-17 14:10:50 -07001023}
1024
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001025static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -07001026 const webrtc::RtpExtension& ext_to_match,
1027 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -07001028 // We assume that all URIs are given in a canonical format.
1029 const webrtc::RtpExtension* found =
1030 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
1031 ext_to_match.uri);
1032 if (!found) {
1033 return false;
1034 }
1035 if (found_extension) {
1036 *found_extension = *found;
1037 }
1038 return true;
1039}
1040
1041static bool FindByUriWithEncryptionPreference(
1042 const RtpHeaderExtensions& extensions,
Yves Gerey665174f2018-06-19 15:03:05 +02001043 const webrtc::RtpExtension& ext_to_match,
1044 bool encryption_preference,
jbauch5869f502017-06-29 12:31:36 -07001045 webrtc::RtpExtension* found_extension) {
1046 const webrtc::RtpExtension* unencrypted_extension = nullptr;
Steve Anton3a66edf2018-09-10 12:57:37 -07001047 for (const webrtc::RtpExtension& extension : extensions) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048 // We assume that all URIs are given in a canonical format.
Steve Anton3a66edf2018-09-10 12:57:37 -07001049 if (extension.uri == ext_to_match.uri) {
1050 if (!encryption_preference || extension.encrypt) {
jbauch5869f502017-06-29 12:31:36 -07001051 if (found_extension) {
Steve Anton3a66edf2018-09-10 12:57:37 -07001052 *found_extension = extension;
jbauch5869f502017-06-29 12:31:36 -07001053 }
1054 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001055 }
Steve Anton3a66edf2018-09-10 12:57:37 -07001056 unencrypted_extension = &extension;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001057 }
1058 }
jbauch5869f502017-06-29 12:31:36 -07001059 if (unencrypted_extension) {
1060 if (found_extension) {
1061 *found_extension = *unencrypted_extension;
1062 }
1063 return true;
1064 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001065 return false;
1066}
1067
zhihuang1c378ed2017-08-17 14:10:50 -07001068// Adds all extensions from |reference_extensions| to |offered_extensions| that
1069// don't already exist in |offered_extensions| and ensure the IDs don't
1070// collide. If an extension is added, it's also added to |regular_extensions| or
1071// |encrypted_extensions|, and if the extension is in |regular_extensions| or
1072// |encrypted_extensions|, its ID is marked as used in |used_ids|.
1073// |offered_extensions| is for either audio or video while |regular_extensions|
1074// and |encrypted_extensions| are used for both audio and video. There could be
1075// overlap between audio extensions and video extensions.
1076static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1077 RtpHeaderExtensions* offered_extensions,
1078 RtpHeaderExtensions* regular_extensions,
1079 RtpHeaderExtensions* encrypted_extensions,
1080 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -07001081 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -07001082 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
1083 nullptr)) {
olka3c747662017-08-17 06:50:32 -07001084 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -07001085 if (reference_extension.encrypt) {
1086 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
1087 &existing)) {
1088 offered_extensions->push_back(existing);
1089 } else {
1090 used_ids->FindAndSetIdUsed(&reference_extension);
1091 encrypted_extensions->push_back(reference_extension);
1092 offered_extensions->push_back(reference_extension);
1093 }
olka3c747662017-08-17 06:50:32 -07001094 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07001095 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
1096 &existing)) {
1097 offered_extensions->push_back(existing);
1098 } else {
1099 used_ids->FindAndSetIdUsed(&reference_extension);
1100 regular_extensions->push_back(reference_extension);
1101 offered_extensions->push_back(reference_extension);
1102 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001103 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001104 }
1105 }
1106}
1107
jbauch5869f502017-06-29 12:31:36 -07001108static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1109 RtpHeaderExtensions* all_extensions,
1110 UsedRtpHeaderExtensionIds* used_ids) {
1111 RtpHeaderExtensions encrypted_extensions;
1112 for (const webrtc::RtpExtension& extension : *extensions) {
1113 webrtc::RtpExtension existing;
1114 // Don't add encrypted extensions again that were already included in a
1115 // previous offer or regular extensions that are also included as encrypted
1116 // extensions.
1117 if (extension.encrypt ||
1118 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1119 (FindByUriWithEncryptionPreference(*extensions, extension, true,
Yves Gerey665174f2018-06-19 15:03:05 +02001120 &existing) &&
1121 existing.encrypt)) {
jbauch5869f502017-06-29 12:31:36 -07001122 continue;
1123 }
1124
1125 if (FindByUri(*all_extensions, extension, &existing)) {
1126 encrypted_extensions.push_back(existing);
1127 } else {
1128 webrtc::RtpExtension encrypted(extension);
1129 encrypted.encrypt = true;
1130 used_ids->FindAndSetIdUsed(&encrypted);
1131 all_extensions->push_back(encrypted);
1132 encrypted_extensions.push_back(encrypted);
1133 }
1134 }
1135 extensions->insert(extensions->end(), encrypted_extensions.begin(),
Yves Gerey665174f2018-06-19 15:03:05 +02001136 encrypted_extensions.end());
jbauch5869f502017-06-29 12:31:36 -07001137}
1138
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001139static void NegotiateRtpHeaderExtensions(
1140 const RtpHeaderExtensions& local_extensions,
1141 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001142 bool enable_encrypted_rtp_header_extensions,
Johannes Kronce8e8672019-02-22 13:06:44 +01001143 RtpHeaderExtensions* negotiated_extensions) {
1144 // TransportSequenceNumberV2 is not offered by default. The special logic for
1145 // the TransportSequenceNumber extensions works as follows:
1146 // Offer Answer
1147 // V1 V1 if in local_extensions.
1148 // V1 and V2 V2 regardless of local_extensions.
1149 // V2 V2 regardless of local_extensions.
1150 const webrtc::RtpExtension* transport_sequence_number_v2_offer =
1151 webrtc::RtpExtension::FindHeaderExtensionByUri(
1152 offered_extensions,
1153 webrtc::RtpExtension::kTransportSequenceNumberV2Uri);
1154
Steve Anton3a66edf2018-09-10 12:57:37 -07001155 for (const webrtc::RtpExtension& ours : local_extensions) {
isheriff6f8d6862016-05-26 11:24:55 -07001156 webrtc::RtpExtension theirs;
Yves Gerey665174f2018-06-19 15:03:05 +02001157 if (FindByUriWithEncryptionPreference(
Steve Anton3a66edf2018-09-10 12:57:37 -07001158 offered_extensions, ours, enable_encrypted_rtp_header_extensions,
Yves Gerey665174f2018-06-19 15:03:05 +02001159 &theirs)) {
Johannes Kronce8e8672019-02-22 13:06:44 +01001160 if (transport_sequence_number_v2_offer &&
1161 ours.uri == webrtc::RtpExtension::kTransportSequenceNumberUri) {
1162 // Don't respond to
1163 // http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
1164 // if we get an offer including
Johannes Kron8cc711a2019-03-07 22:36:35 +01001165 // http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02
Johannes Kronce8e8672019-02-22 13:06:44 +01001166 continue;
1167 } else {
1168 // We respond with their RTP header extension id.
1169 negotiated_extensions->push_back(theirs);
1170 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001171 }
1172 }
Johannes Kronce8e8672019-02-22 13:06:44 +01001173
1174 if (transport_sequence_number_v2_offer) {
1175 // Respond that we support kTransportSequenceNumberV2Uri.
1176 negotiated_extensions->push_back(*transport_sequence_number_v2_offer);
1177 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001178}
1179
1180static void StripCNCodecs(AudioCodecs* audio_codecs) {
Steve Anton3a66edf2018-09-10 12:57:37 -07001181 audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
1182 [](const AudioCodec& codec) {
Niels Möller2edab4c2018-10-22 09:48:08 +02001183 return absl::EqualsIgnoreCase(
1184 codec.name, kComfortNoiseCodecName);
Steve Anton3a66edf2018-09-10 12:57:37 -07001185 }),
1186 audio_codecs->end());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001187}
1188
zhihuang1c378ed2017-08-17 14:10:50 -07001189// Create a media content to be answered for the given |sender_options|
1190// according to the given session_options.rtcp_mux, session_options.streams,
1191// codecs, crypto, and current_streams. If we don't currently have crypto (in
1192// current_cryptos) and it is enabled (in secure_policy), crypto is created
1193// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1194// negotiated with the offer. If the negotiation fails, this method returns
1195// false. The created content is added to the offer.
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001196template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001197static bool CreateMediaContentAnswer(
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001198 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001199 const MediaDescriptionOptions& media_description_options,
1200 const MediaSessionOptions& session_options,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001201 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001202 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001203 const CryptoParamsVec* current_cryptos,
1204 const RtpHeaderExtensions& local_rtp_extenstions,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08001205 UniqueRandomIdGenerator* ssrc_generator,
jbauch5869f502017-06-29 12:31:36 -07001206 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001208 bool bundle_enabled,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001209 MediaContentDescriptionImpl<C>* answer) {
1210 std::vector<C> negotiated_codecs;
1211 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
1212 media_description_options.codec_preferences.empty());
1213 answer->AddCodecs(negotiated_codecs);
1214 answer->set_protocol(offer->protocol());
1215
Johannes Kron9581bc42018-10-23 10:17:39 +02001216 answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 RtpHeaderExtensions negotiated_rtp_extensions;
Yves Gerey665174f2018-06-19 15:03:05 +02001218 NegotiateRtpHeaderExtensions(
1219 local_rtp_extenstions, offer->rtp_header_extensions(),
1220 enable_encrypted_rtp_header_extensions, &negotiated_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001221 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1222
zhihuang1c378ed2017-08-17 14:10:50 -07001223 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001224 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1225 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1226 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001227
1228 if (sdes_policy != SEC_DISABLED) {
1229 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001230 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1231 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001232 if (current_cryptos) {
1233 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1234 }
1235 answer->AddCrypto(crypto);
1236 }
1237 }
1238
deadbeef7af91dd2016-12-13 11:29:11 -08001239 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240 return false;
1241 }
1242
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001243 if (!AddStreamParams(media_description_options.sender_options,
1244 session_options.rtcp_cname, ssrc_generator,
1245 current_streams, answer)) {
1246 return false; // Something went seriously wrong.
1247 }
1248
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001249 AddSimulcastToMediaDescription(media_description_options, answer);
1250
Steve Anton4e70a722017-11-28 14:57:10 -08001251 answer->set_direction(NegotiateRtpTransceiverDirection(
1252 offer->direction(), media_description_options.direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001253 return true;
1254}
1255
1256static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001257 const std::string& protocol,
1258 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001259 // Since not all applications serialize and deserialize the media protocol,
1260 // we will have to accept |protocol| to be empty.
1261 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001262 return true;
1263 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001264
zhihuangcf5b37c2016-05-05 11:44:35 -07001265 if (type == MEDIA_TYPE_DATA) {
1266 // Check for SCTP, but also for RTP for RTP-based data channels.
1267 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1268 if (secure_transport) {
1269 // Most likely scenarios first.
1270 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1271 IsPlainRtp(protocol);
1272 } else {
1273 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1274 }
1275 }
1276
1277 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1278 // JSEP specifies.
1279 if (secure_transport) {
1280 // Most likely scenarios first.
1281 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1282 } else {
1283 return IsPlainRtp(protocol);
1284 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001285}
1286
1287static void SetMediaProtocol(bool secure_transport,
1288 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001289 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001290 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001291 else if (secure_transport)
1292 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293 else
1294 desc->set_protocol(kMediaProtocolAvpf);
1295}
1296
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001297// Gets the TransportInfo of the given |content_name| from the
1298// |current_description|. If doesn't exist, returns a new one.
1299static const TransportDescription* GetTransportDescription(
1300 const std::string& content_name,
1301 const SessionDescription* current_description) {
1302 const TransportDescription* desc = NULL;
1303 if (current_description) {
1304 const TransportInfo* info =
1305 current_description->GetTransportInfoByName(content_name);
1306 if (info) {
1307 desc = &info->description;
1308 }
1309 }
1310 return desc;
1311}
1312
1313// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001314static bool IsDtlsActive(const ContentInfo* content,
1315 const SessionDescription* current_description) {
1316 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001317 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001318 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001319
zhihuang1c378ed2017-08-17 14:10:50 -07001320 size_t msection_index = content - &current_description->contents()[0];
1321
1322 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001323 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001324 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001325
zhihuang1c378ed2017-08-17 14:10:50 -07001326 return current_description->transport_infos()[msection_index]
1327 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001328}
1329
Steve Anton8ffb9c32017-08-31 15:45:38 -07001330void MediaDescriptionOptions::AddAudioSender(
1331 const std::string& track_id,
1332 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001333 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001334 AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001335}
1336
Steve Anton8ffb9c32017-08-31 15:45:38 -07001337void MediaDescriptionOptions::AddVideoSender(
1338 const std::string& track_id,
1339 const std::vector<std::string>& stream_ids,
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001340 const std::vector<RidDescription>& rids,
1341 const SimulcastLayerList& simulcast_layers,
Steve Anton8ffb9c32017-08-31 15:45:38 -07001342 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001343 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001344 RTC_DCHECK(rids.empty() || num_sim_layers == 0)
1345 << "RIDs are the compliant way to indicate simulcast.";
1346 RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers));
1347 AddSenderInternal(track_id, stream_ids, rids, simulcast_layers,
1348 num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001349}
1350
zhihuang1c378ed2017-08-17 14:10:50 -07001351void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1352 const std::string& stream_id) {
1353 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001354 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1355 // than one stream?
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001356 AddSenderInternal(track_id, {stream_id}, {}, SimulcastLayerList(), 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001357}
1358
Steve Anton8ffb9c32017-08-31 15:45:38 -07001359void MediaDescriptionOptions::AddSenderInternal(
1360 const std::string& track_id,
1361 const std::vector<std::string>& stream_ids,
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001362 const std::vector<RidDescription>& rids,
1363 const SimulcastLayerList& simulcast_layers,
Steve Anton8ffb9c32017-08-31 15:45:38 -07001364 int num_sim_layers) {
1365 // TODO(steveanton): Support any number of stream ids.
1366 RTC_CHECK(stream_ids.size() == 1U);
Amit Hilbuchc63ddb22019-01-02 10:13:58 -08001367 SenderOptions options;
1368 options.track_id = track_id;
1369 options.stream_ids = stream_ids;
1370 options.simulcast_layers = simulcast_layers;
1371 options.rids = rids;
1372 options.num_sim_layers = num_sim_layers;
1373 sender_options.push_back(options);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001374}
1375
zhihuang1c378ed2017-08-17 14:10:50 -07001376bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
Steve Anton64b626b2019-01-28 17:25:26 -08001377 return absl::c_any_of(
1378 media_description_options,
1379 [type](const MediaDescriptionOptions& t) { return t.type == type; });
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001380}
1381
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001382MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08001383 const TransportDescriptionFactory* transport_desc_factory,
1384 rtc::UniqueRandomIdGenerator* ssrc_generator)
1385 : ssrc_generator_(ssrc_generator),
1386 transport_desc_factory_(transport_desc_factory) {
1387 RTC_DCHECK(ssrc_generator_);
1388}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001389
1390MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1391 ChannelManager* channel_manager,
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08001392 const TransportDescriptionFactory* transport_desc_factory,
1393 rtc::UniqueRandomIdGenerator* ssrc_generator)
1394 : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
ossudedfd282016-06-14 07:12:39 -07001395 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1396 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001397 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001398 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001399 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001400 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001401 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001402}
1403
ossudedfd282016-06-14 07:12:39 -07001404const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1405 const {
ossu075af922016-06-14 03:29:38 -07001406 return audio_sendrecv_codecs_;
1407}
1408
1409const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1410 return audio_send_codecs_;
1411}
1412
1413const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1414 return audio_recv_codecs_;
1415}
1416
1417void MediaSessionDescriptionFactory::set_audio_codecs(
Yves Gerey665174f2018-06-19 15:03:05 +02001418 const AudioCodecs& send_codecs,
1419 const AudioCodecs& recv_codecs) {
ossu075af922016-06-14 03:29:38 -07001420 audio_send_codecs_ = send_codecs;
1421 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001422 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001423}
1424
Amit Hilbuch77938e62018-12-21 09:23:38 -08001425static void AddUnifiedPlanExtensions(RtpHeaderExtensions* extensions) {
1426 RTC_DCHECK(extensions);
Elad Alon157540a2019-02-08 23:37:52 +01001427
1428 rtc::UniqueNumberGenerator<int> unique_id_generator;
1429 unique_id_generator.AddKnownId(0); // The first valid RTP extension ID is 1.
1430 for (const webrtc::RtpExtension& extension : *extensions) {
1431 const bool collision_free = unique_id_generator.AddKnownId(extension.id);
1432 RTC_DCHECK(collision_free);
1433 }
1434
Amit Hilbuch77938e62018-12-21 09:23:38 -08001435 // Unified Plan also offers the MID and RID header extensions.
Elad Alon157540a2019-02-08 23:37:52 +01001436 extensions->push_back(webrtc::RtpExtension(webrtc::RtpExtension::kMidUri,
1437 unique_id_generator()));
1438 extensions->push_back(webrtc::RtpExtension(webrtc::RtpExtension::kRidUri,
1439 unique_id_generator()));
Amit Hilbuch77938e62018-12-21 09:23:38 -08001440 extensions->push_back(webrtc::RtpExtension(
Elad Alon157540a2019-02-08 23:37:52 +01001441 webrtc::RtpExtension::kRepairedRidUri, unique_id_generator()));
Amit Hilbuch77938e62018-12-21 09:23:38 -08001442}
1443
1444RtpHeaderExtensions
1445MediaSessionDescriptionFactory::audio_rtp_header_extensions() const {
1446 RtpHeaderExtensions extensions = audio_rtp_extensions_;
1447 if (is_unified_plan_) {
1448 AddUnifiedPlanExtensions(&extensions);
1449 }
1450
1451 return extensions;
1452}
1453
1454RtpHeaderExtensions
1455MediaSessionDescriptionFactory::video_rtp_header_extensions() const {
1456 RtpHeaderExtensions extensions = video_rtp_extensions_;
1457 if (is_unified_plan_) {
1458 AddUnifiedPlanExtensions(&extensions);
1459 }
1460
1461 return extensions;
1462}
1463
Steve Anton6fe1fba2018-12-11 10:15:23 -08001464std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001465 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001466 const SessionDescription* current_description) const {
Steve Anton5c72e712018-12-10 14:25:30 -08001467 // Must have options for each existing section.
1468 if (current_description) {
1469 RTC_DCHECK_LE(current_description->contents().size(),
1470 session_options.media_description_options.size());
1471 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001472
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001473 IceCredentialsIterator ice_credentials(
1474 session_options.pooled_ice_credentials);
Steve Anton5c72e712018-12-10 14:25:30 -08001475
1476 std::vector<const ContentInfo*> current_active_contents;
1477 if (current_description) {
1478 current_active_contents =
1479 GetActiveContents(*current_description, session_options);
1480 }
1481
1482 StreamParamsVec current_streams =
1483 GetCurrentStreamParams(current_active_contents);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001484
zhihuang1c378ed2017-08-17 14:10:50 -07001485 AudioCodecs offer_audio_codecs;
1486 VideoCodecs offer_video_codecs;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001487 DataCodecs offer_data_codecs;
Steve Anton5c72e712018-12-10 14:25:30 -08001488 GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001489 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001490
zhihuang1c378ed2017-08-17 14:10:50 -07001491 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001492 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001493 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001494 }
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001495 FilterDataCodecs(&offer_data_codecs,
zhihuang1c378ed2017-08-17 14:10:50 -07001496 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001497
1498 RtpHeaderExtensions audio_rtp_extensions;
1499 RtpHeaderExtensions video_rtp_extensions;
Steve Anton8f66ddb2018-12-10 16:08:05 -08001500 GetRtpHdrExtsToOffer(current_active_contents, &audio_rtp_extensions,
1501 &video_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001502
Steve Anton5c72e712018-12-10 14:25:30 -08001503 auto offer = absl::make_unique<SessionDescription>();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001504
zhihuang1c378ed2017-08-17 14:10:50 -07001505 // Iterate through the media description options, matching with existing media
1506 // descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001507 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001508 for (const MediaDescriptionOptions& media_description_options :
1509 session_options.media_description_options) {
1510 const ContentInfo* current_content = nullptr;
1511 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001512 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001513 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-22 16:02:54 -08001514 // Media type must match unless this media section is being recycled.
Steve Anton5c72e712018-12-10 14:25:30 -08001515 RTC_DCHECK(current_content->name != media_description_options.mid ||
Steve Antondcc3c022017-12-22 16:02:54 -08001516 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 14:10:50 -07001517 media_description_options.type));
1518 }
1519 switch (media_description_options.type) {
1520 case MEDIA_TYPE_AUDIO:
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001521 if (!AddAudioContentForOffer(
1522 media_description_options, session_options, current_content,
1523 current_description, audio_rtp_extensions, offer_audio_codecs,
1524 &current_streams, offer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001525 return nullptr;
1526 }
1527 break;
1528 case MEDIA_TYPE_VIDEO:
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001529 if (!AddVideoContentForOffer(
1530 media_description_options, session_options, current_content,
1531 current_description, video_rtp_extensions, offer_video_codecs,
1532 &current_streams, offer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001533 return nullptr;
1534 }
1535 break;
1536 case MEDIA_TYPE_DATA:
1537 if (!AddDataContentForOffer(media_description_options, session_options,
1538 current_content, current_description,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001539 offer_data_codecs, &current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001540 offer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001541 return nullptr;
1542 }
1543 break;
1544 default:
1545 RTC_NOTREACHED();
1546 }
1547 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001548 }
1549
1550 // Bundle the contents together, if we've been asked to do so, and update any
1551 // parameters that need to be tweaked for BUNDLE.
Steve Anton2bed3972019-01-04 17:04:30 -08001552 if (session_options.bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001553 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001554 for (const ContentInfo& content : offer->contents()) {
Steve Anton2bed3972019-01-04 17:04:30 -08001555 if (content.rejected) {
1556 continue;
1557 }
zhihuang1c378ed2017-08-17 14:10:50 -07001558 // TODO(deadbeef): There are conditions that make bundling two media
1559 // descriptions together illegal. For example, they use the same payload
1560 // type to represent different codecs, or same IDs for different header
1561 // extensions. We need to detect this and not try to bundle those media
1562 // descriptions together.
1563 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001564 }
Steve Anton2bed3972019-01-04 17:04:30 -08001565 if (!offer_bundle.content_names().empty()) {
1566 offer->AddGroup(offer_bundle);
1567 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1568 RTC_LOG(LS_ERROR)
1569 << "CreateOffer failed to UpdateTransportInfoForBundle.";
1570 return nullptr;
1571 }
1572 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1573 RTC_LOG(LS_ERROR)
1574 << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1575 return nullptr;
1576 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001577 }
1578 }
Steve Antone831b8c2018-02-01 12:22:16 -08001579
1580 // The following determines how to signal MSIDs to ensure compatibility with
1581 // older endpoints (in particular, older Plan B endpoints).
Steve Anton8f66ddb2018-12-10 16:08:05 -08001582 if (is_unified_plan_) {
Steve Antone831b8c2018-02-01 12:22:16 -08001583 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1584 // Plan answerers will look at a=msid and Plan B answerers will look at the
1585 // a=ssrc MSID line.
1586 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1587 cricket::kMsidSignalingSsrcAttribute);
1588 } else {
1589 // Plan B always signals MSID using a=ssrc lines.
1590 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1591 }
1592
Johannes Kron89f874e2018-11-12 10:25:48 +01001593 offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
1594
Piotr (Peter) Slatalab1ae10b2019-03-01 11:14:05 -08001595 if (session_options.media_transport_settings.has_value()) {
1596 offer->AddMediaTransportSetting(
1597 session_options.media_transport_settings->transport_name,
1598 session_options.media_transport_settings->transport_setting);
1599 }
1600
Steve Anton6fe1fba2018-12-11 10:15:23 -08001601 return offer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001602}
1603
Steve Anton6fe1fba2018-12-11 10:15:23 -08001604std::unique_ptr<SessionDescription>
1605MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001606 const SessionDescription* offer,
1607 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001608 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001609 if (!offer) {
1610 return nullptr;
1611 }
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001612
Steve Anton5c72e712018-12-10 14:25:30 -08001613 // Must have options for exactly as many sections as in the offer.
1614 RTC_DCHECK_EQ(offer->contents().size(),
1615 session_options.media_description_options.size());
1616
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001617 IceCredentialsIterator ice_credentials(
1618 session_options.pooled_ice_credentials);
1619
Steve Anton5c72e712018-12-10 14:25:30 -08001620 std::vector<const ContentInfo*> current_active_contents;
1621 if (current_description) {
1622 current_active_contents =
1623 GetActiveContents(*current_description, session_options);
1624 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001625
Steve Anton5c72e712018-12-10 14:25:30 -08001626 StreamParamsVec current_streams =
1627 GetCurrentStreamParams(current_active_contents);
Johannes Kron0854eb62018-10-10 22:33:20 +02001628
zhihuang1c378ed2017-08-17 14:10:50 -07001629 // Get list of all possible codecs that respects existing payload type
1630 // mappings and uses a single payload type space.
1631 //
1632 // Note that these lists may be further filtered for each m= section; this
1633 // step is done just to establish the payload type mappings shared by all
1634 // sections.
1635 AudioCodecs answer_audio_codecs;
1636 VideoCodecs answer_video_codecs;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001637 DataCodecs answer_data_codecs;
Steve Anton5c72e712018-12-10 14:25:30 -08001638 GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001639 &answer_video_codecs, &answer_data_codecs);
zhihuang1c378ed2017-08-17 14:10:50 -07001640
1641 if (!session_options.vad_enabled) {
1642 // If application doesn't want CN codecs in answer.
1643 StripCNCodecs(&answer_audio_codecs);
1644 }
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001645 FilterDataCodecs(&answer_data_codecs,
zhihuang1c378ed2017-08-17 14:10:50 -07001646 session_options.data_channel_type == DCT_SCTP);
1647
Steve Anton5c72e712018-12-10 14:25:30 -08001648 auto answer = absl::make_unique<SessionDescription>();
1649
1650 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1651 // group in the answer with the appropriate content names.
1652 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1653 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1654 // Transport info shared by the bundle group.
1655 std::unique_ptr<TransportInfo> bundle_transport;
1656
1657 answer->set_extmap_allow_mixed(offer->extmap_allow_mixed());
1658
zhihuang1c378ed2017-08-17 14:10:50 -07001659 // Iterate through the media description options, matching with existing
1660 // media descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001661 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001662 for (const MediaDescriptionOptions& media_description_options :
1663 session_options.media_description_options) {
1664 const ContentInfo* offer_content = &offer->contents()[msection_index];
1665 // Media types and MIDs must match between the remote offer and the
1666 // MediaDescriptionOptions.
1667 RTC_DCHECK(
1668 IsMediaContentOfType(offer_content, media_description_options.type));
1669 RTC_DCHECK(media_description_options.mid == offer_content->name);
1670 const ContentInfo* current_content = nullptr;
1671 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001672 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001673 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001674 }
zhihuang1c378ed2017-08-17 14:10:50 -07001675 switch (media_description_options.type) {
1676 case MEDIA_TYPE_AUDIO:
1677 if (!AddAudioContentForAnswer(
1678 media_description_options, session_options, offer_content,
1679 offer, current_content, current_description,
1680 bundle_transport.get(), answer_audio_codecs, &current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001681 answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001682 return nullptr;
1683 }
1684 break;
1685 case MEDIA_TYPE_VIDEO:
1686 if (!AddVideoContentForAnswer(
1687 media_description_options, session_options, offer_content,
1688 offer, current_content, current_description,
1689 bundle_transport.get(), answer_video_codecs, &current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001690 answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001691 return nullptr;
1692 }
1693 break;
1694 case MEDIA_TYPE_DATA:
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001695 if (!AddDataContentForAnswer(
1696 media_description_options, session_options, offer_content,
1697 offer, current_content, current_description,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001698 bundle_transport.get(), answer_data_codecs, &current_streams,
1699 answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001700 return nullptr;
1701 }
1702 break;
1703 default:
1704 RTC_NOTREACHED();
1705 }
1706 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001707 // See if we can add the newly generated m= section to the BUNDLE group in
1708 // the answer.
1709 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001710 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001711 offer_bundle->HasContentName(added.name)) {
1712 answer_bundle.AddContentName(added.name);
1713 bundle_transport.reset(
1714 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001715 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001716 }
1717
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001718 // If a BUNDLE group was offered, put a BUNDLE group in the answer even if
1719 // it's empty. RFC5888 says:
1720 //
1721 // A SIP entity that receives an offer that contains an "a=group" line
1722 // with semantics that are understood MUST return an answer that
1723 // contains an "a=group" line with the same semantics.
1724 if (offer_bundle) {
deadbeefb7892532017-02-22 19:35:18 -08001725 answer->AddGroup(answer_bundle);
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001726 }
deadbeefb7892532017-02-22 19:35:18 -08001727
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001728 if (answer_bundle.FirstContentName()) {
deadbeefb7892532017-02-22 19:35:18 -08001729 // Share the same ICE credentials and crypto params across all contents,
1730 // as BUNDLE requires.
1731 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001732 RTC_LOG(LS_ERROR)
1733 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001734 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001735 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001736
deadbeefb7892532017-02-22 19:35:18 -08001737 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001738 RTC_LOG(LS_ERROR)
1739 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001740 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001741 }
1742 }
1743
Steve Antone831b8c2018-02-01 12:22:16 -08001744 // The following determines how to signal MSIDs to ensure compatibility with
1745 // older endpoints (in particular, older Plan B endpoints).
Steve Anton8f66ddb2018-12-10 16:08:05 -08001746 if (is_unified_plan_) {
Steve Antone831b8c2018-02-01 12:22:16 -08001747 // Unified Plan needs to look at what the offer included to find the most
1748 // compatible answer.
1749 if (offer->msid_signaling() == 0) {
1750 // We end up here in one of three cases:
1751 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1752 // matter what we pick here.
1753 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1754 // it also doesn't matter what we pick here.
1755 // 3. Media that's either sendonly or inactive from the remote endpoint.
1756 // We don't have any information to say whether the endpoint is Plan B
1757 // or Unified Plan, so be conservative and send both.
1758 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1759 cricket::kMsidSignalingSsrcAttribute);
1760 } else if (offer->msid_signaling() ==
1761 (cricket::kMsidSignalingMediaSection |
1762 cricket::kMsidSignalingSsrcAttribute)) {
1763 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1764 // probably talking to a Unified Plan endpoint so respond with just
1765 // a=msid.
1766 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1767 } else {
1768 // Otherwise, it's clear which method the offerer is using so repeat that
1769 // back to them.
1770 answer->set_msid_signaling(offer->msid_signaling());
1771 }
1772 } else {
1773 // Plan B always signals MSID using a=ssrc lines.
1774 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1775 }
1776
Steve Anton6fe1fba2018-12-11 10:15:23 -08001777 return answer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001778}
1779
ossu075af922016-06-14 03:29:38 -07001780const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1781 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001782 switch (direction) {
1783 // If stream is inactive - generate list as if sendrecv.
1784 case RtpTransceiverDirection::kSendRecv:
1785 case RtpTransceiverDirection::kInactive:
1786 return audio_sendrecv_codecs_;
1787 case RtpTransceiverDirection::kSendOnly:
1788 return audio_send_codecs_;
1789 case RtpTransceiverDirection::kRecvOnly:
1790 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001791 }
Steve Anton1d03a752017-11-27 14:30:09 -08001792 RTC_NOTREACHED();
1793 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001794}
1795
1796const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1797 const RtpTransceiverDirection& offer,
1798 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001799 switch (answer) {
1800 // For inactive and sendrecv answers, generate lists as if we were to accept
1801 // the offer's direction. See RFC 3264 Section 6.1.
1802 case RtpTransceiverDirection::kSendRecv:
1803 case RtpTransceiverDirection::kInactive:
1804 return GetAudioCodecsForOffer(
1805 webrtc::RtpTransceiverDirectionReversed(offer));
1806 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 03:29:38 -07001807 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 14:30:09 -08001808 case RtpTransceiverDirection::kRecvOnly:
1809 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001810 }
Steve Anton1d03a752017-11-27 14:30:09 -08001811 RTC_NOTREACHED();
1812 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001813}
1814
Steve Anton5c72e712018-12-10 14:25:30 -08001815void MergeCodecsFromDescription(
1816 const std::vector<const ContentInfo*>& current_active_contents,
1817 AudioCodecs* audio_codecs,
1818 VideoCodecs* video_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001819 DataCodecs* data_codecs,
Steve Anton5c72e712018-12-10 14:25:30 -08001820 UsedPayloadTypes* used_pltypes) {
1821 for (const ContentInfo* content : current_active_contents) {
1822 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001823 const AudioContentDescription* audio =
Steve Anton5c72e712018-12-10 14:25:30 -08001824 content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001825 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
Steve Anton5c72e712018-12-10 14:25:30 -08001826 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001827 const VideoContentDescription* video =
Steve Anton5c72e712018-12-10 14:25:30 -08001828 content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001829 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
Steve Anton5c72e712018-12-10 14:25:30 -08001830 } else if (IsMediaContentOfType(content, MEDIA_TYPE_DATA)) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001831 const DataContentDescription* data =
1832 content->media_description()->as_data();
1833 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
zhihuang1c378ed2017-08-17 14:10:50 -07001834 }
1835 }
1836}
1837
1838// Getting codecs for an offer involves these steps:
1839//
1840// 1. Construct payload type -> codec mappings for current description.
1841// 2. Add any reference codecs that weren't already present
1842// 3. For each individual media description (m= section), filter codecs based
1843// on the directional attribute (happens in another method).
1844void MediaSessionDescriptionFactory::GetCodecsForOffer(
Steve Anton5c72e712018-12-10 14:25:30 -08001845 const std::vector<const ContentInfo*>& current_active_contents,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001846 AudioCodecs* audio_codecs,
1847 VideoCodecs* video_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001848 DataCodecs* data_codecs) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001849 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001850 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1851 // new media type is added.
Steve Anton5c72e712018-12-10 14:25:30 -08001852 UsedPayloadTypes used_pltypes;
1853 MergeCodecsFromDescription(current_active_contents, audio_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001854 video_codecs, data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001855
Steve Anton5c72e712018-12-10 14:25:30 -08001856 // Add our codecs that are not in the current description.
zhihuang1c378ed2017-08-17 14:10:50 -07001857 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1858 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001859 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
zhihuang1c378ed2017-08-17 14:10:50 -07001860}
1861
1862// Getting codecs for an answer involves these steps:
1863//
1864// 1. Construct payload type -> codec mappings for current description.
1865// 2. Add any codecs from the offer that weren't already present.
1866// 3. Add any remaining codecs that weren't already present.
1867// 4. For each individual media description (m= section), filter codecs based
1868// on the directional attribute (happens in another method).
1869void MediaSessionDescriptionFactory::GetCodecsForAnswer(
Steve Anton5c72e712018-12-10 14:25:30 -08001870 const std::vector<const ContentInfo*>& current_active_contents,
1871 const SessionDescription& remote_offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001872 AudioCodecs* audio_codecs,
1873 VideoCodecs* video_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001874 DataCodecs* data_codecs) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001875 // First - get all codecs from the current description if the media type
1876 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1877 // new media type is added.
Steve Anton5c72e712018-12-10 14:25:30 -08001878 UsedPayloadTypes used_pltypes;
1879 MergeCodecsFromDescription(current_active_contents, audio_codecs,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001880 video_codecs, data_codecs, &used_pltypes);
zhihuang1c378ed2017-08-17 14:10:50 -07001881
1882 // Second - filter out codecs that we don't support at all and should ignore.
1883 AudioCodecs filtered_offered_audio_codecs;
1884 VideoCodecs filtered_offered_video_codecs;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001885 DataCodecs filtered_offered_data_codecs;
Steve Anton5c72e712018-12-10 14:25:30 -08001886 for (const ContentInfo& content : remote_offer.contents()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001887 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1888 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001889 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001890 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1891 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1892 filtered_offered_audio_codecs,
1893 offered_audio_codec, nullptr) &&
1894 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1895 offered_audio_codec, nullptr)) {
1896 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1897 }
1898 }
1899 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1900 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001901 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001902 for (const VideoCodec& offered_video_codec : video->codecs()) {
1903 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1904 filtered_offered_video_codecs,
1905 offered_video_codec, nullptr) &&
1906 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1907 offered_video_codec, nullptr)) {
1908 filtered_offered_video_codecs.push_back(offered_video_codec);
1909 }
1910 }
1911 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001912 const DataContentDescription* data =
1913 content.media_description()->as_data();
1914 for (const DataCodec& offered_data_codec : data->codecs()) {
1915 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1916 filtered_offered_data_codecs,
1917 offered_data_codec, nullptr) &&
1918 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1919 offered_data_codec, nullptr)) {
1920 filtered_offered_data_codecs.push_back(offered_data_codec);
zhihuang1c378ed2017-08-17 14:10:50 -07001921 }
1922 }
1923 }
1924 }
1925
Steve Anton5c72e712018-12-10 14:25:30 -08001926 // Add codecs that are not in the current description but were in
zhihuang1c378ed2017-08-17 14:10:50 -07001927 // |remote_offer|.
1928 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1929 &used_pltypes);
1930 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1931 &used_pltypes);
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00001932 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
zhihuang1c378ed2017-08-17 14:10:50 -07001933 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001934}
1935
1936void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
Steve Anton5c72e712018-12-10 14:25:30 -08001937 const std::vector<const ContentInfo*>& current_active_contents,
zhihuang1c378ed2017-08-17 14:10:50 -07001938 RtpHeaderExtensions* offer_audio_extensions,
1939 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001940 // All header extensions allocated from the same range to avoid potential
1941 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001942 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001943 RtpHeaderExtensions all_regular_extensions;
1944 RtpHeaderExtensions all_encrypted_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001945
1946 // First - get all extensions from the current description if the media type
1947 // is used.
1948 // Add them to |used_ids| so the local ids are not reused if a new media
1949 // type is added.
Steve Anton5c72e712018-12-10 14:25:30 -08001950 for (const ContentInfo* content : current_active_contents) {
1951 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
1952 const AudioContentDescription* audio =
1953 content->media_description()->as_audio();
1954 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1955 &all_regular_extensions, &all_encrypted_extensions,
1956 &used_ids);
1957 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
1958 const VideoContentDescription* video =
1959 content->media_description()->as_video();
1960 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1961 &all_regular_extensions, &all_encrypted_extensions,
1962 &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001963 }
1964 }
1965
Steve Anton5c72e712018-12-10 14:25:30 -08001966 // Add our default RTP header extensions that are not in the current
1967 // description.
Steve Anton8f66ddb2018-12-10 16:08:05 -08001968 MergeRtpHdrExts(audio_rtp_header_extensions(), offer_audio_extensions,
1969 &all_regular_extensions, &all_encrypted_extensions,
1970 &used_ids);
1971 MergeRtpHdrExts(video_rtp_header_extensions(), offer_video_extensions,
1972 &all_regular_extensions, &all_encrypted_extensions,
1973 &used_ids);
zhihuang1c378ed2017-08-17 14:10:50 -07001974
jbauch5869f502017-06-29 12:31:36 -07001975 // TODO(jbauch): Support adding encrypted header extensions to existing
1976 // sessions.
Steve Anton5c72e712018-12-10 14:25:30 -08001977 if (enable_encrypted_rtp_header_extensions_ &&
1978 current_active_contents.empty()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001979 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1980 &all_encrypted_extensions, &used_ids);
1981 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1982 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001983 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001984}
1985
1986bool MediaSessionDescriptionFactory::AddTransportOffer(
Yves Gerey665174f2018-06-19 15:03:05 +02001987 const std::string& content_name,
1988 const TransportOptions& transport_options,
1989 const SessionDescription* current_desc,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001990 SessionDescription* offer_desc,
1991 IceCredentialsIterator* ice_credentials) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001992 if (!transport_desc_factory_)
Yves Gerey665174f2018-06-19 15:03:05 +02001993 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001994 const TransportDescription* current_tdesc =
1995 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001996 std::unique_ptr<TransportDescription> new_tdesc(
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02001997 transport_desc_factory_->CreateOffer(transport_options, current_tdesc,
1998 ice_credentials));
Steve Anton06817cd2018-12-18 15:55:30 -08001999 if (!new_tdesc) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002000 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
2001 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002002 }
Steve Anton06817cd2018-12-18 15:55:30 -08002003 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc));
2004 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002005}
2006
Steve Anton1a9d3c32018-12-10 17:18:54 -08002007std::unique_ptr<TransportDescription>
2008MediaSessionDescriptionFactory::CreateTransportAnswer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002009 const std::string& content_name,
2010 const SessionDescription* offer_desc,
2011 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002012 const SessionDescription* current_desc,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002013 bool require_transport_attributes,
2014 IceCredentialsIterator* ice_credentials) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002015 if (!transport_desc_factory_)
2016 return NULL;
2017 const TransportDescription* offer_tdesc =
2018 GetTransportDescription(content_name, offer_desc);
2019 const TransportDescription* current_tdesc =
2020 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08002021 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
2022 require_transport_attributes,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002023 current_tdesc, ice_credentials);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002024}
2025
2026bool MediaSessionDescriptionFactory::AddTransportAnswer(
2027 const std::string& content_name,
2028 const TransportDescription& transport_desc,
2029 SessionDescription* answer_desc) const {
Steve Anton06817cd2018-12-18 15:55:30 -08002030 answer_desc->AddTransportInfo(TransportInfo(content_name, transport_desc));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002031 return true;
2032}
2033
zhihuang1c378ed2017-08-17 14:10:50 -07002034// |audio_codecs| = set of all possible codecs that can be used, with correct
2035// payload type mappings
2036//
2037// |supported_audio_codecs| = set of codecs that are supported for the direction
2038// of this m= section
2039//
2040// acd->codecs() = set of previously negotiated codecs for this m= section
2041//
2042// The payload types should come from audio_codecs, but the order should come
2043// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2044// change existing codec priority, and that new codecs are added with the right
2045// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002046bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002047 const MediaDescriptionOptions& media_description_options,
2048 const MediaSessionOptions& session_options,
2049 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002050 const SessionDescription* current_description,
2051 const RtpHeaderExtensions& audio_rtp_extensions,
2052 const AudioCodecs& audio_codecs,
2053 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002054 SessionDescription* desc,
2055 IceCredentialsIterator* ice_credentials) const {
zhihuang1c378ed2017-08-17 14:10:50 -07002056 // Filter audio_codecs (which includes all codecs, with correctly remapped
2057 // payload types) based on transceiver direction.
2058 const AudioCodecs& supported_audio_codecs =
2059 GetAudioCodecsForOffer(media_description_options.direction);
2060
2061 AudioCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002062
2063 if (!media_description_options.codec_preferences.empty()) {
2064 // Add the codecs from the current transceiver's codec preferences.
2065 // They override any existing codecs from previous negotiations.
2066 filtered_codecs = MatchCodecPreference(
2067 media_description_options.codec_preferences, supported_audio_codecs);
2068 } else {
2069 // Add the codecs from current content if it exists and is not rejected nor
2070 // recycled.
2071 if (current_content && !current_content->rejected &&
2072 current_content->name == media_description_options.mid) {
2073 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2074 const AudioContentDescription* acd =
2075 current_content->media_description()->as_audio();
2076 for (const AudioCodec& codec : acd->codecs()) {
2077 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2078 nullptr)) {
2079 filtered_codecs.push_back(codec);
2080 }
zhihuang1c378ed2017-08-17 14:10:50 -07002081 }
2082 }
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002083 // Add other supported audio codecs.
2084 AudioCodec found_codec;
2085 for (const AudioCodec& codec : supported_audio_codecs) {
2086 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2087 codec, &found_codec) &&
2088 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2089 filtered_codecs, codec, nullptr)) {
2090 // Use the |found_codec| from |audio_codecs| because it has the
2091 // correctly mapped payload type.
2092 filtered_codecs.push_back(found_codec);
2093 }
zhihuang1c378ed2017-08-17 14:10:50 -07002094 }
2095 }
deadbeef44f08192015-12-15 16:20:09 -08002096
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002097 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07002098 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2099 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002100
kwiberg31022942016-03-11 14:18:21 -08002101 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002102 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07002103 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
2104 &crypto_suites);
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08002105 if (!CreateMediaContentOffer(media_description_options, session_options,
2106 filtered_codecs, sdes_policy,
2107 GetCryptos(current_content), crypto_suites,
2108 audio_rtp_extensions, ssrc_generator_,
2109 current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002110 return false;
2111 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002112
2113 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2114 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00002115
Steve Anton4e70a722017-11-28 14:57:10 -08002116 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00002117
Steve Anton5adfafd2017-12-20 16:34:00 -08002118 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07002119 media_description_options.stopped, audio.release());
2120 if (!AddTransportOffer(media_description_options.mid,
2121 media_description_options.transport_options,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002122 current_description, desc, ice_credentials)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002123 return false;
2124 }
2125
2126 return true;
2127}
2128
2129bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002130 const MediaDescriptionOptions& media_description_options,
2131 const MediaSessionOptions& session_options,
2132 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002133 const SessionDescription* current_description,
2134 const RtpHeaderExtensions& video_rtp_extensions,
2135 const VideoCodecs& video_codecs,
2136 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002137 SessionDescription* desc,
2138 IceCredentialsIterator* ice_credentials) const {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002139 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07002140 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2141 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002142
kwiberg31022942016-03-11 14:18:21 -08002143 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002144 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07002145 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
2146 &crypto_suites);
2147
2148 VideoCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002149
2150 if (!media_description_options.codec_preferences.empty()) {
2151 // Add the codecs from the current transceiver's codec preferences.
2152 // They override any existing codecs from previous negotiations.
2153 filtered_codecs = MatchCodecPreference(
2154 media_description_options.codec_preferences, video_codecs_);
2155 } else {
2156 // Add the codecs from current content if it exists and is not rejected nor
2157 // recycled.
2158 if (current_content && !current_content->rejected &&
2159 current_content->name == media_description_options.mid) {
2160 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2161 const VideoContentDescription* vcd =
2162 current_content->media_description()->as_video();
2163 for (const VideoCodec& codec : vcd->codecs()) {
2164 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2165 nullptr)) {
2166 filtered_codecs.push_back(codec);
2167 }
zhihuang1c378ed2017-08-17 14:10:50 -07002168 }
2169 }
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002170 // Add other supported video codecs.
2171 VideoCodec found_codec;
2172 for (const VideoCodec& codec : video_codecs_) {
2173 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
2174 &found_codec) &&
2175 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2176 nullptr)) {
2177 // Use the |found_codec| from |video_codecs| because it has the
2178 // correctly mapped payload type.
2179 filtered_codecs.push_back(found_codec);
2180 }
zhihuang1c378ed2017-08-17 14:10:50 -07002181 }
2182 }
2183
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08002184 if (!CreateMediaContentOffer(media_description_options, session_options,
2185 filtered_codecs, sdes_policy,
2186 GetCryptos(current_content), crypto_suites,
2187 video_rtp_extensions, ssrc_generator_,
2188 current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002189 return false;
2190 }
2191
zhihuang1c378ed2017-08-17 14:10:50 -07002192 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002193
2194 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2195 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00002196
Steve Anton4e70a722017-11-28 14:57:10 -08002197 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00002198
Steve Anton5adfafd2017-12-20 16:34:00 -08002199 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07002200 media_description_options.stopped, video.release());
2201 if (!AddTransportOffer(media_description_options.mid,
2202 media_description_options.transport_options,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002203 current_description, desc, ice_credentials)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002204 return false;
2205 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002206 return true;
2207}
2208
2209bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002210 const MediaDescriptionOptions& media_description_options,
2211 const MediaSessionOptions& session_options,
2212 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002213 const SessionDescription* current_description,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002214 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002215 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002216 SessionDescription* desc,
2217 IceCredentialsIterator* ice_credentials) const {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002218 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2219
2220 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07002221 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
2222 // If the DataChannel type is not specified, use the DataChannel type in
2223 // the current description.
2224 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002225 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
Steve Antonb1c1de12017-12-21 15:14:30 -08002226 is_sctp = (current_content->media_description()->protocol() ==
2227 kMediaProtocolSctp);
zhihuang1c378ed2017-08-17 14:10:50 -07002228 }
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002229
2230 cricket::SecurePolicy sdes_policy =
2231 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2232 : secure();
2233 std::vector<std::string> crypto_suites;
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002234 if (is_sctp) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002235 // SDES doesn't make sense for SCTP, so we disable it, and we only
2236 // get SDES crypto suites for RTP-based data channels.
2237 sdes_policy = cricket::SEC_DISABLED;
2238 // Unlike SetMediaProtocol below, we need to set the protocol
2239 // before we call CreateMediaContentOffer. Otherwise,
2240 // CreateMediaContentOffer won't know this is SCTP and will
2241 // generate SSRCs rather than SIDs.
2242 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2243 // it's safe to do so. Older versions of webrtc would reject these
2244 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
2245 data->set_protocol(secure_transport ? kMediaProtocolDtlsSctp
2246 : kMediaProtocolSctp);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247 } else {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002248 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
2249 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002250 }
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002251
2252 // Even SCTP uses a "codec".
2253 if (!CreateMediaContentOffer(
2254 media_description_options, session_options, data_codecs, sdes_policy,
2255 GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(),
2256 ssrc_generator_, current_streams, data.get())) {
2257 return false;
2258 }
2259
2260 if (is_sctp) {
2261 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
2262 data.release());
2263 } else {
2264 data->set_bandwidth(kDataMaxBandwidth);
2265 SetMediaProtocol(secure_transport, data.get());
2266 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2267 media_description_options.stopped, data.release());
2268 }
2269 if (!AddTransportOffer(media_description_options.mid,
2270 media_description_options.transport_options,
2271 current_description, desc, ice_credentials)) {
2272 return false;
2273 }
2274 return true;
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002275}
2276
zhihuang1c378ed2017-08-17 14:10:50 -07002277// |audio_codecs| = set of all possible codecs that can be used, with correct
2278// payload type mappings
2279//
2280// |supported_audio_codecs| = set of codecs that are supported for the direction
2281// of this m= section
2282//
2283// acd->codecs() = set of previously negotiated codecs for this m= section
2284//
2285// The payload types should come from audio_codecs, but the order should come
2286// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2287// change existing codec priority, and that new codecs are added with the right
2288// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002289bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002290 const MediaDescriptionOptions& media_description_options,
2291 const MediaSessionOptions& session_options,
2292 const ContentInfo* offer_content,
2293 const SessionDescription* offer_description,
2294 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002295 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002296 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002297 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002298 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002299 SessionDescription* answer,
2300 IceCredentialsIterator* ice_credentials) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002301 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002302 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002303 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002304
Steve Anton1a9d3c32018-12-10 17:18:54 -08002305 std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002306 media_description_options.mid, offer_description,
2307 media_description_options.transport_options, current_description,
Steve Anton1a9d3c32018-12-10 17:18:54 -08002308 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002309 if (!audio_transport) {
2310 return false;
2311 }
2312
zhihuang1c378ed2017-08-17 14:10:50 -07002313 // Pick codecs based on the requested communications direction in the offer
2314 // and the selected direction in the answer.
2315 // Note these will be filtered one final time in CreateMediaContentAnswer.
2316 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 14:57:10 -08002317 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 03:29:38 -07002318 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002319 AudioCodecs supported_audio_codecs =
2320 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2321
2322 AudioCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002323
2324 if (!media_description_options.codec_preferences.empty()) {
2325 filtered_codecs = MatchCodecPreference(
2326 media_description_options.codec_preferences, supported_audio_codecs);
2327 } else {
2328 // Add the codecs from current content if it exists and is not rejected nor
2329 // recycled.
2330 if (current_content && !current_content->rejected &&
2331 current_content->name == media_description_options.mid) {
2332 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2333 const AudioContentDescription* acd =
2334 current_content->media_description()->as_audio();
2335 for (const AudioCodec& codec : acd->codecs()) {
2336 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2337 nullptr)) {
2338 filtered_codecs.push_back(codec);
2339 }
zhihuang1c378ed2017-08-17 14:10:50 -07002340 }
2341 }
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002342 // Add other supported audio codecs.
2343 for (const AudioCodec& codec : supported_audio_codecs) {
2344 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2345 codec, nullptr) &&
2346 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2347 filtered_codecs, codec, nullptr)) {
2348 // We should use the local codec with local parameters and the codec id
2349 // would be correctly mapped in |NegotiateCodecs|.
2350 filtered_codecs.push_back(codec);
2351 }
zhihuang1c378ed2017-08-17 14:10:50 -07002352 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002353 }
2354
zhihuang1c378ed2017-08-17 14:10:50 -07002355 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2356 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002357 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002358 new AudioContentDescription());
2359 // Do not require or create SDES cryptos if DTLS is used.
2360 cricket::SecurePolicy sdes_policy =
2361 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2362 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002363 offer_audio_description, media_description_options, session_options,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002364 filtered_codecs, sdes_policy, GetCryptos(current_content),
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08002365 audio_rtp_header_extensions(), ssrc_generator_,
Steve Anton1b8773d2018-04-06 11:13:34 -07002366 enable_encrypted_rtp_header_extensions_, current_streams,
2367 bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002368 return false; // Fails the session setup.
2369 }
2370
deadbeefb7892532017-02-22 19:35:18 -08002371 bool secure = bundle_transport ? bundle_transport->description.secure()
2372 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002373 bool rejected = media_description_options.stopped ||
2374 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002375 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2376 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002377 if (!AddTransportAnswer(media_description_options.mid,
2378 *(audio_transport.get()), answer)) {
2379 return false;
2380 }
2381
2382 if (rejected) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002383 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2384 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002385 }
2386
zhihuang1c378ed2017-08-17 14:10:50 -07002387 answer->AddContent(media_description_options.mid, offer_content->type,
2388 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002389 return true;
2390}
2391
2392bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002393 const MediaDescriptionOptions& media_description_options,
2394 const MediaSessionOptions& session_options,
2395 const ContentInfo* offer_content,
2396 const SessionDescription* offer_description,
2397 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002398 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002399 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002400 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002401 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002402 SessionDescription* answer,
2403 IceCredentialsIterator* ice_credentials) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002404 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002405 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002406 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002407
Steve Anton1a9d3c32018-12-10 17:18:54 -08002408 std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002409 media_description_options.mid, offer_description,
2410 media_description_options.transport_options, current_description,
Steve Anton1a9d3c32018-12-10 17:18:54 -08002411 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002412 if (!video_transport) {
2413 return false;
2414 }
2415
zhihuang1c378ed2017-08-17 14:10:50 -07002416 VideoCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002417
2418 if (!media_description_options.codec_preferences.empty()) {
2419 filtered_codecs = MatchCodecPreference(
2420 media_description_options.codec_preferences, video_codecs_);
2421 } else {
2422 // Add the codecs from current content if it exists and is not rejected nor
2423 // recycled.
2424 if (current_content && !current_content->rejected &&
2425 current_content->name == media_description_options.mid) {
2426 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2427 const VideoContentDescription* vcd =
2428 current_content->media_description()->as_video();
2429 for (const VideoCodec& codec : vcd->codecs()) {
2430 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2431 nullptr)) {
2432 filtered_codecs.push_back(codec);
2433 }
zhihuang1c378ed2017-08-17 14:10:50 -07002434 }
2435 }
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002436 // Add other supported video codecs.
2437 for (const VideoCodec& codec : video_codecs_) {
2438 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
2439 nullptr) &&
2440 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2441 nullptr)) {
2442 // We should use the local codec with local parameters and the codec id
2443 // would be correctly mapped in |NegotiateCodecs|.
2444 filtered_codecs.push_back(codec);
2445 }
zhihuang1c378ed2017-08-17 14:10:50 -07002446 }
2447 }
2448
2449 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2450 session_options.bundle_enabled;
2451
kwiberg31022942016-03-11 14:18:21 -08002452 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002453 new VideoContentDescription());
2454 // Do not require or create SDES cryptos if DTLS is used.
2455 cricket::SecurePolicy sdes_policy =
2456 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002457 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002458 offer_video_description, media_description_options, session_options,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002459 filtered_codecs, sdes_policy, GetCryptos(current_content),
Amit Hilbuchbcd39d42019-01-25 17:13:56 -08002460 video_rtp_header_extensions(), ssrc_generator_,
Steve Anton1b8773d2018-04-06 11:13:34 -07002461 enable_encrypted_rtp_header_extensions_, current_streams,
2462 bundle_enabled, video_answer.get())) {
zhihuang1c378ed2017-08-17 14:10:50 -07002463 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002464 }
deadbeefb7892532017-02-22 19:35:18 -08002465 bool secure = bundle_transport ? bundle_transport->description.secure()
2466 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002467 bool rejected = media_description_options.stopped ||
2468 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002469 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2470 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002471 if (!AddTransportAnswer(media_description_options.mid,
2472 *(video_transport.get()), answer)) {
2473 return false;
2474 }
2475
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002476 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002477 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002478 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002479 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2480 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002481 }
zhihuang1c378ed2017-08-17 14:10:50 -07002482 answer->AddContent(media_description_options.mid, offer_content->type,
2483 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002484 return true;
2485}
2486
2487bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002488 const MediaDescriptionOptions& media_description_options,
2489 const MediaSessionOptions& session_options,
2490 const ContentInfo* offer_content,
2491 const SessionDescription* offer_description,
2492 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002493 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002494 const TransportInfo* bundle_transport,
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002495 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002496 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002497 SessionDescription* answer,
2498 IceCredentialsIterator* ice_credentials) const {
Steve Anton1a9d3c32018-12-10 17:18:54 -08002499 std::unique_ptr<TransportDescription> data_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 07:47:12 +02002500 media_description_options.mid, offer_description,
2501 media_description_options.transport_options, current_description,
Steve Anton1a9d3c32018-12-10 17:18:54 -08002502 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002503 if (!data_transport) {
2504 return false;
2505 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002506
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002507 std::unique_ptr<DataContentDescription> data_answer(
2508 new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002509 // Do not require or create SDES cryptos if DTLS is used.
2510 cricket::SecurePolicy sdes_policy =
2511 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002512 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2513 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002514 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002515 const DataContentDescription* offer_data_description =
2516 offer_content->media_description()->as_data();
2517 if (!CreateMediaContentAnswer(
2518 offer_data_description, media_description_options, session_options,
2519 data_codecs, sdes_policy, GetCryptos(current_content),
2520 RtpHeaderExtensions(), ssrc_generator_,
2521 enable_encrypted_rtp_header_extensions_, current_streams,
2522 bundle_enabled, data_answer.get())) {
2523 return false; // Fails the session setup.
Harald Alvestrand14b27582019-05-04 11:37:04 +02002524 }
Danil Chapovalovc6d1d242019-04-23 09:48:11 +00002525
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002526 // Respond with sctpmap if the offer uses sctpmap.
2527 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2528 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2529
deadbeefb7892532017-02-22 19:35:18 -08002530 bool secure = bundle_transport ? bundle_transport->description.secure()
2531 : data_transport->secure();
2532
zhihuang1c378ed2017-08-17 14:10:50 -07002533 bool rejected = session_options.data_channel_type == DCT_NONE ||
2534 media_description_options.stopped ||
2535 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002536 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2537 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002538 if (!AddTransportAnswer(media_description_options.mid,
2539 *(data_transport.get()), answer)) {
2540 return false;
2541 }
2542
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002543 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002544 data_answer->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002545 } else {
2546 // RFC 3264
2547 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002548 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002549 }
zhihuang1c378ed2017-08-17 14:10:50 -07002550 answer->AddContent(media_description_options.mid, offer_content->type,
2551 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002552 return true;
2553}
2554
zhihuang1c378ed2017-08-17 14:10:50 -07002555void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2556 audio_sendrecv_codecs_.clear();
2557 all_audio_codecs_.clear();
2558 // Compute the audio codecs union.
2559 for (const AudioCodec& send : audio_send_codecs_) {
2560 all_audio_codecs_.push_back(send);
2561 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2562 send, nullptr)) {
2563 // It doesn't make sense to have an RTX codec we support sending but not
2564 // receiving.
2565 RTC_DCHECK(!IsRtxCodec(send));
2566 }
2567 }
2568 for (const AudioCodec& recv : audio_recv_codecs_) {
2569 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2570 recv, nullptr)) {
2571 all_audio_codecs_.push_back(recv);
2572 }
2573 }
2574 // Use NegotiateCodecs to merge our codec lists, since the operation is
2575 // essentially the same. Put send_codecs as the offered_codecs, which is the
2576 // order we'd like to follow. The reasoning is that encoding is usually more
2577 // expensive than decoding, and prioritizing a codec in the send list probably
2578 // means it's a codec we can handle efficiently.
2579 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002580 &audio_sendrecv_codecs_, true);
zhihuang1c378ed2017-08-17 14:10:50 -07002581}
2582
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002583bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002584 return (content && (content->type == MediaProtocolType::kRtp ||
2585 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002586}
2587
2588bool IsAudioContent(const ContentInfo* content) {
2589 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2590}
2591
2592bool IsVideoContent(const ContentInfo* content) {
2593 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2594}
2595
2596bool IsDataContent(const ContentInfo* content) {
2597 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2598}
2599
deadbeef0ed85b22016-02-23 17:24:52 -08002600const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2601 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002602 for (const ContentInfo& content : contents) {
2603 if (IsMediaContentOfType(&content, media_type)) {
2604 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002605 }
2606 }
deadbeef0ed85b22016-02-23 17:24:52 -08002607 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002608}
2609
2610const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2611 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2612}
2613
2614const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2615 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2616}
2617
2618const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2619 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2620}
2621
Steve Antonad7bffc2018-01-22 10:21:56 -08002622const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2623 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002624 if (sdesc == nullptr) {
2625 return nullptr;
2626 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002627
2628 return GetFirstMediaContent(sdesc->contents(), media_type);
2629}
2630
2631const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2632 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2633}
2634
2635const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2636 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2637}
2638
2639const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2640 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2641}
2642
2643const MediaContentDescription* GetFirstMediaContentDescription(
Yves Gerey665174f2018-06-19 15:03:05 +02002644 const SessionDescription* sdesc,
2645 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002646 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002647 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002648}
2649
2650const AudioContentDescription* GetFirstAudioContentDescription(
2651 const SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002652 return static_cast<const AudioContentDescription*>(
2653 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002654}
2655
2656const VideoContentDescription* GetFirstVideoContentDescription(
2657 const SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002658 return static_cast<const VideoContentDescription*>(
2659 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002660}
2661
2662const DataContentDescription* GetFirstDataContentDescription(
2663 const SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002664 return static_cast<const DataContentDescription*>(
2665 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002666}
2667
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002668//
2669// Non-const versions of the above functions.
2670//
2671
Steve Anton36b29d12017-10-30 09:57:42 -07002672ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002673 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002674 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002675 if (IsMediaContentOfType(&content, media_type)) {
2676 return &content;
2677 }
2678 }
2679 return nullptr;
2680}
2681
Steve Anton36b29d12017-10-30 09:57:42 -07002682ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002683 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2684}
2685
Steve Anton36b29d12017-10-30 09:57:42 -07002686ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002687 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2688}
2689
Steve Anton36b29d12017-10-30 09:57:42 -07002690ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002691 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2692}
2693
Steve Antonad7bffc2018-01-22 10:21:56 -08002694ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2695 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002696 if (sdesc == nullptr) {
2697 return nullptr;
2698 }
2699
Steve Anton36b29d12017-10-30 09:57:42 -07002700 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002701}
2702
2703ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2704 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2705}
2706
2707ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2708 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2709}
2710
2711ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2712 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2713}
2714
2715MediaContentDescription* GetFirstMediaContentDescription(
2716 SessionDescription* sdesc,
2717 MediaType media_type) {
2718 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002719 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002720}
2721
2722AudioContentDescription* GetFirstAudioContentDescription(
2723 SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002724 return static_cast<AudioContentDescription*>(
2725 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002726}
2727
2728VideoContentDescription* GetFirstVideoContentDescription(
2729 SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002730 return static_cast<VideoContentDescription*>(
2731 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002732}
2733
2734DataContentDescription* GetFirstDataContentDescription(
2735 SessionDescription* sdesc) {
Harald Alvestrand141c0ad2019-05-05 19:00:00 +00002736 return static_cast<DataContentDescription*>(
2737 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002738}
2739
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002740} // namespace cricket