blob: eba3249d03fae1e129c1e70a4591a510b58c6b1a [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
deadbeef67cf2c12016-04-13 10:07:16 -070013#include <algorithm> // For std::find_if, std::sort.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <functional>
15#include <map>
kwiberg31022942016-03-11 14:18:21 -080016#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <set>
deadbeef67cf2c12016-04-13 10:07:16 -070018#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <utility>
20
Patrik Höglund7aee3d52017-11-15 13:15:17 +010021#include "api/cryptoparams.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "api/optional.h"
Mirko Bonadei71207422017-09-15 13:58:09 +020023#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "media/base/h264_profile_level_id.h"
25#include "media/base/mediaconstants.h"
26#include "p2p/base/p2pconstants.h"
27#include "pc/channelmanager.h"
Steve Anton1d03a752017-11-27 14:30:09 -080028#include "pc/rtpmediautils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "pc/srtpfilter.h"
30#include "rtc_base/base64.h"
31#include "rtc_base/checks.h"
32#include "rtc_base/helpers.h"
33#include "rtc_base/logging.h"
34#include "rtc_base/stringutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035
36namespace {
Steve Anton1d03a752017-11-27 14:30:09 -080037
38using webrtc::RtpTransceiverDirection;
39
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080041
deadbeef7914b8c2017-04-21 03:23:33 -070042void GetSupportedSdesCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
43 std::vector<int>*),
44 const rtc::CryptoOptions& crypto_options,
45 std::vector<std::string>* names) {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080046 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070047 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080048 for (const auto crypto : crypto_suites) {
49 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
50 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080051}
terelius8c011e52016-04-26 05:28:11 -070052} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053
54namespace cricket {
55
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056// RTP Profile names
57// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
58// RFC4585
59const char kMediaProtocolAvpf[] = "RTP/AVPF";
60// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000061const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
62
deadbeeff3938292015-07-15 12:20:53 -070063// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
64// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065const char kMediaProtocolSavpf[] = "RTP/SAVPF";
66
67const char kMediaProtocolRtpPrefix[] = "RTP/";
68
69const char kMediaProtocolSctp[] = "SCTP";
70const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000071const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000072const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073
deadbeef8b7e9ad2017-05-25 09:38:55 -070074// Note that the below functions support some protocol strings purely for
75// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
76// and Interoperability.
77
78static bool IsDtlsRtp(const std::string& protocol) {
79 // Most-likely values first.
80 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
81 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
82}
83
84static bool IsPlainRtp(const std::string& protocol) {
85 // Most-likely values first.
86 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
87 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
88}
89
90static bool IsDtlsSctp(const std::string& protocol) {
91 return protocol == kMediaProtocolDtlsSctp ||
92 protocol == kMediaProtocolUdpDtlsSctp ||
93 protocol == kMediaProtocolTcpDtlsSctp;
94}
95
96static bool IsPlainSctp(const std::string& protocol) {
97 return protocol == kMediaProtocolSctp;
98}
99
100static bool IsSctp(const std::string& protocol) {
101 return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
102}
103
Steve Anton1d03a752017-11-27 14:30:09 -0800104static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
105 RtpTransceiverDirection offer,
106 RtpTransceiverDirection wants) {
107 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
108 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
109 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
110 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
111 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
112 offer_send && wants_recv);
ossu075af922016-06-14 03:29:38 -0700113}
114
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115static bool IsMediaContentOfType(const ContentInfo* content,
116 MediaType media_type) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800117 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118 return false;
119 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800120 return content->media_description()->type() == media_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121}
122
123static bool CreateCryptoParams(int tag, const std::string& cipher,
124 CryptoParams *out) {
jbauchcb560652016-08-04 05:20:32 -0700125 int key_len;
126 int salt_len;
127 if (!rtc::GetSrtpKeyAndSaltLengths(
128 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129 return false;
130 }
jbauchcb560652016-08-04 05:20:32 -0700131
132 int master_key_len = key_len + salt_len;
133 std::string master_key;
134 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
135 return false;
136 }
137
kwiberg352444f2016-11-28 15:58:53 -0800138 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700139 std::string key = rtc::Base64::Encode(master_key);
140
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141 out->tag = tag;
142 out->cipher_suite = cipher;
143 out->key_params = kInline;
144 out->key_params += key;
145 return true;
146}
147
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148static bool AddCryptoParams(const std::string& cipher_suite,
149 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000150 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151
152 out->resize(size + 1);
153 return CreateCryptoParams(size, cipher_suite, &out->at(size));
154}
155
156void AddMediaCryptos(const CryptoParamsVec& cryptos,
157 MediaContentDescription* media) {
158 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
159 crypto != cryptos.end(); ++crypto) {
160 media->AddCrypto(*crypto);
161 }
162}
163
164bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
165 MediaContentDescription* media) {
166 CryptoParamsVec cryptos;
167 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
168 it != crypto_suites.end(); ++it) {
169 if (!AddCryptoParams(*it, &cryptos)) {
170 return false;
171 }
172 }
173 AddMediaCryptos(cryptos, media);
174 return true;
175}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176
zhihuang1c378ed2017-08-17 14:10:50 -0700177const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800178 if (!content || !content->media_description()) {
zhihuang1c378ed2017-08-17 14:10:50 -0700179 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000180 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800181 return &content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182}
183
184bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
185 const CryptoParams& crypto,
186 CryptoParams* out) {
187 for (CryptoParamsVec::const_iterator it = cryptos.begin();
188 it != cryptos.end(); ++it) {
189 if (crypto.Matches(*it)) {
190 *out = *it;
191 return true;
192 }
193 }
194 return false;
195}
196
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700197// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
198// low overhead.
deadbeef7914b8c2017-04-21 03:23:33 -0700199void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
200 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700201 if (crypto_options.enable_gcm_crypto_suites) {
202 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
203 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
204 }
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700205 if (crypto_options.enable_aes128_sha1_32_crypto_cipher) {
206 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
207 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800208 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000209}
210
deadbeef7914b8c2017-04-21 03:23:33 -0700211void GetSupportedAudioSdesCryptoSuiteNames(
212 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800213 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700214 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
215 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000216}
217
deadbeef7914b8c2017-04-21 03:23:33 -0700218void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
219 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700220 if (crypto_options.enable_gcm_crypto_suites) {
221 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
222 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
223 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800224 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225}
226
deadbeef7914b8c2017-04-21 03:23:33 -0700227void GetSupportedVideoSdesCryptoSuiteNames(
228 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800229 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700230 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
231 crypto_options, crypto_suite_names);
232}
233
234void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
235 std::vector<int>* crypto_suites) {
236 if (crypto_options.enable_gcm_crypto_suites) {
237 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
238 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
239 }
240 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
241}
242
243void GetSupportedDataSdesCryptoSuiteNames(
244 const rtc::CryptoOptions& crypto_options,
245 std::vector<std::string>* crypto_suite_names) {
246 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
247 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800248}
249
jbauchcb560652016-08-04 05:20:32 -0700250// Support any GCM cipher (if enabled through options). For video support only
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700251// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
252// bundle is enabled because it is low overhead.
jbauchcb560652016-08-04 05:20:32 -0700253// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254static bool SelectCrypto(const MediaContentDescription* offer,
255 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700256 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 CryptoParams *crypto) {
258 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
259 const CryptoParamsVec& cryptos = offer->cryptos();
260
261 for (CryptoParamsVec::const_iterator i = cryptos.begin();
262 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700263 if ((crypto_options.enable_gcm_crypto_suites &&
264 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
265 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700266 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
Taylor Brandstetter5e55fe82018-03-23 11:50:16 -0700267 !bundle && crypto_options.enable_aes128_sha1_32_crypto_cipher)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
269 }
270 }
271 return false;
272}
273
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000275// The generated values are added to |ssrcs|.
276// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000278 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200279 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000280 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200281 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000283 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000284 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
286 ssrcs->push_back(candidate);
287 }
288}
289
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290// Finds all StreamParams of all media types and attach them to stream_params.
291static void GetCurrentStreamParams(const SessionDescription* sdesc,
292 StreamParamsVec* stream_params) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800293 RTC_DCHECK(stream_params);
294 if (!sdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000295 return;
Steve Antonb1c1de12017-12-21 15:14:30 -0800296 }
297 for (const ContentInfo& content : sdesc->contents()) {
298 if (!content.media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000299 continue;
300 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800301 for (const StreamParams& params : content.media_description()->streams()) {
302 stream_params->push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 }
304 }
305}
306
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000307// Filters the data codecs for the data channel type.
308void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
309 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700310 const char* codec_name =
311 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000312 for (std::vector<DataCodec>::iterator iter = codecs->begin();
313 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700314 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000315 iter = codecs->erase(iter);
316 } else {
317 ++iter;
318 }
319 }
320}
321
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322template <typename IdStruct>
323class UsedIds {
324 public:
325 UsedIds(int min_allowed_id, int max_allowed_id)
326 : min_allowed_id_(min_allowed_id),
327 max_allowed_id_(max_allowed_id),
328 next_id_(max_allowed_id) {
329 }
330
331 // Loops through all Id in |ids| and changes its id if it is
332 // already in use by another IdStruct. Call this methods with all Id
333 // in a session description to make sure no duplicate ids exists.
334 // Note that typename Id must be a type of IdStruct.
335 template <typename Id>
336 void FindAndSetIdUsed(std::vector<Id>* ids) {
337 for (typename std::vector<Id>::iterator it = ids->begin();
338 it != ids->end(); ++it) {
339 FindAndSetIdUsed(&*it);
340 }
341 }
342
343 // Finds and sets an unused id if the |idstruct| id is already in use.
344 void FindAndSetIdUsed(IdStruct* idstruct) {
345 const int original_id = idstruct->id;
346 int new_id = idstruct->id;
347
348 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
349 // If the original id is not in range - this is an id that can't be
350 // dynamically changed.
351 return;
352 }
353
354 if (IsIdUsed(original_id)) {
355 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100356 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
357 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000358 idstruct->id = new_id;
359 }
360 SetIdUsed(new_id);
361 }
362
363 private:
364 // Returns the first unused id in reverse order.
365 // This hopefully reduce the risk of more collisions. We want to change the
366 // default ids as little as possible.
367 int FindUnusedId() {
368 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
369 --next_id_;
370 }
nisseede5da42017-01-12 05:15:36 -0800371 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000372 return next_id_;
373 }
374
375 bool IsIdUsed(int new_id) {
376 return id_set_.find(new_id) != id_set_.end();
377 }
378
379 void SetIdUsed(int new_id) {
380 id_set_.insert(new_id);
381 }
382
383 const int min_allowed_id_;
384 const int max_allowed_id_;
385 int next_id_;
386 std::set<int> id_set_;
387};
388
389// Helper class used for finding duplicate RTP payload types among audio, video
390// and data codecs. When bundle is used the payload types may not collide.
391class UsedPayloadTypes : public UsedIds<Codec> {
392 public:
393 UsedPayloadTypes()
394 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
395 }
396
397
398 private:
399 static const int kDynamicPayloadTypeMin = 96;
400 static const int kDynamicPayloadTypeMax = 127;
401};
402
403// Helper class used for finding duplicate RTP Header extension ids among
404// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700405class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000406 public:
407 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-25 18:15:09 -0800408 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
409 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410
411 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000412};
413
zhihuang1c378ed2017-08-17 14:10:50 -0700414// Adds a StreamParams for each SenderOptions in |sender_options| to
415// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000416// |current_params| - All currently known StreamParams of any media type.
417template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700418static bool AddStreamParams(
419 const std::vector<SenderOptions>& sender_options,
420 const std::string& rtcp_cname,
421 StreamParamsVec* current_streams,
422 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700423 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 09:38:55 -0700424 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700425 return true;
426 }
427
Noah Richards2e7a0982015-05-18 14:02:54 -0700428 const bool include_rtx_streams =
429 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000430
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431
brandtr03d5fb12016-11-22 03:37:59 -0800432 const bool include_flexfec_stream =
433 ContainsFlexfecCodec(content_description->codecs());
434
zhihuang1c378ed2017-08-17 14:10:50 -0700435 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436 // groupid is empty for StreamParams generated using
437 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700438 StreamParams* param =
439 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000440 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700441 // This is a new sender.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200442 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 14:10:50 -0700443 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000444 StreamParams stream_param;
zhihuang1c378ed2017-08-17 14:10:50 -0700445 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000446 // Add the generated ssrc.
447 for (size_t i = 0; i < ssrcs.size(); ++i) {
448 stream_param.ssrcs.push_back(ssrcs[i]);
449 }
zhihuang1c378ed2017-08-17 14:10:50 -0700450 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000451 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
452 stream_param.ssrc_groups.push_back(group);
453 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700454 // Generate extra ssrcs for include_rtx_streams case.
455 if (include_rtx_streams) {
456 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200457 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700458 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
459 &rtx_ssrcs);
460 for (size_t i = 0; i < ssrcs.size(); ++i) {
461 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
462 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 }
brandtr03d5fb12016-11-22 03:37:59 -0800464 // Generate extra ssrc for include_flexfec_stream case.
465 if (include_flexfec_stream) {
466 // TODO(brandtr): Update when we support multistream protection.
467 if (ssrcs.size() == 1) {
468 std::vector<uint32_t> flexfec_ssrcs;
469 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
470 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
brandtr03d5fb12016-11-22 03:37:59 -0800471 } else if (!ssrcs.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100472 RTC_LOG(LS_WARNING)
brandtr03d5fb12016-11-22 03:37:59 -0800473 << "Our FlexFEC implementation only supports protecting "
Jonas Olsson45cc8902018-02-13 10:37:07 +0100474 "a single media streams. This session has multiple "
475 "media streams however, so no FlexFEC SSRC will be generated.";
brandtr03d5fb12016-11-22 03:37:59 -0800476 }
477 }
zhihuang1c378ed2017-08-17 14:10:50 -0700478 stream_param.cname = rtcp_cname;
Seth Hampson845e8782018-03-02 11:34:10 -0800479 stream_param.set_stream_ids(sender.stream_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000480 content_description->AddStream(stream_param);
481
482 // Store the new StreamParams in current_streams.
483 // This is necessary so that we can use the CNAME for other media types.
484 current_streams->push_back(stream_param);
485 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700486 // Use existing generated SSRCs/groups, but update the sync_label if
487 // necessary. This may be needed if a MediaStreamTrack was moved from one
488 // MediaStream to another.
Seth Hampson845e8782018-03-02 11:34:10 -0800489 param->set_stream_ids(sender.stream_ids);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000490 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000491 }
492 }
493 return true;
494}
495
496// Updates the transport infos of the |sdesc| according to the given
497// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800498// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
499// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000500static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
501 SessionDescription* sdesc) {
502 // The bundle should not be empty.
503 if (!sdesc || !bundle_group.FirstContentName()) {
504 return false;
505 }
506
507 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700508 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000509 const TransportInfo* selected_transport_info =
510 sdesc->GetTransportInfoByName(selected_content_name);
511 if (!selected_transport_info) {
512 return false;
513 }
514
515 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700516 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700518 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000519 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800520 ConnectionRole selected_connection_role =
521 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522 for (TransportInfos::iterator it =
523 sdesc->transport_infos().begin();
524 it != sdesc->transport_infos().end(); ++it) {
525 if (bundle_group.HasContentName(it->content_name) &&
526 it->content_name != selected_content_name) {
527 it->description.ice_ufrag = selected_ufrag;
528 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800529 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000530 }
531 }
532 return true;
533}
534
535// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
536// sets it to |cryptos|.
537static bool GetCryptosByName(const SessionDescription* sdesc,
538 const std::string& content_name,
539 CryptoParamsVec* cryptos) {
540 if (!sdesc || !cryptos) {
541 return false;
542 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000543 const ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800544 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000545 return false;
546 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800547 *cryptos = content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548 return true;
549}
550
551// Predicate function used by the remove_if.
552// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
553static bool CryptoNotFound(const CryptoParams crypto,
554 const CryptoParamsVec* filter) {
555 if (filter == NULL) {
556 return true;
557 }
558 for (CryptoParamsVec::const_iterator it = filter->begin();
559 it != filter->end(); ++it) {
560 if (it->cipher_suite == crypto.cipher_suite) {
561 return false;
562 }
563 }
564 return true;
565}
566
567// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
568// which are not available in |filter|.
569static void PruneCryptos(const CryptoParamsVec& filter,
570 CryptoParamsVec* target_cryptos) {
571 if (!target_cryptos) {
572 return;
573 }
574 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
575 target_cryptos->end(),
576 bind2nd(ptr_fun(CryptoNotFound),
577 &filter)),
578 target_cryptos->end());
579}
580
Steve Antonfa2260d2017-12-28 16:38:23 -0800581bool IsRtpProtocol(const std::string& protocol) {
deadbeefb5cb19b2015-11-23 16:39:12 -0800582 return protocol.empty() ||
583 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
584}
585
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586static bool IsRtpContent(SessionDescription* sdesc,
587 const std::string& content_name) {
588 bool is_rtp = false;
589 ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800590 if (content && content->media_description()) {
591 is_rtp = IsRtpProtocol(content->media_description()->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 }
593 return is_rtp;
594}
595
596// Updates the crypto parameters of the |sdesc| according to the given
597// |bundle_group|. The crypto parameters of all the contents within the
598// |bundle_group| should be updated to use the common subset of the
599// available cryptos.
600static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
601 SessionDescription* sdesc) {
602 // The bundle should not be empty.
603 if (!sdesc || !bundle_group.FirstContentName()) {
604 return false;
605 }
606
wu@webrtc.org78187522013-10-07 23:32:02 +0000607 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000608 // Get the common cryptos.
609 const ContentNames& content_names = bundle_group.content_names();
610 CryptoParamsVec common_cryptos;
611 for (ContentNames::const_iterator it = content_names.begin();
612 it != content_names.end(); ++it) {
613 if (!IsRtpContent(sdesc, *it)) {
614 continue;
615 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000616 // The common cryptos are needed if any of the content does not have DTLS
617 // enabled.
618 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
619 common_cryptos_needed = true;
620 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000621 if (it == content_names.begin()) {
622 // Initial the common_cryptos with the first content in the bundle group.
623 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
624 return false;
625 }
626 if (common_cryptos.empty()) {
627 // If there's no crypto params, we should just return.
628 return true;
629 }
630 } else {
631 CryptoParamsVec cryptos;
632 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
633 return false;
634 }
635 PruneCryptos(cryptos, &common_cryptos);
636 }
637 }
638
wu@webrtc.org78187522013-10-07 23:32:02 +0000639 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000640 return false;
641 }
642
643 // Update to use the common cryptos.
644 for (ContentNames::const_iterator it = content_names.begin();
645 it != content_names.end(); ++it) {
646 if (!IsRtpContent(sdesc, *it)) {
647 continue;
648 }
649 ContentInfo* content = sdesc->GetContentByName(*it);
650 if (IsMediaContent(content)) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800651 MediaContentDescription* media_desc = content->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000652 if (!media_desc) {
653 return false;
654 }
655 media_desc->set_cryptos(common_cryptos);
656 }
657 }
658 return true;
659}
660
661template <class C>
662static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800663 for (const auto& codec : codecs) {
664 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000665 return true;
666 }
667 }
668 return false;
669}
670
671template <class C>
672static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800673 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000674}
675
brandtr03d5fb12016-11-22 03:37:59 -0800676template <class C>
677static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
678 for (const auto& codec : codecs) {
679 if (IsFlexfecCodec(codec)) {
680 return true;
681 }
682 }
683 return false;
684}
685
686template <class C>
687static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800688 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800689}
690
zhihuang1c378ed2017-08-17 14:10:50 -0700691// Create a media content to be offered for the given |sender_options|,
692// according to the given options.rtcp_mux, session_options.is_muc, codecs,
693// secure_transport, crypto, and current_streams. If we don't currently have
694// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
695// created (according to crypto_suites). The created content is added to the
696// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000697template <class C>
698static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -0700699 const std::vector<SenderOptions>& sender_options,
700 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000701 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000702 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000703 const CryptoParamsVec* current_cryptos,
704 const std::vector<std::string>& crypto_suites,
705 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000706 StreamParamsVec* current_streams,
707 MediaContentDescriptionImpl<C>* offer) {
708 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709
zhihuang1c378ed2017-08-17 14:10:50 -0700710 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700711 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
712 offer->set_rtcp_reduced_size(true);
713 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000714 offer->set_rtp_header_extensions(rtp_extensions);
715
zhihuang1c378ed2017-08-17 14:10:50 -0700716 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
717 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718 return false;
719 }
720
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721 if (secure_policy != SEC_DISABLED) {
722 if (current_cryptos) {
723 AddMediaCryptos(*current_cryptos, offer);
724 }
725 if (offer->cryptos().empty()) {
726 if (!CreateMediaCryptos(crypto_suites, offer)) {
727 return false;
728 }
729 }
730 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731
deadbeef7af91dd2016-12-13 11:29:11 -0800732 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733 return false;
734 }
735 return true;
736}
737
738template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000739static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800740 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000741 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800742 const int codec2_id) {
743 const C* codec1 = FindCodecById(codecs1, codec1_id);
744 const C* codec2 = FindCodecById(codecs2, codec2_id);
745 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000746}
747
748template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000749static void NegotiateCodecs(const std::vector<C>& local_codecs,
750 const std::vector<C>& offered_codecs,
751 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800752 for (const C& ours : local_codecs) {
753 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700754 // Note that we intentionally only find one matching codec for each of our
755 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800756 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
757 C negotiated = ours;
758 negotiated.IntersectFeedbackParams(theirs);
759 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800760 const auto apt_it =
761 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800762 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800763 RTC_DCHECK(apt_it != theirs.params.end());
764 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765 }
magjedf823ede2016-11-12 09:53:04 -0800766 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
767 webrtc::H264::GenerateProfileLevelIdForAnswer(
768 ours.params, theirs.params, &negotiated.params);
769 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800770 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700771 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800772 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000773 }
774 }
deadbeef67cf2c12016-04-13 10:07:16 -0700775 // RFC3264: Although the answerer MAY list the formats in their desired
776 // order of preference, it is RECOMMENDED that unless there is a
777 // specific reason, the answerer list formats in the same relative order
778 // they were present in the offer.
779 std::unordered_map<int, int> payload_type_preferences;
780 int preference = static_cast<int>(offered_codecs.size() + 1);
781 for (const C& codec : offered_codecs) {
782 payload_type_preferences[codec.id] = preference--;
783 }
784 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
785 [&payload_type_preferences](const C& a, const C& b) {
786 return payload_type_preferences[a.id] >
787 payload_type_preferences[b.id];
788 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000789}
790
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800791// Finds a codec in |codecs2| that matches |codec_to_match|, which is
792// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
793// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000794template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800795static bool FindMatchingCodec(const std::vector<C>& codecs1,
796 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000797 const C& codec_to_match,
798 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700799 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
800 // codecs' associated codecs correctly. If not, that's a programming error.
801 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
802 [&codec_to_match](const C& codec) {
803 return &codec == &codec_to_match;
804 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800805 for (const C& potential_match : codecs2) {
806 if (potential_match.Matches(codec_to_match)) {
807 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800808 int apt_value_1 = 0;
809 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800810 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
811 &apt_value_1) ||
812 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
813 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100814 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800815 continue;
816 }
817 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
818 apt_value_2)) {
819 continue;
820 }
821 }
822 if (found_codec) {
823 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000824 }
825 return true;
826 }
827 }
828 return false;
829}
830
zhihuang1c378ed2017-08-17 14:10:50 -0700831// Find the codec in |codec_list| that |rtx_codec| is associated with.
832template <class C>
833static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
834 const C& rtx_codec) {
835 std::string associated_pt_str;
836 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
837 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100838 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
839 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 14:10:50 -0700840 return nullptr;
841 }
842
843 int associated_pt;
844 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100845 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
846 << " of RTX codec " << rtx_codec.name
847 << " to an integer.";
zhihuang1c378ed2017-08-17 14:10:50 -0700848 return nullptr;
849 }
850
851 // Find the associated reference codec for the reference RTX codec.
852 const C* associated_codec = FindCodecById(codec_list, associated_pt);
853 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100854 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
855 << associated_pt << " for RTX codec " << rtx_codec.name
856 << ".";
zhihuang1c378ed2017-08-17 14:10:50 -0700857 }
858 return associated_codec;
859}
860
861// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000862// already exist in |offered_codecs| and ensure the payload types don't
863// collide.
864template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700865static void MergeCodecs(const std::vector<C>& reference_codecs,
866 std::vector<C>* offered_codecs,
867 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000868 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800869 for (const C& reference_codec : reference_codecs) {
870 if (!IsRtxCodec(reference_codec) &&
871 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
872 reference_codec, nullptr)) {
873 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000874 used_pltypes->FindAndSetIdUsed(&codec);
875 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000876 }
877 }
878
879 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800880 for (const C& reference_codec : reference_codecs) {
881 if (IsRtxCodec(reference_codec) &&
882 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
883 reference_codec, nullptr)) {
884 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700885 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700886 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700887 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700888 continue;
889 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800890 // Find a codec in the offered list that matches the reference codec.
891 // Its payload type may be different than the reference codec.
892 C matching_codec;
893 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800894 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100895 RTC_LOG(LS_WARNING)
896 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800897 continue;
898 }
899
900 rtx_codec.params[kCodecParamAssociatedPayloadType] =
901 rtc::ToString(matching_codec.id);
902 used_pltypes->FindAndSetIdUsed(&rtx_codec);
903 offered_codecs->push_back(rtx_codec);
904 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000905 }
906}
907
zhihuang1c378ed2017-08-17 14:10:50 -0700908static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
909 const webrtc::RtpExtension& ext_to_match,
910 webrtc::RtpExtension* found_extension) {
911 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
912 it != extensions.end(); ++it) {
913 // We assume that all URIs are given in a canonical format.
914 if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
915 if (found_extension) {
916 *found_extension = *it;
917 }
918 return true;
919 }
920 }
921 return false;
922}
923
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000924static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700925 const webrtc::RtpExtension& ext_to_match,
926 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -0700927 // We assume that all URIs are given in a canonical format.
928 const webrtc::RtpExtension* found =
929 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
930 ext_to_match.uri);
931 if (!found) {
932 return false;
933 }
934 if (found_extension) {
935 *found_extension = *found;
936 }
937 return true;
938}
939
940static bool FindByUriWithEncryptionPreference(
941 const RtpHeaderExtensions& extensions,
942 const webrtc::RtpExtension& ext_to_match, bool encryption_preference,
943 webrtc::RtpExtension* found_extension) {
944 const webrtc::RtpExtension* unencrypted_extension = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000945 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
946 it != extensions.end(); ++it) {
947 // We assume that all URIs are given in a canonical format.
948 if (it->uri == ext_to_match.uri) {
jbauch5869f502017-06-29 12:31:36 -0700949 if (!encryption_preference || it->encrypt) {
950 if (found_extension) {
951 *found_extension = *it;
952 }
953 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000954 }
jbauch5869f502017-06-29 12:31:36 -0700955 unencrypted_extension = &(*it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000956 }
957 }
jbauch5869f502017-06-29 12:31:36 -0700958 if (unencrypted_extension) {
959 if (found_extension) {
960 *found_extension = *unencrypted_extension;
961 }
962 return true;
963 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000964 return false;
965}
966
zhihuang1c378ed2017-08-17 14:10:50 -0700967// Adds all extensions from |reference_extensions| to |offered_extensions| that
968// don't already exist in |offered_extensions| and ensure the IDs don't
969// collide. If an extension is added, it's also added to |regular_extensions| or
970// |encrypted_extensions|, and if the extension is in |regular_extensions| or
971// |encrypted_extensions|, its ID is marked as used in |used_ids|.
972// |offered_extensions| is for either audio or video while |regular_extensions|
973// and |encrypted_extensions| are used for both audio and video. There could be
974// overlap between audio extensions and video extensions.
975static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
976 RtpHeaderExtensions* offered_extensions,
977 RtpHeaderExtensions* regular_extensions,
978 RtpHeaderExtensions* encrypted_extensions,
979 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -0700980 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -0700981 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
982 nullptr)) {
olka3c747662017-08-17 06:50:32 -0700983 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -0700984 if (reference_extension.encrypt) {
985 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
986 &existing)) {
987 offered_extensions->push_back(existing);
988 } else {
989 used_ids->FindAndSetIdUsed(&reference_extension);
990 encrypted_extensions->push_back(reference_extension);
991 offered_extensions->push_back(reference_extension);
992 }
olka3c747662017-08-17 06:50:32 -0700993 } else {
zhihuang1c378ed2017-08-17 14:10:50 -0700994 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
995 &existing)) {
996 offered_extensions->push_back(existing);
997 } else {
998 used_ids->FindAndSetIdUsed(&reference_extension);
999 regular_extensions->push_back(reference_extension);
1000 offered_extensions->push_back(reference_extension);
1001 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001002 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001003 }
1004 }
1005}
1006
jbauch5869f502017-06-29 12:31:36 -07001007static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1008 RtpHeaderExtensions* all_extensions,
1009 UsedRtpHeaderExtensionIds* used_ids) {
1010 RtpHeaderExtensions encrypted_extensions;
1011 for (const webrtc::RtpExtension& extension : *extensions) {
1012 webrtc::RtpExtension existing;
1013 // Don't add encrypted extensions again that were already included in a
1014 // previous offer or regular extensions that are also included as encrypted
1015 // extensions.
1016 if (extension.encrypt ||
1017 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1018 (FindByUriWithEncryptionPreference(*extensions, extension, true,
1019 &existing) && existing.encrypt)) {
1020 continue;
1021 }
1022
1023 if (FindByUri(*all_extensions, extension, &existing)) {
1024 encrypted_extensions.push_back(existing);
1025 } else {
1026 webrtc::RtpExtension encrypted(extension);
1027 encrypted.encrypt = true;
1028 used_ids->FindAndSetIdUsed(&encrypted);
1029 all_extensions->push_back(encrypted);
1030 encrypted_extensions.push_back(encrypted);
1031 }
1032 }
1033 extensions->insert(extensions->end(), encrypted_extensions.begin(),
1034 encrypted_extensions.end());
1035}
1036
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001037static void NegotiateRtpHeaderExtensions(
1038 const RtpHeaderExtensions& local_extensions,
1039 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001040 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001041 RtpHeaderExtensions* negotiated_extenstions) {
1042 RtpHeaderExtensions::const_iterator ours;
1043 for (ours = local_extensions.begin();
1044 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001045 webrtc::RtpExtension theirs;
jbauch5869f502017-06-29 12:31:36 -07001046 if (FindByUriWithEncryptionPreference(offered_extensions, *ours,
1047 enable_encrypted_rtp_header_extensions, &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048 // We respond with their RTP header extension id.
1049 negotiated_extenstions->push_back(theirs);
1050 }
1051 }
1052}
1053
1054static void StripCNCodecs(AudioCodecs* audio_codecs) {
1055 AudioCodecs::iterator iter = audio_codecs->begin();
1056 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001057 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001058 iter = audio_codecs->erase(iter);
1059 } else {
1060 ++iter;
1061 }
1062 }
1063}
1064
zhihuang1c378ed2017-08-17 14:10:50 -07001065// Create a media content to be answered for the given |sender_options|
1066// according to the given session_options.rtcp_mux, session_options.streams,
1067// codecs, crypto, and current_streams. If we don't currently have crypto (in
1068// current_cryptos) and it is enabled (in secure_policy), crypto is created
1069// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1070// negotiated with the offer. If the negotiation fails, this method returns
1071// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001072template <class C>
1073static bool CreateMediaContentAnswer(
1074 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001075 const MediaDescriptionOptions& media_description_options,
1076 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001077 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001078 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001079 const CryptoParamsVec* current_cryptos,
1080 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 12:31:36 -07001081 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001082 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001083 bool bundle_enabled,
1084 MediaContentDescriptionImpl<C>* answer) {
1085 std::vector<C> negotiated_codecs;
1086 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1087 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001088 answer->set_protocol(offer->protocol());
1089 RtpHeaderExtensions negotiated_rtp_extensions;
1090 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1091 offer->rtp_header_extensions(),
jbauch5869f502017-06-29 12:31:36 -07001092 enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001093 &negotiated_rtp_extensions);
1094 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1095
zhihuang1c378ed2017-08-17 14:10:50 -07001096 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001097 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1098 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1099 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001100
1101 if (sdes_policy != SEC_DISABLED) {
1102 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001103 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1104 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001105 if (current_cryptos) {
1106 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1107 }
1108 answer->AddCrypto(crypto);
1109 }
1110 }
1111
deadbeef7af91dd2016-12-13 11:29:11 -08001112 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001113 return false;
1114 }
1115
zhihuang1c378ed2017-08-17 14:10:50 -07001116 if (!AddStreamParams(media_description_options.sender_options,
1117 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001118 return false; // Something went seriously wrong.
1119 }
1120
Steve Anton4e70a722017-11-28 14:57:10 -08001121 answer->set_direction(NegotiateRtpTransceiverDirection(
1122 offer->direction(), media_description_options.direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001123 return true;
1124}
1125
1126static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001127 const std::string& protocol,
1128 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001129 // Since not all applications serialize and deserialize the media protocol,
1130 // we will have to accept |protocol| to be empty.
1131 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 return true;
1133 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001134
zhihuangcf5b37c2016-05-05 11:44:35 -07001135 if (type == MEDIA_TYPE_DATA) {
1136 // Check for SCTP, but also for RTP for RTP-based data channels.
1137 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1138 if (secure_transport) {
1139 // Most likely scenarios first.
1140 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1141 IsPlainRtp(protocol);
1142 } else {
1143 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1144 }
1145 }
1146
1147 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1148 // JSEP specifies.
1149 if (secure_transport) {
1150 // Most likely scenarios first.
1151 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1152 } else {
1153 return IsPlainRtp(protocol);
1154 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001155}
1156
1157static void SetMediaProtocol(bool secure_transport,
1158 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001159 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001160 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001161 else if (secure_transport)
1162 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001163 else
1164 desc->set_protocol(kMediaProtocolAvpf);
1165}
1166
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001167// Gets the TransportInfo of the given |content_name| from the
1168// |current_description|. If doesn't exist, returns a new one.
1169static const TransportDescription* GetTransportDescription(
1170 const std::string& content_name,
1171 const SessionDescription* current_description) {
1172 const TransportDescription* desc = NULL;
1173 if (current_description) {
1174 const TransportInfo* info =
1175 current_description->GetTransportInfoByName(content_name);
1176 if (info) {
1177 desc = &info->description;
1178 }
1179 }
1180 return desc;
1181}
1182
1183// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001184static bool IsDtlsActive(const ContentInfo* content,
1185 const SessionDescription* current_description) {
1186 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001187 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001188 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001189
zhihuang1c378ed2017-08-17 14:10:50 -07001190 size_t msection_index = content - &current_description->contents()[0];
1191
1192 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001193 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001194 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001195
zhihuang1c378ed2017-08-17 14:10:50 -07001196 return current_description->transport_infos()[msection_index]
1197 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001198}
1199
Steve Anton8ffb9c32017-08-31 15:45:38 -07001200void MediaDescriptionOptions::AddAudioSender(
1201 const std::string& track_id,
1202 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001203 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001204 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001205}
1206
Steve Anton8ffb9c32017-08-31 15:45:38 -07001207void MediaDescriptionOptions::AddVideoSender(
1208 const std::string& track_id,
1209 const std::vector<std::string>& stream_ids,
1210 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001211 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001212 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001213}
1214
zhihuang1c378ed2017-08-17 14:10:50 -07001215void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1216 const std::string& stream_id) {
1217 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001218 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1219 // than one stream?
1220 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001221}
1222
Steve Anton8ffb9c32017-08-31 15:45:38 -07001223void MediaDescriptionOptions::AddSenderInternal(
1224 const std::string& track_id,
1225 const std::vector<std::string>& stream_ids,
1226 int num_sim_layers) {
1227 // TODO(steveanton): Support any number of stream ids.
1228 RTC_CHECK(stream_ids.size() == 1U);
1229 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001230}
1231
zhihuang1c378ed2017-08-17 14:10:50 -07001232bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1233 return std::find_if(media_description_options.begin(),
1234 media_description_options.end(),
1235 [type](const MediaDescriptionOptions& t) {
1236 return t.type == type;
1237 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001238}
1239
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1241 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001242 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001243
1244MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1245 ChannelManager* channel_manager,
1246 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001247 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001248 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1249 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001250 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001251 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001252 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1253 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001254 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001255}
1256
ossudedfd282016-06-14 07:12:39 -07001257const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1258 const {
ossu075af922016-06-14 03:29:38 -07001259 return audio_sendrecv_codecs_;
1260}
1261
1262const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1263 return audio_send_codecs_;
1264}
1265
1266const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1267 return audio_recv_codecs_;
1268}
1269
1270void MediaSessionDescriptionFactory::set_audio_codecs(
1271 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1272 audio_send_codecs_ = send_codecs;
1273 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001274 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001275}
1276
1277SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001278 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001279 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001280 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001281
1282 StreamParamsVec current_streams;
1283 GetCurrentStreamParams(current_description, &current_streams);
1284
zhihuang1c378ed2017-08-17 14:10:50 -07001285 AudioCodecs offer_audio_codecs;
1286 VideoCodecs offer_video_codecs;
1287 DataCodecs offer_data_codecs;
1288 GetCodecsForOffer(current_description, &offer_audio_codecs,
1289 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001290
zhihuang1c378ed2017-08-17 14:10:50 -07001291 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001293 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001294 }
zhihuang1c378ed2017-08-17 14:10:50 -07001295 FilterDataCodecs(&offer_data_codecs,
1296 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001297
1298 RtpHeaderExtensions audio_rtp_extensions;
1299 RtpHeaderExtensions video_rtp_extensions;
Steve Anton1b8773d2018-04-06 11:13:34 -07001300 GetRtpHdrExtsToOffer(session_options, current_description,
1301 &audio_rtp_extensions, &video_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001302
zhihuang1c378ed2017-08-17 14:10:50 -07001303 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001304 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001305 RTC_DCHECK(current_description->contents().size() <=
1306 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001307 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001308
zhihuang1c378ed2017-08-17 14:10:50 -07001309 // Iterate through the media description options, matching with existing media
1310 // descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001311 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001312 for (const MediaDescriptionOptions& media_description_options :
1313 session_options.media_description_options) {
1314 const ContentInfo* current_content = nullptr;
1315 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001316 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001317 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-22 16:02:54 -08001318 // Media type must match unless this media section is being recycled.
1319 RTC_DCHECK(current_content->rejected ||
1320 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 14:10:50 -07001321 media_description_options.type));
1322 }
1323 switch (media_description_options.type) {
1324 case MEDIA_TYPE_AUDIO:
1325 if (!AddAudioContentForOffer(media_description_options, session_options,
1326 current_content, current_description,
1327 audio_rtp_extensions, offer_audio_codecs,
1328 &current_streams, offer.get())) {
1329 return nullptr;
1330 }
1331 break;
1332 case MEDIA_TYPE_VIDEO:
1333 if (!AddVideoContentForOffer(media_description_options, session_options,
1334 current_content, current_description,
1335 video_rtp_extensions, offer_video_codecs,
1336 &current_streams, offer.get())) {
1337 return nullptr;
1338 }
1339 break;
1340 case MEDIA_TYPE_DATA:
1341 if (!AddDataContentForOffer(media_description_options, session_options,
1342 current_content, current_description,
1343 offer_data_codecs, &current_streams,
1344 offer.get())) {
1345 return nullptr;
1346 }
1347 break;
1348 default:
1349 RTC_NOTREACHED();
1350 }
1351 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001352 }
1353
1354 // Bundle the contents together, if we've been asked to do so, and update any
1355 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 14:10:50 -07001356 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001357 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001358 for (const ContentInfo& content : offer->contents()) {
1359 // TODO(deadbeef): There are conditions that make bundling two media
1360 // descriptions together illegal. For example, they use the same payload
1361 // type to represent different codecs, or same IDs for different header
1362 // extensions. We need to detect this and not try to bundle those media
1363 // descriptions together.
1364 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365 }
1366 offer->AddGroup(offer_bundle);
1367 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001368 RTC_LOG(LS_ERROR)
1369 << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001370 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001371 }
1372 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001373 RTC_LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001374 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001375 }
1376 }
Steve Antone831b8c2018-02-01 12:22:16 -08001377
1378 // The following determines how to signal MSIDs to ensure compatibility with
1379 // older endpoints (in particular, older Plan B endpoints).
1380 if (session_options.is_unified_plan) {
1381 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1382 // Plan answerers will look at a=msid and Plan B answerers will look at the
1383 // a=ssrc MSID line.
1384 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1385 cricket::kMsidSignalingSsrcAttribute);
1386 } else {
1387 // Plan B always signals MSID using a=ssrc lines.
1388 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1389 }
1390
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001391 return offer.release();
1392}
1393
1394SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001395 const SessionDescription* offer,
1396 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001397 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001398 if (!offer) {
1399 return nullptr;
1400 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001401 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001402 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1403 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001404 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001405
1406 StreamParamsVec current_streams;
1407 GetCurrentStreamParams(current_description, &current_streams);
1408
deadbeefb7892532017-02-22 19:35:18 -08001409 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1410 // group in the answer with the appropriate content names.
1411 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1412 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1413 // Transport info shared by the bundle group.
1414 std::unique_ptr<TransportInfo> bundle_transport;
1415
zhihuang1c378ed2017-08-17 14:10:50 -07001416 // Get list of all possible codecs that respects existing payload type
1417 // mappings and uses a single payload type space.
1418 //
1419 // Note that these lists may be further filtered for each m= section; this
1420 // step is done just to establish the payload type mappings shared by all
1421 // sections.
1422 AudioCodecs answer_audio_codecs;
1423 VideoCodecs answer_video_codecs;
1424 DataCodecs answer_data_codecs;
1425 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1426 &answer_video_codecs, &answer_data_codecs);
1427
1428 if (!session_options.vad_enabled) {
1429 // If application doesn't want CN codecs in answer.
1430 StripCNCodecs(&answer_audio_codecs);
1431 }
1432 FilterDataCodecs(&answer_data_codecs,
1433 session_options.data_channel_type == DCT_SCTP);
1434
1435 // Must have options for exactly as many sections as in the offer.
1436 RTC_DCHECK(offer->contents().size() ==
1437 session_options.media_description_options.size());
1438 // Iterate through the media description options, matching with existing
1439 // media descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001440 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001441 for (const MediaDescriptionOptions& media_description_options :
1442 session_options.media_description_options) {
1443 const ContentInfo* offer_content = &offer->contents()[msection_index];
1444 // Media types and MIDs must match between the remote offer and the
1445 // MediaDescriptionOptions.
1446 RTC_DCHECK(
1447 IsMediaContentOfType(offer_content, media_description_options.type));
1448 RTC_DCHECK(media_description_options.mid == offer_content->name);
1449 const ContentInfo* current_content = nullptr;
1450 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001451 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001452 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001453 }
zhihuang1c378ed2017-08-17 14:10:50 -07001454 switch (media_description_options.type) {
1455 case MEDIA_TYPE_AUDIO:
1456 if (!AddAudioContentForAnswer(
1457 media_description_options, session_options, offer_content,
1458 offer, current_content, current_description,
1459 bundle_transport.get(), answer_audio_codecs, &current_streams,
1460 answer.get())) {
1461 return nullptr;
1462 }
1463 break;
1464 case MEDIA_TYPE_VIDEO:
1465 if (!AddVideoContentForAnswer(
1466 media_description_options, session_options, offer_content,
1467 offer, current_content, current_description,
1468 bundle_transport.get(), answer_video_codecs, &current_streams,
1469 answer.get())) {
1470 return nullptr;
1471 }
1472 break;
1473 case MEDIA_TYPE_DATA:
1474 if (!AddDataContentForAnswer(media_description_options, session_options,
1475 offer_content, offer, current_content,
1476 current_description,
1477 bundle_transport.get(), answer_data_codecs,
1478 &current_streams, answer.get())) {
1479 return nullptr;
1480 }
1481 break;
1482 default:
1483 RTC_NOTREACHED();
1484 }
1485 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001486 // See if we can add the newly generated m= section to the BUNDLE group in
1487 // the answer.
1488 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001489 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001490 offer_bundle->HasContentName(added.name)) {
1491 answer_bundle.AddContentName(added.name);
1492 bundle_transport.reset(
1493 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001494 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001495 }
1496
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001497 // If a BUNDLE group was offered, put a BUNDLE group in the answer even if
1498 // it's empty. RFC5888 says:
1499 //
1500 // A SIP entity that receives an offer that contains an "a=group" line
1501 // with semantics that are understood MUST return an answer that
1502 // contains an "a=group" line with the same semantics.
1503 if (offer_bundle) {
deadbeefb7892532017-02-22 19:35:18 -08001504 answer->AddGroup(answer_bundle);
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001505 }
deadbeefb7892532017-02-22 19:35:18 -08001506
Taylor Brandstetter0ab56512018-04-12 10:30:48 -07001507 if (answer_bundle.FirstContentName()) {
deadbeefb7892532017-02-22 19:35:18 -08001508 // Share the same ICE credentials and crypto params across all contents,
1509 // as BUNDLE requires.
1510 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001511 RTC_LOG(LS_ERROR)
1512 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001513 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001514 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001515
deadbeefb7892532017-02-22 19:35:18 -08001516 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001517 RTC_LOG(LS_ERROR)
1518 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001519 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001520 }
1521 }
1522
Steve Antone831b8c2018-02-01 12:22:16 -08001523 // The following determines how to signal MSIDs to ensure compatibility with
1524 // older endpoints (in particular, older Plan B endpoints).
1525 if (session_options.is_unified_plan) {
1526 // Unified Plan needs to look at what the offer included to find the most
1527 // compatible answer.
1528 if (offer->msid_signaling() == 0) {
1529 // We end up here in one of three cases:
1530 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1531 // matter what we pick here.
1532 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1533 // it also doesn't matter what we pick here.
1534 // 3. Media that's either sendonly or inactive from the remote endpoint.
1535 // We don't have any information to say whether the endpoint is Plan B
1536 // or Unified Plan, so be conservative and send both.
1537 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1538 cricket::kMsidSignalingSsrcAttribute);
1539 } else if (offer->msid_signaling() ==
1540 (cricket::kMsidSignalingMediaSection |
1541 cricket::kMsidSignalingSsrcAttribute)) {
1542 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1543 // probably talking to a Unified Plan endpoint so respond with just
1544 // a=msid.
1545 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1546 } else {
1547 // Otherwise, it's clear which method the offerer is using so repeat that
1548 // back to them.
1549 answer->set_msid_signaling(offer->msid_signaling());
1550 }
1551 } else {
1552 // Plan B always signals MSID using a=ssrc lines.
1553 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1554 }
1555
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001556 return answer.release();
1557}
1558
ossu075af922016-06-14 03:29:38 -07001559const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1560 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001561 switch (direction) {
1562 // If stream is inactive - generate list as if sendrecv.
1563 case RtpTransceiverDirection::kSendRecv:
1564 case RtpTransceiverDirection::kInactive:
1565 return audio_sendrecv_codecs_;
1566 case RtpTransceiverDirection::kSendOnly:
1567 return audio_send_codecs_;
1568 case RtpTransceiverDirection::kRecvOnly:
1569 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001570 }
Steve Anton1d03a752017-11-27 14:30:09 -08001571 RTC_NOTREACHED();
1572 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001573}
1574
1575const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1576 const RtpTransceiverDirection& offer,
1577 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001578 switch (answer) {
1579 // For inactive and sendrecv answers, generate lists as if we were to accept
1580 // the offer's direction. See RFC 3264 Section 6.1.
1581 case RtpTransceiverDirection::kSendRecv:
1582 case RtpTransceiverDirection::kInactive:
1583 return GetAudioCodecsForOffer(
1584 webrtc::RtpTransceiverDirectionReversed(offer));
1585 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 03:29:38 -07001586 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 14:30:09 -08001587 case RtpTransceiverDirection::kRecvOnly:
1588 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001589 }
Steve Anton1d03a752017-11-27 14:30:09 -08001590 RTC_NOTREACHED();
1591 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001592}
1593
zhihuang1c378ed2017-08-17 14:10:50 -07001594void MergeCodecsFromDescription(const SessionDescription* description,
1595 AudioCodecs* audio_codecs,
1596 VideoCodecs* video_codecs,
1597 DataCodecs* data_codecs,
1598 UsedPayloadTypes* used_pltypes) {
1599 RTC_DCHECK(description);
1600 for (const ContentInfo& content : description->contents()) {
1601 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1602 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001603 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001604 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1605 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1606 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001607 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001608 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1609 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1610 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001611 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001612 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1613 }
1614 }
1615}
1616
1617// Getting codecs for an offer involves these steps:
1618//
1619// 1. Construct payload type -> codec mappings for current description.
1620// 2. Add any reference codecs that weren't already present
1621// 3. For each individual media description (m= section), filter codecs based
1622// on the directional attribute (happens in another method).
1623void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001624 const SessionDescription* current_description,
1625 AudioCodecs* audio_codecs,
1626 VideoCodecs* video_codecs,
1627 DataCodecs* data_codecs) const {
1628 UsedPayloadTypes used_pltypes;
1629 audio_codecs->clear();
1630 video_codecs->clear();
1631 data_codecs->clear();
1632
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001633 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001634 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1635 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001636 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001637 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1638 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001639 }
1640
1641 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001642 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1643 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1644 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1645}
1646
1647// Getting codecs for an answer involves these steps:
1648//
1649// 1. Construct payload type -> codec mappings for current description.
1650// 2. Add any codecs from the offer that weren't already present.
1651// 3. Add any remaining codecs that weren't already present.
1652// 4. For each individual media description (m= section), filter codecs based
1653// on the directional attribute (happens in another method).
1654void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1655 const SessionDescription* current_description,
1656 const SessionDescription* remote_offer,
1657 AudioCodecs* audio_codecs,
1658 VideoCodecs* video_codecs,
1659 DataCodecs* data_codecs) const {
1660 UsedPayloadTypes used_pltypes;
1661 audio_codecs->clear();
1662 video_codecs->clear();
1663 data_codecs->clear();
1664
1665 // First - get all codecs from the current description if the media type
1666 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1667 // new media type is added.
1668 if (current_description) {
1669 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1670 data_codecs, &used_pltypes);
1671 }
1672
1673 // Second - filter out codecs that we don't support at all and should ignore.
1674 AudioCodecs filtered_offered_audio_codecs;
1675 VideoCodecs filtered_offered_video_codecs;
1676 DataCodecs filtered_offered_data_codecs;
1677 for (const ContentInfo& content : remote_offer->contents()) {
1678 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1679 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001680 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001681 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1682 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1683 filtered_offered_audio_codecs,
1684 offered_audio_codec, nullptr) &&
1685 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1686 offered_audio_codec, nullptr)) {
1687 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1688 }
1689 }
1690 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1691 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001692 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001693 for (const VideoCodec& offered_video_codec : video->codecs()) {
1694 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1695 filtered_offered_video_codecs,
1696 offered_video_codec, nullptr) &&
1697 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1698 offered_video_codec, nullptr)) {
1699 filtered_offered_video_codecs.push_back(offered_video_codec);
1700 }
1701 }
1702 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1703 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001704 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001705 for (const DataCodec& offered_data_codec : data->codecs()) {
1706 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1707 filtered_offered_data_codecs,
1708 offered_data_codec, nullptr) &&
1709 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1710 offered_data_codec, nullptr)) {
1711 filtered_offered_data_codecs.push_back(offered_data_codec);
1712 }
1713 }
1714 }
1715 }
1716
1717 // Add codecs that are not in |current_description| but were in
1718 // |remote_offer|.
1719 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1720 &used_pltypes);
1721 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1722 &used_pltypes);
1723 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1724 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001725}
1726
1727void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
Steve Anton1b8773d2018-04-06 11:13:34 -07001728 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001729 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001730 RtpHeaderExtensions* offer_audio_extensions,
1731 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001732 // All header extensions allocated from the same range to avoid potential
1733 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001734 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001735 RtpHeaderExtensions all_regular_extensions;
1736 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 14:10:50 -07001737 offer_audio_extensions->clear();
1738 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001739
1740 // First - get all extensions from the current description if the media type
1741 // is used.
1742 // Add them to |used_ids| so the local ids are not reused if a new media
1743 // type is added.
1744 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001745 for (const ContentInfo& content : current_description->contents()) {
1746 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1747 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001748 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001749 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1750 &all_regular_extensions, &all_encrypted_extensions,
1751 &used_ids);
1752 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1753 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001754 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001755 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1756 &all_regular_extensions, &all_encrypted_extensions,
1757 &used_ids);
1758 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001759 }
1760 }
1761
1762 // Add our default RTP header extensions that are not in
1763 // |current_description|.
Steve Anton1b8773d2018-04-06 11:13:34 -07001764 MergeRtpHdrExts(audio_rtp_header_extensions(session_options.is_unified_plan),
1765 offer_audio_extensions, &all_regular_extensions,
1766 &all_encrypted_extensions, &used_ids);
1767 MergeRtpHdrExts(video_rtp_header_extensions(session_options.is_unified_plan),
1768 offer_video_extensions, &all_regular_extensions,
1769 &all_encrypted_extensions, &used_ids);
zhihuang1c378ed2017-08-17 14:10:50 -07001770
jbauch5869f502017-06-29 12:31:36 -07001771 // TODO(jbauch): Support adding encrypted header extensions to existing
1772 // sessions.
1773 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001774 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1775 &all_encrypted_extensions, &used_ids);
1776 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1777 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001778 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001779}
1780
1781bool MediaSessionDescriptionFactory::AddTransportOffer(
1782 const std::string& content_name,
1783 const TransportOptions& transport_options,
1784 const SessionDescription* current_desc,
1785 SessionDescription* offer_desc) const {
1786 if (!transport_desc_factory_)
1787 return false;
1788 const TransportDescription* current_tdesc =
1789 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001790 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001791 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1792 bool ret = (new_tdesc.get() != NULL &&
1793 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1794 if (!ret) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001795 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
1796 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001797 }
1798 return ret;
1799}
1800
1801TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1802 const std::string& content_name,
1803 const SessionDescription* offer_desc,
1804 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08001805 const SessionDescription* current_desc,
1806 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001807 if (!transport_desc_factory_)
1808 return NULL;
1809 const TransportDescription* offer_tdesc =
1810 GetTransportDescription(content_name, offer_desc);
1811 const TransportDescription* current_tdesc =
1812 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08001813 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1814 require_transport_attributes,
1815 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001816}
1817
1818bool MediaSessionDescriptionFactory::AddTransportAnswer(
1819 const std::string& content_name,
1820 const TransportDescription& transport_desc,
1821 SessionDescription* answer_desc) const {
1822 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1823 transport_desc))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001824 RTC_LOG(LS_ERROR) << "Failed to AddTransportAnswer, content name="
1825 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001826 return false;
1827 }
1828 return true;
1829}
1830
zhihuang1c378ed2017-08-17 14:10:50 -07001831// |audio_codecs| = set of all possible codecs that can be used, with correct
1832// payload type mappings
1833//
1834// |supported_audio_codecs| = set of codecs that are supported for the direction
1835// of this m= section
1836//
1837// acd->codecs() = set of previously negotiated codecs for this m= section
1838//
1839// The payload types should come from audio_codecs, but the order should come
1840// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1841// change existing codec priority, and that new codecs are added with the right
1842// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001843bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001844 const MediaDescriptionOptions& media_description_options,
1845 const MediaSessionOptions& session_options,
1846 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001847 const SessionDescription* current_description,
1848 const RtpHeaderExtensions& audio_rtp_extensions,
1849 const AudioCodecs& audio_codecs,
1850 StreamParamsVec* current_streams,
1851 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001852 // Filter audio_codecs (which includes all codecs, with correctly remapped
1853 // payload types) based on transceiver direction.
1854 const AudioCodecs& supported_audio_codecs =
1855 GetAudioCodecsForOffer(media_description_options.direction);
1856
1857 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001858 // Add the codecs from current content if it exists and is not being recycled.
1859 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001860 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07001861 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001862 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001863 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001864 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1865 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001866 filtered_codecs.push_back(codec);
1867 }
1868 }
1869 }
1870 // Add other supported audio codecs.
1871 AudioCodec found_codec;
1872 for (const AudioCodec& codec : supported_audio_codecs) {
1873 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1874 codec, &found_codec) &&
1875 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1876 codec, nullptr)) {
1877 // Use the |found_codec| from |audio_codecs| because it has the correctly
1878 // mapped payload type.
1879 filtered_codecs.push_back(found_codec);
1880 }
1881 }
deadbeef44f08192015-12-15 16:20:09 -08001882
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001883 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001884 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1885 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001886
kwiberg31022942016-03-11 14:18:21 -08001887 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001888 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001889 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1890 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001891 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001892 media_description_options.sender_options, session_options,
1893 filtered_codecs, sdes_policy, GetCryptos(current_content),
1894 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001895 return false;
1896 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001897
1898 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1899 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001900
Steve Anton4e70a722017-11-28 14:57:10 -08001901 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001902
Steve Anton5adfafd2017-12-20 16:34:00 -08001903 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001904 media_description_options.stopped, audio.release());
1905 if (!AddTransportOffer(media_description_options.mid,
1906 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001907 current_description, desc)) {
1908 return false;
1909 }
1910
1911 return true;
1912}
1913
1914bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001915 const MediaDescriptionOptions& media_description_options,
1916 const MediaSessionOptions& session_options,
1917 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001918 const SessionDescription* current_description,
1919 const RtpHeaderExtensions& video_rtp_extensions,
1920 const VideoCodecs& video_codecs,
1921 StreamParamsVec* current_streams,
1922 SessionDescription* desc) const {
1923 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001924 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1925 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001926
kwiberg31022942016-03-11 14:18:21 -08001927 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001928 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001929 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1930 &crypto_suites);
1931
1932 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001933 // Add the codecs from current content if it exists and is not being recycled.
1934 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001935 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07001936 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001937 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001938 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001939 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07001940 nullptr)) {
1941 filtered_codecs.push_back(codec);
1942 }
1943 }
1944 }
1945 // Add other supported video codecs.
1946 VideoCodec found_codec;
1947 for (const VideoCodec& codec : video_codecs_) {
1948 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1949 &found_codec) &&
1950 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1951 nullptr)) {
1952 // Use the |found_codec| from |video_codecs| because it has the correctly
1953 // mapped payload type.
1954 filtered_codecs.push_back(found_codec);
1955 }
1956 }
1957
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001958 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001959 media_description_options.sender_options, session_options,
1960 filtered_codecs, sdes_policy, GetCryptos(current_content),
1961 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001962 return false;
1963 }
1964
zhihuang1c378ed2017-08-17 14:10:50 -07001965 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001966
1967 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1968 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001969
Steve Anton4e70a722017-11-28 14:57:10 -08001970 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001971
Steve Anton5adfafd2017-12-20 16:34:00 -08001972 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001973 media_description_options.stopped, video.release());
1974 if (!AddTransportOffer(media_description_options.mid,
1975 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001976 current_description, desc)) {
1977 return false;
1978 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001979 return true;
1980}
1981
1982bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001983 const MediaDescriptionOptions& media_description_options,
1984 const MediaSessionOptions& session_options,
1985 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001986 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001987 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001988 StreamParamsVec* current_streams,
1989 SessionDescription* desc) const {
1990 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1991
kwiberg31022942016-03-11 14:18:21 -08001992 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07001993 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
1994 // If the DataChannel type is not specified, use the DataChannel type in
1995 // the current description.
1996 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001997 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
Steve Antonb1c1de12017-12-21 15:14:30 -08001998 is_sctp = (current_content->media_description()->protocol() ==
1999 kMediaProtocolSctp);
zhihuang1c378ed2017-08-17 14:10:50 -07002000 }
deadbeef44f08192015-12-15 16:20:09 -08002001
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002002 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07002003 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2004 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002005 std::vector<std::string> crypto_suites;
2006 if (is_sctp) {
2007 // SDES doesn't make sense for SCTP, so we disable it, and we only
2008 // get SDES crypto suites for RTP-based data channels.
2009 sdes_policy = cricket::SEC_DISABLED;
2010 // Unlike SetMediaProtocol below, we need to set the protocol
2011 // before we call CreateMediaContentOffer. Otherwise,
2012 // CreateMediaContentOffer won't know this is SCTP and will
2013 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 09:38:55 -07002014 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2015 // it's safe to do so. Older versions of webrtc would reject these
2016 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002017 data->set_protocol(
2018 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
2019 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002020 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -07002021 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002022 }
2023
zhihuang1c378ed2017-08-17 14:10:50 -07002024 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002025 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002026 media_description_options.sender_options, session_options,
2027 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2028 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002029 return false;
2030 }
2031
2032 if (is_sctp) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002033 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
zhihuang1c378ed2017-08-17 14:10:50 -07002034 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002035 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002036 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002037 SetMediaProtocol(secure_transport, data.get());
Steve Anton5adfafd2017-12-20 16:34:00 -08002038 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07002039 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002040 }
zhihuang1c378ed2017-08-17 14:10:50 -07002041 if (!AddTransportOffer(media_description_options.mid,
2042 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002043 current_description, desc)) {
2044 return false;
2045 }
2046 return true;
2047}
2048
zhihuang1c378ed2017-08-17 14:10:50 -07002049// |audio_codecs| = set of all possible codecs that can be used, with correct
2050// payload type mappings
2051//
2052// |supported_audio_codecs| = set of codecs that are supported for the direction
2053// of this m= section
2054//
2055// acd->codecs() = set of previously negotiated codecs for this m= section
2056//
2057// The payload types should come from audio_codecs, but the order should come
2058// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2059// change existing codec priority, and that new codecs are added with the right
2060// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002061bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002062 const MediaDescriptionOptions& media_description_options,
2063 const MediaSessionOptions& session_options,
2064 const ContentInfo* offer_content,
2065 const SessionDescription* offer_description,
2066 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002067 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002068 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002069 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002070 StreamParamsVec* current_streams,
2071 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002072 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002073 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002074 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002075
deadbeefb7892532017-02-22 19:35:18 -08002076 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002077 CreateTransportAnswer(media_description_options.mid, offer_description,
2078 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002079 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002080 if (!audio_transport) {
2081 return false;
2082 }
2083
zhihuang1c378ed2017-08-17 14:10:50 -07002084 // Pick codecs based on the requested communications direction in the offer
2085 // and the selected direction in the answer.
2086 // Note these will be filtered one final time in CreateMediaContentAnswer.
2087 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 14:57:10 -08002088 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 03:29:38 -07002089 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002090 AudioCodecs supported_audio_codecs =
2091 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2092
2093 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002094 // Add the codecs from current content if it exists and is not being recycled.
2095 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002096 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002097 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002098 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07002099 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002100 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2101 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07002102 filtered_codecs.push_back(codec);
2103 }
2104 }
2105 }
2106 // Add other supported audio codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002107 for (const AudioCodec& codec : supported_audio_codecs) {
2108 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Zhi Huang6f367472017-11-22 13:20:02 -08002109 codec, nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002110 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2111 codec, nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002112 // We should use the local codec with local parameters and the codec id
2113 // would be correctly mapped in |NegotiateCodecs|.
2114 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002115 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002116 }
2117
zhihuang1c378ed2017-08-17 14:10:50 -07002118 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2119 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002120 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002121 new AudioContentDescription());
2122 // Do not require or create SDES cryptos if DTLS is used.
2123 cricket::SecurePolicy sdes_policy =
2124 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2125 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002126 offer_audio_description, media_description_options, session_options,
2127 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 11:13:34 -07002128 audio_rtp_header_extensions(session_options.is_unified_plan),
2129 enable_encrypted_rtp_header_extensions_, current_streams,
2130 bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002131 return false; // Fails the session setup.
2132 }
2133
deadbeefb7892532017-02-22 19:35:18 -08002134 bool secure = bundle_transport ? bundle_transport->description.secure()
2135 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002136 bool rejected = media_description_options.stopped ||
2137 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002138 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2139 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002140 if (!AddTransportAnswer(media_description_options.mid,
2141 *(audio_transport.get()), answer)) {
2142 return false;
2143 }
2144
2145 if (rejected) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002146 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2147 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002148 }
2149
zhihuang1c378ed2017-08-17 14:10:50 -07002150 answer->AddContent(media_description_options.mid, offer_content->type,
2151 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002152 return true;
2153}
2154
2155bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002156 const MediaDescriptionOptions& media_description_options,
2157 const MediaSessionOptions& session_options,
2158 const ContentInfo* offer_content,
2159 const SessionDescription* offer_description,
2160 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002161 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002162 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002163 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002164 StreamParamsVec* current_streams,
2165 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002166 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002167 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002168 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002169
deadbeefb7892532017-02-22 19:35:18 -08002170 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002171 CreateTransportAnswer(media_description_options.mid, offer_description,
2172 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002173 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002174 if (!video_transport) {
2175 return false;
2176 }
2177
zhihuang1c378ed2017-08-17 14:10:50 -07002178 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002179 // Add the codecs from current content if it exists and is not being recycled.
2180 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002181 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002182 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002183 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002184 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002185 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07002186 nullptr)) {
2187 filtered_codecs.push_back(codec);
2188 }
2189 }
2190 }
2191 // Add other supported video codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002192 for (const VideoCodec& codec : video_codecs_) {
2193 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
Zhi Huang6f367472017-11-22 13:20:02 -08002194 nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002195 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2196 nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002197 // We should use the local codec with local parameters and the codec id
2198 // would be correctly mapped in |NegotiateCodecs|.
2199 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002200 }
2201 }
2202
2203 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2204 session_options.bundle_enabled;
2205
kwiberg31022942016-03-11 14:18:21 -08002206 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002207 new VideoContentDescription());
2208 // Do not require or create SDES cryptos if DTLS is used.
2209 cricket::SecurePolicy sdes_policy =
2210 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002211 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002212 offer_video_description, media_description_options, session_options,
2213 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 11:13:34 -07002214 video_rtp_header_extensions(session_options.is_unified_plan),
2215 enable_encrypted_rtp_header_extensions_, current_streams,
2216 bundle_enabled, video_answer.get())) {
zhihuang1c378ed2017-08-17 14:10:50 -07002217 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002218 }
deadbeefb7892532017-02-22 19:35:18 -08002219 bool secure = bundle_transport ? bundle_transport->description.secure()
2220 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002221 bool rejected = media_description_options.stopped ||
2222 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002223 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2224 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002225 if (!AddTransportAnswer(media_description_options.mid,
2226 *(video_transport.get()), answer)) {
2227 return false;
2228 }
2229
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002230 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002231 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002232 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002233 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2234 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002235 }
zhihuang1c378ed2017-08-17 14:10:50 -07002236 answer->AddContent(media_description_options.mid, offer_content->type,
2237 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002238 return true;
2239}
2240
2241bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002242 const MediaDescriptionOptions& media_description_options,
2243 const MediaSessionOptions& session_options,
2244 const ContentInfo* offer_content,
2245 const SessionDescription* offer_description,
2246 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002248 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002249 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002250 StreamParamsVec* current_streams,
2251 SessionDescription* answer) const {
deadbeefb7892532017-02-22 19:35:18 -08002252 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002253 CreateTransportAnswer(media_description_options.mid, offer_description,
2254 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002255 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002256 if (!data_transport) {
2257 return false;
2258 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002259
kwiberg31022942016-03-11 14:18:21 -08002260 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002261 new DataContentDescription());
2262 // Do not require or create SDES cryptos if DTLS is used.
2263 cricket::SecurePolicy sdes_policy =
2264 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002265 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2266 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002267 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2268 const DataContentDescription* offer_data_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002269 offer_content->media_description()->as_data();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002270 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002271 offer_data_description, media_description_options, session_options,
2272 data_codecs, sdes_policy, GetCryptos(current_content),
2273 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2274 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002275 return false; // Fails the session setup.
2276 }
2277
zstein4b2e0822017-02-17 19:48:38 -08002278 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-17 19:48:38 -08002279 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2280 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2281
deadbeefb7892532017-02-22 19:35:18 -08002282 bool secure = bundle_transport ? bundle_transport->description.secure()
2283 : data_transport->secure();
2284
zhihuang1c378ed2017-08-17 14:10:50 -07002285 bool rejected = session_options.data_channel_type == DCT_NONE ||
2286 media_description_options.stopped ||
2287 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002288 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2289 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002290 if (!AddTransportAnswer(media_description_options.mid,
2291 *(data_transport.get()), answer)) {
2292 return false;
2293 }
2294
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002295 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002296 data_answer->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002297 } else {
2298 // RFC 3264
2299 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002300 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002301 }
zhihuang1c378ed2017-08-17 14:10:50 -07002302 answer->AddContent(media_description_options.mid, offer_content->type,
2303 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002304 return true;
2305}
2306
zhihuang1c378ed2017-08-17 14:10:50 -07002307void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2308 audio_sendrecv_codecs_.clear();
2309 all_audio_codecs_.clear();
2310 // Compute the audio codecs union.
2311 for (const AudioCodec& send : audio_send_codecs_) {
2312 all_audio_codecs_.push_back(send);
2313 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2314 send, nullptr)) {
2315 // It doesn't make sense to have an RTX codec we support sending but not
2316 // receiving.
2317 RTC_DCHECK(!IsRtxCodec(send));
2318 }
2319 }
2320 for (const AudioCodec& recv : audio_recv_codecs_) {
2321 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2322 recv, nullptr)) {
2323 all_audio_codecs_.push_back(recv);
2324 }
2325 }
2326 // Use NegotiateCodecs to merge our codec lists, since the operation is
2327 // essentially the same. Put send_codecs as the offered_codecs, which is the
2328 // order we'd like to follow. The reasoning is that encoding is usually more
2329 // expensive than decoding, and prioritizing a codec in the send list probably
2330 // means it's a codec we can handle efficiently.
2331 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2332 &audio_sendrecv_codecs_);
2333}
2334
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002335bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002336 return (content && (content->type == MediaProtocolType::kRtp ||
2337 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002338}
2339
2340bool IsAudioContent(const ContentInfo* content) {
2341 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2342}
2343
2344bool IsVideoContent(const ContentInfo* content) {
2345 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2346}
2347
2348bool IsDataContent(const ContentInfo* content) {
2349 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2350}
2351
deadbeef0ed85b22016-02-23 17:24:52 -08002352const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2353 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002354 for (const ContentInfo& content : contents) {
2355 if (IsMediaContentOfType(&content, media_type)) {
2356 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002357 }
2358 }
deadbeef0ed85b22016-02-23 17:24:52 -08002359 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002360}
2361
2362const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2363 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2364}
2365
2366const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2367 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2368}
2369
2370const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2371 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2372}
2373
Steve Antonad7bffc2018-01-22 10:21:56 -08002374const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2375 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002376 if (sdesc == nullptr) {
2377 return nullptr;
2378 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002379
2380 return GetFirstMediaContent(sdesc->contents(), media_type);
2381}
2382
2383const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2384 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2385}
2386
2387const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2388 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2389}
2390
2391const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2392 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2393}
2394
2395const MediaContentDescription* GetFirstMediaContentDescription(
2396 const SessionDescription* sdesc, MediaType media_type) {
2397 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002398 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002399}
2400
2401const AudioContentDescription* GetFirstAudioContentDescription(
2402 const SessionDescription* sdesc) {
2403 return static_cast<const AudioContentDescription*>(
2404 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2405}
2406
2407const VideoContentDescription* GetFirstVideoContentDescription(
2408 const SessionDescription* sdesc) {
2409 return static_cast<const VideoContentDescription*>(
2410 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2411}
2412
2413const DataContentDescription* GetFirstDataContentDescription(
2414 const SessionDescription* sdesc) {
2415 return static_cast<const DataContentDescription*>(
2416 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2417}
2418
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002419//
2420// Non-const versions of the above functions.
2421//
2422
Steve Anton36b29d12017-10-30 09:57:42 -07002423ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002424 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002425 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002426 if (IsMediaContentOfType(&content, media_type)) {
2427 return &content;
2428 }
2429 }
2430 return nullptr;
2431}
2432
Steve Anton36b29d12017-10-30 09:57:42 -07002433ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002434 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2435}
2436
Steve Anton36b29d12017-10-30 09:57:42 -07002437ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002438 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2439}
2440
Steve Anton36b29d12017-10-30 09:57:42 -07002441ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002442 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2443}
2444
Steve Antonad7bffc2018-01-22 10:21:56 -08002445ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2446 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002447 if (sdesc == nullptr) {
2448 return nullptr;
2449 }
2450
Steve Anton36b29d12017-10-30 09:57:42 -07002451 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002452}
2453
2454ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2455 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2456}
2457
2458ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2459 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2460}
2461
2462ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2463 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2464}
2465
2466MediaContentDescription* GetFirstMediaContentDescription(
2467 SessionDescription* sdesc,
2468 MediaType media_type) {
2469 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002470 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002471}
2472
2473AudioContentDescription* GetFirstAudioContentDescription(
2474 SessionDescription* sdesc) {
2475 return static_cast<AudioContentDescription*>(
2476 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2477}
2478
2479VideoContentDescription* GetFirstVideoContentDescription(
2480 SessionDescription* sdesc) {
2481 return static_cast<VideoContentDescription*>(
2482 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2483}
2484
2485DataContentDescription* GetFirstDataContentDescription(
2486 SessionDescription* sdesc) {
2487 return static_cast<DataContentDescription*>(
2488 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2489}
2490
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002491} // namespace cricket