blob: a2f47dcb27889d9deb641c47bf4a773a5b4c3e60 [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
jbauchcb560652016-08-04 05:20:32 -0700197// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
deadbeef7914b8c2017-04-21 03:23:33 -0700198void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
199 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700200 if (crypto_options.enable_gcm_crypto_suites) {
201 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
202 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
203 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800204 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
205 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206}
207
deadbeef7914b8c2017-04-21 03:23:33 -0700208void GetSupportedAudioSdesCryptoSuiteNames(
209 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800210 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700211 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
212 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213}
214
deadbeef7914b8c2017-04-21 03:23:33 -0700215void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
216 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700217 if (crypto_options.enable_gcm_crypto_suites) {
218 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
219 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
220 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800221 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222}
223
deadbeef7914b8c2017-04-21 03:23:33 -0700224void GetSupportedVideoSdesCryptoSuiteNames(
225 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800226 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700227 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
228 crypto_options, crypto_suite_names);
229}
230
231void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
232 std::vector<int>* crypto_suites) {
233 if (crypto_options.enable_gcm_crypto_suites) {
234 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
235 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
236 }
237 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
238}
239
240void GetSupportedDataSdesCryptoSuiteNames(
241 const rtc::CryptoOptions& crypto_options,
242 std::vector<std::string>* crypto_suite_names) {
243 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
244 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800245}
246
jbauchcb560652016-08-04 05:20:32 -0700247// Support any GCM cipher (if enabled through options). For video support only
248// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
249// because it is low overhead.
250// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251static bool SelectCrypto(const MediaContentDescription* offer,
252 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700253 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 CryptoParams *crypto) {
255 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
256 const CryptoParamsVec& cryptos = offer->cryptos();
257
258 for (CryptoParamsVec::const_iterator i = cryptos.begin();
259 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700260 if ((crypto_options.enable_gcm_crypto_suites &&
261 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
262 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700263 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
264 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
266 }
267 }
268 return false;
269}
270
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000271// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000272// The generated values are added to |ssrcs|.
273// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000275 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200276 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000277 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200278 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000280 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000281 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
283 ssrcs->push_back(candidate);
284 }
285}
286
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287// Finds all StreamParams of all media types and attach them to stream_params.
288static void GetCurrentStreamParams(const SessionDescription* sdesc,
289 StreamParamsVec* stream_params) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800290 RTC_DCHECK(stream_params);
291 if (!sdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292 return;
Steve Antonb1c1de12017-12-21 15:14:30 -0800293 }
294 for (const ContentInfo& content : sdesc->contents()) {
295 if (!content.media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 continue;
297 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800298 for (const StreamParams& params : content.media_description()->streams()) {
299 stream_params->push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000300 }
301 }
302}
303
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000304// Filters the data codecs for the data channel type.
305void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
306 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700307 const char* codec_name =
308 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000309 for (std::vector<DataCodec>::iterator iter = codecs->begin();
310 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700311 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000312 iter = codecs->erase(iter);
313 } else {
314 ++iter;
315 }
316 }
317}
318
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319template <typename IdStruct>
320class UsedIds {
321 public:
322 UsedIds(int min_allowed_id, int max_allowed_id)
323 : min_allowed_id_(min_allowed_id),
324 max_allowed_id_(max_allowed_id),
325 next_id_(max_allowed_id) {
326 }
327
328 // Loops through all Id in |ids| and changes its id if it is
329 // already in use by another IdStruct. Call this methods with all Id
330 // in a session description to make sure no duplicate ids exists.
331 // Note that typename Id must be a type of IdStruct.
332 template <typename Id>
333 void FindAndSetIdUsed(std::vector<Id>* ids) {
334 for (typename std::vector<Id>::iterator it = ids->begin();
335 it != ids->end(); ++it) {
336 FindAndSetIdUsed(&*it);
337 }
338 }
339
340 // Finds and sets an unused id if the |idstruct| id is already in use.
341 void FindAndSetIdUsed(IdStruct* idstruct) {
342 const int original_id = idstruct->id;
343 int new_id = idstruct->id;
344
345 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
346 // If the original id is not in range - this is an id that can't be
347 // dynamically changed.
348 return;
349 }
350
351 if (IsIdUsed(original_id)) {
352 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100353 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
354 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000355 idstruct->id = new_id;
356 }
357 SetIdUsed(new_id);
358 }
359
360 private:
361 // Returns the first unused id in reverse order.
362 // This hopefully reduce the risk of more collisions. We want to change the
363 // default ids as little as possible.
364 int FindUnusedId() {
365 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
366 --next_id_;
367 }
nisseede5da42017-01-12 05:15:36 -0800368 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369 return next_id_;
370 }
371
372 bool IsIdUsed(int new_id) {
373 return id_set_.find(new_id) != id_set_.end();
374 }
375
376 void SetIdUsed(int new_id) {
377 id_set_.insert(new_id);
378 }
379
380 const int min_allowed_id_;
381 const int max_allowed_id_;
382 int next_id_;
383 std::set<int> id_set_;
384};
385
386// Helper class used for finding duplicate RTP payload types among audio, video
387// and data codecs. When bundle is used the payload types may not collide.
388class UsedPayloadTypes : public UsedIds<Codec> {
389 public:
390 UsedPayloadTypes()
391 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
392 }
393
394
395 private:
396 static const int kDynamicPayloadTypeMin = 96;
397 static const int kDynamicPayloadTypeMax = 127;
398};
399
400// Helper class used for finding duplicate RTP Header extension ids among
401// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700402class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000403 public:
404 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-25 18:15:09 -0800405 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
406 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000407
408 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000409};
410
zhihuang1c378ed2017-08-17 14:10:50 -0700411// Adds a StreamParams for each SenderOptions in |sender_options| to
412// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000413// |current_params| - All currently known StreamParams of any media type.
414template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700415static bool AddStreamParams(
416 const std::vector<SenderOptions>& sender_options,
417 const std::string& rtcp_cname,
418 StreamParamsVec* current_streams,
419 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700420 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 09:38:55 -0700421 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700422 return true;
423 }
424
Noah Richards2e7a0982015-05-18 14:02:54 -0700425 const bool include_rtx_streams =
426 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000427
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000428
brandtr03d5fb12016-11-22 03:37:59 -0800429 const bool include_flexfec_stream =
430 ContainsFlexfecCodec(content_description->codecs());
431
zhihuang1c378ed2017-08-17 14:10:50 -0700432 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 // groupid is empty for StreamParams generated using
434 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700435 StreamParams* param =
436 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000437 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700438 // This is a new sender.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200439 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 14:10:50 -0700440 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 StreamParams stream_param;
zhihuang1c378ed2017-08-17 14:10:50 -0700442 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000443 // Add the generated ssrc.
444 for (size_t i = 0; i < ssrcs.size(); ++i) {
445 stream_param.ssrcs.push_back(ssrcs[i]);
446 }
zhihuang1c378ed2017-08-17 14:10:50 -0700447 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000448 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
449 stream_param.ssrc_groups.push_back(group);
450 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700451 // Generate extra ssrcs for include_rtx_streams case.
452 if (include_rtx_streams) {
453 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200454 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700455 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
456 &rtx_ssrcs);
457 for (size_t i = 0; i < ssrcs.size(); ++i) {
458 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
459 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000460 }
brandtr03d5fb12016-11-22 03:37:59 -0800461 // Generate extra ssrc for include_flexfec_stream case.
462 if (include_flexfec_stream) {
463 // TODO(brandtr): Update when we support multistream protection.
464 if (ssrcs.size() == 1) {
465 std::vector<uint32_t> flexfec_ssrcs;
466 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
467 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
brandtr03d5fb12016-11-22 03:37:59 -0800468 } else if (!ssrcs.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100469 RTC_LOG(LS_WARNING)
brandtr03d5fb12016-11-22 03:37:59 -0800470 << "Our FlexFEC implementation only supports protecting "
471 << "a single media streams. This session has multiple "
472 << "media streams however, so no FlexFEC SSRC will be generated.";
473 }
474 }
zhihuang1c378ed2017-08-17 14:10:50 -0700475 stream_param.cname = rtcp_cname;
Steve Anton8ffb9c32017-08-31 15:45:38 -0700476 // TODO(steveanton): Support any number of stream ids.
477 RTC_CHECK(sender.stream_ids.size() == 1U);
478 stream_param.sync_label = sender.stream_ids[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000479 content_description->AddStream(stream_param);
480
481 // Store the new StreamParams in current_streams.
482 // This is necessary so that we can use the CNAME for other media types.
483 current_streams->push_back(stream_param);
484 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700485 // Use existing generated SSRCs/groups, but update the sync_label if
486 // necessary. This may be needed if a MediaStreamTrack was moved from one
487 // MediaStream to another.
Steve Anton8ffb9c32017-08-31 15:45:38 -0700488 // TODO(steveanton): Support any number of stream ids.
489 RTC_CHECK(sender.stream_ids.size() == 1U);
490 param->sync_label = sender.stream_ids[0];
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000491 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000492 }
493 }
494 return true;
495}
496
497// Updates the transport infos of the |sdesc| according to the given
498// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800499// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
500// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000501static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
502 SessionDescription* sdesc) {
503 // The bundle should not be empty.
504 if (!sdesc || !bundle_group.FirstContentName()) {
505 return false;
506 }
507
508 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700509 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000510 const TransportInfo* selected_transport_info =
511 sdesc->GetTransportInfoByName(selected_content_name);
512 if (!selected_transport_info) {
513 return false;
514 }
515
516 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700517 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700519 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000520 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800521 ConnectionRole selected_connection_role =
522 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523 for (TransportInfos::iterator it =
524 sdesc->transport_infos().begin();
525 it != sdesc->transport_infos().end(); ++it) {
526 if (bundle_group.HasContentName(it->content_name) &&
527 it->content_name != selected_content_name) {
528 it->description.ice_ufrag = selected_ufrag;
529 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800530 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 }
532 }
533 return true;
534}
535
536// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
537// sets it to |cryptos|.
538static bool GetCryptosByName(const SessionDescription* sdesc,
539 const std::string& content_name,
540 CryptoParamsVec* cryptos) {
541 if (!sdesc || !cryptos) {
542 return false;
543 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000544 const ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800545 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546 return false;
547 }
Steve Antonb1c1de12017-12-21 15:14:30 -0800548 *cryptos = content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549 return true;
550}
551
552// Predicate function used by the remove_if.
553// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
554static bool CryptoNotFound(const CryptoParams crypto,
555 const CryptoParamsVec* filter) {
556 if (filter == NULL) {
557 return true;
558 }
559 for (CryptoParamsVec::const_iterator it = filter->begin();
560 it != filter->end(); ++it) {
561 if (it->cipher_suite == crypto.cipher_suite) {
562 return false;
563 }
564 }
565 return true;
566}
567
568// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
569// which are not available in |filter|.
570static void PruneCryptos(const CryptoParamsVec& filter,
571 CryptoParamsVec* target_cryptos) {
572 if (!target_cryptos) {
573 return;
574 }
575 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
576 target_cryptos->end(),
577 bind2nd(ptr_fun(CryptoNotFound),
578 &filter)),
579 target_cryptos->end());
580}
581
Steve Antonfa2260d2017-12-28 16:38:23 -0800582bool IsRtpProtocol(const std::string& protocol) {
deadbeefb5cb19b2015-11-23 16:39:12 -0800583 return protocol.empty() ||
584 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
585}
586
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000587static bool IsRtpContent(SessionDescription* sdesc,
588 const std::string& content_name) {
589 bool is_rtp = false;
590 ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 15:14:30 -0800591 if (content && content->media_description()) {
592 is_rtp = IsRtpProtocol(content->media_description()->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 }
594 return is_rtp;
595}
596
597// Updates the crypto parameters of the |sdesc| according to the given
598// |bundle_group|. The crypto parameters of all the contents within the
599// |bundle_group| should be updated to use the common subset of the
600// available cryptos.
601static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
602 SessionDescription* sdesc) {
603 // The bundle should not be empty.
604 if (!sdesc || !bundle_group.FirstContentName()) {
605 return false;
606 }
607
wu@webrtc.org78187522013-10-07 23:32:02 +0000608 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000609 // Get the common cryptos.
610 const ContentNames& content_names = bundle_group.content_names();
611 CryptoParamsVec common_cryptos;
612 for (ContentNames::const_iterator it = content_names.begin();
613 it != content_names.end(); ++it) {
614 if (!IsRtpContent(sdesc, *it)) {
615 continue;
616 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000617 // The common cryptos are needed if any of the content does not have DTLS
618 // enabled.
619 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
620 common_cryptos_needed = true;
621 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000622 if (it == content_names.begin()) {
623 // Initial the common_cryptos with the first content in the bundle group.
624 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
625 return false;
626 }
627 if (common_cryptos.empty()) {
628 // If there's no crypto params, we should just return.
629 return true;
630 }
631 } else {
632 CryptoParamsVec cryptos;
633 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
634 return false;
635 }
636 PruneCryptos(cryptos, &common_cryptos);
637 }
638 }
639
wu@webrtc.org78187522013-10-07 23:32:02 +0000640 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000641 return false;
642 }
643
644 // Update to use the common cryptos.
645 for (ContentNames::const_iterator it = content_names.begin();
646 it != content_names.end(); ++it) {
647 if (!IsRtpContent(sdesc, *it)) {
648 continue;
649 }
650 ContentInfo* content = sdesc->GetContentByName(*it);
651 if (IsMediaContent(content)) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800652 MediaContentDescription* media_desc = content->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000653 if (!media_desc) {
654 return false;
655 }
656 media_desc->set_cryptos(common_cryptos);
657 }
658 }
659 return true;
660}
661
662template <class C>
663static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800664 for (const auto& codec : codecs) {
665 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000666 return true;
667 }
668 }
669 return false;
670}
671
672template <class C>
673static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800674 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675}
676
brandtr03d5fb12016-11-22 03:37:59 -0800677template <class C>
678static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
679 for (const auto& codec : codecs) {
680 if (IsFlexfecCodec(codec)) {
681 return true;
682 }
683 }
684 return false;
685}
686
687template <class C>
688static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800689 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800690}
691
zhihuang1c378ed2017-08-17 14:10:50 -0700692// Create a media content to be offered for the given |sender_options|,
693// according to the given options.rtcp_mux, session_options.is_muc, codecs,
694// secure_transport, crypto, and current_streams. If we don't currently have
695// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
696// created (according to crypto_suites). The created content is added to the
697// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000698template <class C>
699static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -0700700 const std::vector<SenderOptions>& sender_options,
701 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000702 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000703 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704 const CryptoParamsVec* current_cryptos,
705 const std::vector<std::string>& crypto_suites,
706 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000707 StreamParamsVec* current_streams,
708 MediaContentDescriptionImpl<C>* offer) {
709 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000710
zhihuang1c378ed2017-08-17 14:10:50 -0700711 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700712 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
713 offer->set_rtcp_reduced_size(true);
714 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715 offer->set_rtp_header_extensions(rtp_extensions);
716
zhihuang1c378ed2017-08-17 14:10:50 -0700717 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
718 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000719 return false;
720 }
721
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 if (secure_policy != SEC_DISABLED) {
723 if (current_cryptos) {
724 AddMediaCryptos(*current_cryptos, offer);
725 }
726 if (offer->cryptos().empty()) {
727 if (!CreateMediaCryptos(crypto_suites, offer)) {
728 return false;
729 }
730 }
731 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000732
deadbeef7af91dd2016-12-13 11:29:11 -0800733 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734 return false;
735 }
736 return true;
737}
738
739template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000740static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800741 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000742 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800743 const int codec2_id) {
744 const C* codec1 = FindCodecById(codecs1, codec1_id);
745 const C* codec2 = FindCodecById(codecs2, codec2_id);
746 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000747}
748
749template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750static void NegotiateCodecs(const std::vector<C>& local_codecs,
751 const std::vector<C>& offered_codecs,
752 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800753 for (const C& ours : local_codecs) {
754 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700755 // Note that we intentionally only find one matching codec for each of our
756 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800757 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
758 C negotiated = ours;
759 negotiated.IntersectFeedbackParams(theirs);
760 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800761 const auto apt_it =
762 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800763 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800764 RTC_DCHECK(apt_it != theirs.params.end());
765 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000766 }
magjedf823ede2016-11-12 09:53:04 -0800767 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
768 webrtc::H264::GenerateProfileLevelIdForAnswer(
769 ours.params, theirs.params, &negotiated.params);
770 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800771 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700772 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800773 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000774 }
775 }
deadbeef67cf2c12016-04-13 10:07:16 -0700776 // RFC3264: Although the answerer MAY list the formats in their desired
777 // order of preference, it is RECOMMENDED that unless there is a
778 // specific reason, the answerer list formats in the same relative order
779 // they were present in the offer.
780 std::unordered_map<int, int> payload_type_preferences;
781 int preference = static_cast<int>(offered_codecs.size() + 1);
782 for (const C& codec : offered_codecs) {
783 payload_type_preferences[codec.id] = preference--;
784 }
785 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
786 [&payload_type_preferences](const C& a, const C& b) {
787 return payload_type_preferences[a.id] >
788 payload_type_preferences[b.id];
789 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000790}
791
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800792// Finds a codec in |codecs2| that matches |codec_to_match|, which is
793// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
794// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000795template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800796static bool FindMatchingCodec(const std::vector<C>& codecs1,
797 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798 const C& codec_to_match,
799 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700800 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
801 // codecs' associated codecs correctly. If not, that's a programming error.
802 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
803 [&codec_to_match](const C& codec) {
804 return &codec == &codec_to_match;
805 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800806 for (const C& potential_match : codecs2) {
807 if (potential_match.Matches(codec_to_match)) {
808 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800809 int apt_value_1 = 0;
810 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800811 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
812 &apt_value_1) ||
813 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
814 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100815 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800816 continue;
817 }
818 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
819 apt_value_2)) {
820 continue;
821 }
822 }
823 if (found_codec) {
824 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 }
826 return true;
827 }
828 }
829 return false;
830}
831
zhihuang1c378ed2017-08-17 14:10:50 -0700832// Find the codec in |codec_list| that |rtx_codec| is associated with.
833template <class C>
834static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
835 const C& rtx_codec) {
836 std::string associated_pt_str;
837 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
838 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100839 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
840 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 14:10:50 -0700841 return nullptr;
842 }
843
844 int associated_pt;
845 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100846 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
847 << " of RTX codec " << rtx_codec.name
848 << " to an integer.";
zhihuang1c378ed2017-08-17 14:10:50 -0700849 return nullptr;
850 }
851
852 // Find the associated reference codec for the reference RTX codec.
853 const C* associated_codec = FindCodecById(codec_list, associated_pt);
854 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100855 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
856 << associated_pt << " for RTX codec " << rtx_codec.name
857 << ".";
zhihuang1c378ed2017-08-17 14:10:50 -0700858 }
859 return associated_codec;
860}
861
862// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000863// already exist in |offered_codecs| and ensure the payload types don't
864// collide.
865template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700866static void MergeCodecs(const std::vector<C>& reference_codecs,
867 std::vector<C>* offered_codecs,
868 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800870 for (const C& reference_codec : reference_codecs) {
871 if (!IsRtxCodec(reference_codec) &&
872 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
873 reference_codec, nullptr)) {
874 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000875 used_pltypes->FindAndSetIdUsed(&codec);
876 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000877 }
878 }
879
880 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800881 for (const C& reference_codec : reference_codecs) {
882 if (IsRtxCodec(reference_codec) &&
883 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
884 reference_codec, nullptr)) {
885 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700886 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700887 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700888 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700889 continue;
890 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800891 // Find a codec in the offered list that matches the reference codec.
892 // Its payload type may be different than the reference codec.
893 C matching_codec;
894 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800895 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100896 RTC_LOG(LS_WARNING)
897 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800898 continue;
899 }
900
901 rtx_codec.params[kCodecParamAssociatedPayloadType] =
902 rtc::ToString(matching_codec.id);
903 used_pltypes->FindAndSetIdUsed(&rtx_codec);
904 offered_codecs->push_back(rtx_codec);
905 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000906 }
907}
908
zhihuang1c378ed2017-08-17 14:10:50 -0700909static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
910 const webrtc::RtpExtension& ext_to_match,
911 webrtc::RtpExtension* found_extension) {
912 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
913 it != extensions.end(); ++it) {
914 // We assume that all URIs are given in a canonical format.
915 if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
916 if (found_extension) {
917 *found_extension = *it;
918 }
919 return true;
920 }
921 }
922 return false;
923}
924
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000925static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700926 const webrtc::RtpExtension& ext_to_match,
927 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -0700928 // We assume that all URIs are given in a canonical format.
929 const webrtc::RtpExtension* found =
930 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
931 ext_to_match.uri);
932 if (!found) {
933 return false;
934 }
935 if (found_extension) {
936 *found_extension = *found;
937 }
938 return true;
939}
940
941static bool FindByUriWithEncryptionPreference(
942 const RtpHeaderExtensions& extensions,
943 const webrtc::RtpExtension& ext_to_match, bool encryption_preference,
944 webrtc::RtpExtension* found_extension) {
945 const webrtc::RtpExtension* unencrypted_extension = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000946 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
947 it != extensions.end(); ++it) {
948 // We assume that all URIs are given in a canonical format.
949 if (it->uri == ext_to_match.uri) {
jbauch5869f502017-06-29 12:31:36 -0700950 if (!encryption_preference || it->encrypt) {
951 if (found_extension) {
952 *found_extension = *it;
953 }
954 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000955 }
jbauch5869f502017-06-29 12:31:36 -0700956 unencrypted_extension = &(*it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000957 }
958 }
jbauch5869f502017-06-29 12:31:36 -0700959 if (unencrypted_extension) {
960 if (found_extension) {
961 *found_extension = *unencrypted_extension;
962 }
963 return true;
964 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000965 return false;
966}
967
zhihuang1c378ed2017-08-17 14:10:50 -0700968// Adds all extensions from |reference_extensions| to |offered_extensions| that
969// don't already exist in |offered_extensions| and ensure the IDs don't
970// collide. If an extension is added, it's also added to |regular_extensions| or
971// |encrypted_extensions|, and if the extension is in |regular_extensions| or
972// |encrypted_extensions|, its ID is marked as used in |used_ids|.
973// |offered_extensions| is for either audio or video while |regular_extensions|
974// and |encrypted_extensions| are used for both audio and video. There could be
975// overlap between audio extensions and video extensions.
976static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
977 RtpHeaderExtensions* offered_extensions,
978 RtpHeaderExtensions* regular_extensions,
979 RtpHeaderExtensions* encrypted_extensions,
980 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -0700981 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -0700982 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
983 nullptr)) {
olka3c747662017-08-17 06:50:32 -0700984 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -0700985 if (reference_extension.encrypt) {
986 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
987 &existing)) {
988 offered_extensions->push_back(existing);
989 } else {
990 used_ids->FindAndSetIdUsed(&reference_extension);
991 encrypted_extensions->push_back(reference_extension);
992 offered_extensions->push_back(reference_extension);
993 }
olka3c747662017-08-17 06:50:32 -0700994 } else {
zhihuang1c378ed2017-08-17 14:10:50 -0700995 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
996 &existing)) {
997 offered_extensions->push_back(existing);
998 } else {
999 used_ids->FindAndSetIdUsed(&reference_extension);
1000 regular_extensions->push_back(reference_extension);
1001 offered_extensions->push_back(reference_extension);
1002 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001003 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001004 }
1005 }
1006}
1007
jbauch5869f502017-06-29 12:31:36 -07001008static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1009 RtpHeaderExtensions* all_extensions,
1010 UsedRtpHeaderExtensionIds* used_ids) {
1011 RtpHeaderExtensions encrypted_extensions;
1012 for (const webrtc::RtpExtension& extension : *extensions) {
1013 webrtc::RtpExtension existing;
1014 // Don't add encrypted extensions again that were already included in a
1015 // previous offer or regular extensions that are also included as encrypted
1016 // extensions.
1017 if (extension.encrypt ||
1018 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1019 (FindByUriWithEncryptionPreference(*extensions, extension, true,
1020 &existing) && existing.encrypt)) {
1021 continue;
1022 }
1023
1024 if (FindByUri(*all_extensions, extension, &existing)) {
1025 encrypted_extensions.push_back(existing);
1026 } else {
1027 webrtc::RtpExtension encrypted(extension);
1028 encrypted.encrypt = true;
1029 used_ids->FindAndSetIdUsed(&encrypted);
1030 all_extensions->push_back(encrypted);
1031 encrypted_extensions.push_back(encrypted);
1032 }
1033 }
1034 extensions->insert(extensions->end(), encrypted_extensions.begin(),
1035 encrypted_extensions.end());
1036}
1037
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038static void NegotiateRtpHeaderExtensions(
1039 const RtpHeaderExtensions& local_extensions,
1040 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001041 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001042 RtpHeaderExtensions* negotiated_extenstions) {
1043 RtpHeaderExtensions::const_iterator ours;
1044 for (ours = local_extensions.begin();
1045 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001046 webrtc::RtpExtension theirs;
jbauch5869f502017-06-29 12:31:36 -07001047 if (FindByUriWithEncryptionPreference(offered_extensions, *ours,
1048 enable_encrypted_rtp_header_extensions, &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001049 // We respond with their RTP header extension id.
1050 negotiated_extenstions->push_back(theirs);
1051 }
1052 }
1053}
1054
1055static void StripCNCodecs(AudioCodecs* audio_codecs) {
1056 AudioCodecs::iterator iter = audio_codecs->begin();
1057 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001058 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001059 iter = audio_codecs->erase(iter);
1060 } else {
1061 ++iter;
1062 }
1063 }
1064}
1065
zhihuang1c378ed2017-08-17 14:10:50 -07001066// Create a media content to be answered for the given |sender_options|
1067// according to the given session_options.rtcp_mux, session_options.streams,
1068// codecs, crypto, and current_streams. If we don't currently have crypto (in
1069// current_cryptos) and it is enabled (in secure_policy), crypto is created
1070// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1071// negotiated with the offer. If the negotiation fails, this method returns
1072// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001073template <class C>
1074static bool CreateMediaContentAnswer(
1075 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001076 const MediaDescriptionOptions& media_description_options,
1077 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001078 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001079 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001080 const CryptoParamsVec* current_cryptos,
1081 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 12:31:36 -07001082 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001083 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001084 bool bundle_enabled,
1085 MediaContentDescriptionImpl<C>* answer) {
1086 std::vector<C> negotiated_codecs;
1087 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1088 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001089 answer->set_protocol(offer->protocol());
1090 RtpHeaderExtensions negotiated_rtp_extensions;
1091 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1092 offer->rtp_header_extensions(),
jbauch5869f502017-06-29 12:31:36 -07001093 enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094 &negotiated_rtp_extensions);
1095 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1096
zhihuang1c378ed2017-08-17 14:10:50 -07001097 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001098 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1099 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1100 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001101
1102 if (sdes_policy != SEC_DISABLED) {
1103 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001104 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1105 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001106 if (current_cryptos) {
1107 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1108 }
1109 answer->AddCrypto(crypto);
1110 }
1111 }
1112
deadbeef7af91dd2016-12-13 11:29:11 -08001113 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001114 return false;
1115 }
1116
zhihuang1c378ed2017-08-17 14:10:50 -07001117 if (!AddStreamParams(media_description_options.sender_options,
1118 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001119 return false; // Something went seriously wrong.
1120 }
1121
Steve Anton4e70a722017-11-28 14:57:10 -08001122 answer->set_direction(NegotiateRtpTransceiverDirection(
1123 offer->direction(), media_description_options.direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001124 return true;
1125}
1126
1127static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001128 const std::string& protocol,
1129 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001130 // Since not all applications serialize and deserialize the media protocol,
1131 // we will have to accept |protocol| to be empty.
1132 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001133 return true;
1134 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001135
zhihuangcf5b37c2016-05-05 11:44:35 -07001136 if (type == MEDIA_TYPE_DATA) {
1137 // Check for SCTP, but also for RTP for RTP-based data channels.
1138 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1139 if (secure_transport) {
1140 // Most likely scenarios first.
1141 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1142 IsPlainRtp(protocol);
1143 } else {
1144 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1145 }
1146 }
1147
1148 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1149 // JSEP specifies.
1150 if (secure_transport) {
1151 // Most likely scenarios first.
1152 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1153 } else {
1154 return IsPlainRtp(protocol);
1155 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001156}
1157
1158static void SetMediaProtocol(bool secure_transport,
1159 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001160 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001161 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001162 else if (secure_transport)
1163 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001164 else
1165 desc->set_protocol(kMediaProtocolAvpf);
1166}
1167
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001168// Gets the TransportInfo of the given |content_name| from the
1169// |current_description|. If doesn't exist, returns a new one.
1170static const TransportDescription* GetTransportDescription(
1171 const std::string& content_name,
1172 const SessionDescription* current_description) {
1173 const TransportDescription* desc = NULL;
1174 if (current_description) {
1175 const TransportInfo* info =
1176 current_description->GetTransportInfoByName(content_name);
1177 if (info) {
1178 desc = &info->description;
1179 }
1180 }
1181 return desc;
1182}
1183
1184// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001185static bool IsDtlsActive(const ContentInfo* content,
1186 const SessionDescription* current_description) {
1187 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001188 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001189 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001190
zhihuang1c378ed2017-08-17 14:10:50 -07001191 size_t msection_index = content - &current_description->contents()[0];
1192
1193 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001194 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001195 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001196
zhihuang1c378ed2017-08-17 14:10:50 -07001197 return current_description->transport_infos()[msection_index]
1198 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001199}
1200
Steve Anton8ffb9c32017-08-31 15:45:38 -07001201void MediaDescriptionOptions::AddAudioSender(
1202 const std::string& track_id,
1203 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001204 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001205 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001206}
1207
Steve Anton8ffb9c32017-08-31 15:45:38 -07001208void MediaDescriptionOptions::AddVideoSender(
1209 const std::string& track_id,
1210 const std::vector<std::string>& stream_ids,
1211 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001212 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001213 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001214}
1215
zhihuang1c378ed2017-08-17 14:10:50 -07001216void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1217 const std::string& stream_id) {
1218 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001219 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1220 // than one stream?
1221 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001222}
1223
Steve Anton8ffb9c32017-08-31 15:45:38 -07001224void MediaDescriptionOptions::AddSenderInternal(
1225 const std::string& track_id,
1226 const std::vector<std::string>& stream_ids,
1227 int num_sim_layers) {
1228 // TODO(steveanton): Support any number of stream ids.
1229 RTC_CHECK(stream_ids.size() == 1U);
1230 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001231}
1232
zhihuang1c378ed2017-08-17 14:10:50 -07001233bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1234 return std::find_if(media_description_options.begin(),
1235 media_description_options.end(),
1236 [type](const MediaDescriptionOptions& t) {
1237 return t.type == type;
1238 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001239}
1240
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001241MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1242 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001243 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001244
1245MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1246 ChannelManager* channel_manager,
1247 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001248 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001249 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1250 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001251 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001252 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001253 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1254 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001255 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001256}
1257
ossudedfd282016-06-14 07:12:39 -07001258const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1259 const {
ossu075af922016-06-14 03:29:38 -07001260 return audio_sendrecv_codecs_;
1261}
1262
1263const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1264 return audio_send_codecs_;
1265}
1266
1267const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1268 return audio_recv_codecs_;
1269}
1270
1271void MediaSessionDescriptionFactory::set_audio_codecs(
1272 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1273 audio_send_codecs_ = send_codecs;
1274 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001275 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001276}
1277
1278SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001279 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001280 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001281 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001282
1283 StreamParamsVec current_streams;
1284 GetCurrentStreamParams(current_description, &current_streams);
1285
zhihuang1c378ed2017-08-17 14:10:50 -07001286 AudioCodecs offer_audio_codecs;
1287 VideoCodecs offer_video_codecs;
1288 DataCodecs offer_data_codecs;
1289 GetCodecsForOffer(current_description, &offer_audio_codecs,
1290 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001291
zhihuang1c378ed2017-08-17 14:10:50 -07001292 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001294 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001295 }
zhihuang1c378ed2017-08-17 14:10:50 -07001296 FilterDataCodecs(&offer_data_codecs,
1297 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001298
1299 RtpHeaderExtensions audio_rtp_extensions;
1300 RtpHeaderExtensions video_rtp_extensions;
1301 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1302 &video_rtp_extensions);
1303
zhihuang1c378ed2017-08-17 14:10:50 -07001304 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001305 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001306 RTC_DCHECK(current_description->contents().size() <=
1307 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001308 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001309
zhihuang1c378ed2017-08-17 14:10:50 -07001310 // Iterate through the media description options, matching with existing media
1311 // descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001312 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001313 for (const MediaDescriptionOptions& media_description_options :
1314 session_options.media_description_options) {
1315 const ContentInfo* current_content = nullptr;
1316 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001317 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001318 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-22 16:02:54 -08001319 // Media type must match unless this media section is being recycled.
1320 RTC_DCHECK(current_content->rejected ||
1321 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 14:10:50 -07001322 media_description_options.type));
1323 }
1324 switch (media_description_options.type) {
1325 case MEDIA_TYPE_AUDIO:
1326 if (!AddAudioContentForOffer(media_description_options, session_options,
1327 current_content, current_description,
1328 audio_rtp_extensions, offer_audio_codecs,
1329 &current_streams, offer.get())) {
1330 return nullptr;
1331 }
1332 break;
1333 case MEDIA_TYPE_VIDEO:
1334 if (!AddVideoContentForOffer(media_description_options, session_options,
1335 current_content, current_description,
1336 video_rtp_extensions, offer_video_codecs,
1337 &current_streams, offer.get())) {
1338 return nullptr;
1339 }
1340 break;
1341 case MEDIA_TYPE_DATA:
1342 if (!AddDataContentForOffer(media_description_options, session_options,
1343 current_content, current_description,
1344 offer_data_codecs, &current_streams,
1345 offer.get())) {
1346 return nullptr;
1347 }
1348 break;
1349 default:
1350 RTC_NOTREACHED();
1351 }
1352 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001353 }
1354
1355 // Bundle the contents together, if we've been asked to do so, and update any
1356 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 14:10:50 -07001357 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001358 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001359 for (const ContentInfo& content : offer->contents()) {
1360 // TODO(deadbeef): There are conditions that make bundling two media
1361 // descriptions together illegal. For example, they use the same payload
1362 // type to represent different codecs, or same IDs for different header
1363 // extensions. We need to detect this and not try to bundle those media
1364 // descriptions together.
1365 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001366 }
1367 offer->AddGroup(offer_bundle);
1368 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001369 RTC_LOG(LS_ERROR)
1370 << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001371 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001372 }
1373 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001374 RTC_LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001375 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001376 }
1377 }
Steve Antone831b8c2018-02-01 12:22:16 -08001378
1379 // The following determines how to signal MSIDs to ensure compatibility with
1380 // older endpoints (in particular, older Plan B endpoints).
1381 if (session_options.is_unified_plan) {
1382 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1383 // Plan answerers will look at a=msid and Plan B answerers will look at the
1384 // a=ssrc MSID line.
1385 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1386 cricket::kMsidSignalingSsrcAttribute);
1387 } else {
1388 // Plan B always signals MSID using a=ssrc lines.
1389 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1390 }
1391
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001392 return offer.release();
1393}
1394
1395SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001396 const SessionDescription* offer,
1397 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001399 if (!offer) {
1400 return nullptr;
1401 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001402 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001403 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1404 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001405 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001406
1407 StreamParamsVec current_streams;
1408 GetCurrentStreamParams(current_description, &current_streams);
1409
deadbeefb7892532017-02-22 19:35:18 -08001410 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1411 // group in the answer with the appropriate content names.
1412 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1413 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1414 // Transport info shared by the bundle group.
1415 std::unique_ptr<TransportInfo> bundle_transport;
1416
zhihuang1c378ed2017-08-17 14:10:50 -07001417 // Get list of all possible codecs that respects existing payload type
1418 // mappings and uses a single payload type space.
1419 //
1420 // Note that these lists may be further filtered for each m= section; this
1421 // step is done just to establish the payload type mappings shared by all
1422 // sections.
1423 AudioCodecs answer_audio_codecs;
1424 VideoCodecs answer_video_codecs;
1425 DataCodecs answer_data_codecs;
1426 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1427 &answer_video_codecs, &answer_data_codecs);
1428
1429 if (!session_options.vad_enabled) {
1430 // If application doesn't want CN codecs in answer.
1431 StripCNCodecs(&answer_audio_codecs);
1432 }
1433 FilterDataCodecs(&answer_data_codecs,
1434 session_options.data_channel_type == DCT_SCTP);
1435
1436 // Must have options for exactly as many sections as in the offer.
1437 RTC_DCHECK(offer->contents().size() ==
1438 session_options.media_description_options.size());
1439 // Iterate through the media description options, matching with existing
1440 // media descriptions in |current_description|.
Steve Antondcc3c022017-12-22 16:02:54 -08001441 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 14:10:50 -07001442 for (const MediaDescriptionOptions& media_description_options :
1443 session_options.media_description_options) {
1444 const ContentInfo* offer_content = &offer->contents()[msection_index];
1445 // Media types and MIDs must match between the remote offer and the
1446 // MediaDescriptionOptions.
1447 RTC_DCHECK(
1448 IsMediaContentOfType(offer_content, media_description_options.type));
1449 RTC_DCHECK(media_description_options.mid == offer_content->name);
1450 const ContentInfo* current_content = nullptr;
1451 if (current_description &&
Steve Antondcc3c022017-12-22 16:02:54 -08001452 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 14:10:50 -07001453 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001454 }
zhihuang1c378ed2017-08-17 14:10:50 -07001455 switch (media_description_options.type) {
1456 case MEDIA_TYPE_AUDIO:
1457 if (!AddAudioContentForAnswer(
1458 media_description_options, session_options, offer_content,
1459 offer, current_content, current_description,
1460 bundle_transport.get(), answer_audio_codecs, &current_streams,
1461 answer.get())) {
1462 return nullptr;
1463 }
1464 break;
1465 case MEDIA_TYPE_VIDEO:
1466 if (!AddVideoContentForAnswer(
1467 media_description_options, session_options, offer_content,
1468 offer, current_content, current_description,
1469 bundle_transport.get(), answer_video_codecs, &current_streams,
1470 answer.get())) {
1471 return nullptr;
1472 }
1473 break;
1474 case MEDIA_TYPE_DATA:
1475 if (!AddDataContentForAnswer(media_description_options, session_options,
1476 offer_content, offer, current_content,
1477 current_description,
1478 bundle_transport.get(), answer_data_codecs,
1479 &current_streams, answer.get())) {
1480 return nullptr;
1481 }
1482 break;
1483 default:
1484 RTC_NOTREACHED();
1485 }
1486 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001487 // See if we can add the newly generated m= section to the BUNDLE group in
1488 // the answer.
1489 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001490 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001491 offer_bundle->HasContentName(added.name)) {
1492 answer_bundle.AddContentName(added.name);
1493 bundle_transport.reset(
1494 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001495 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001496 }
1497
deadbeefb7892532017-02-22 19:35:18 -08001498 // Only put BUNDLE group in answer if nonempty.
1499 if (answer_bundle.FirstContentName()) {
1500 answer->AddGroup(answer_bundle);
1501
1502 // Share the same ICE credentials and crypto params across all contents,
1503 // as BUNDLE requires.
1504 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001505 RTC_LOG(LS_ERROR)
1506 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001507 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001508 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001509
deadbeefb7892532017-02-22 19:35:18 -08001510 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001511 RTC_LOG(LS_ERROR)
1512 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001513 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001514 }
1515 }
1516
Steve Antone831b8c2018-02-01 12:22:16 -08001517 // The following determines how to signal MSIDs to ensure compatibility with
1518 // older endpoints (in particular, older Plan B endpoints).
1519 if (session_options.is_unified_plan) {
1520 // Unified Plan needs to look at what the offer included to find the most
1521 // compatible answer.
1522 if (offer->msid_signaling() == 0) {
1523 // We end up here in one of three cases:
1524 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1525 // matter what we pick here.
1526 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1527 // it also doesn't matter what we pick here.
1528 // 3. Media that's either sendonly or inactive from the remote endpoint.
1529 // We don't have any information to say whether the endpoint is Plan B
1530 // or Unified Plan, so be conservative and send both.
1531 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1532 cricket::kMsidSignalingSsrcAttribute);
1533 } else if (offer->msid_signaling() ==
1534 (cricket::kMsidSignalingMediaSection |
1535 cricket::kMsidSignalingSsrcAttribute)) {
1536 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1537 // probably talking to a Unified Plan endpoint so respond with just
1538 // a=msid.
1539 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1540 } else {
1541 // Otherwise, it's clear which method the offerer is using so repeat that
1542 // back to them.
1543 answer->set_msid_signaling(offer->msid_signaling());
1544 }
1545 } else {
1546 // Plan B always signals MSID using a=ssrc lines.
1547 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1548 }
1549
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001550 return answer.release();
1551}
1552
ossu075af922016-06-14 03:29:38 -07001553const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1554 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001555 switch (direction) {
1556 // If stream is inactive - generate list as if sendrecv.
1557 case RtpTransceiverDirection::kSendRecv:
1558 case RtpTransceiverDirection::kInactive:
1559 return audio_sendrecv_codecs_;
1560 case RtpTransceiverDirection::kSendOnly:
1561 return audio_send_codecs_;
1562 case RtpTransceiverDirection::kRecvOnly:
1563 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001564 }
Steve Anton1d03a752017-11-27 14:30:09 -08001565 RTC_NOTREACHED();
1566 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001567}
1568
1569const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1570 const RtpTransceiverDirection& offer,
1571 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001572 switch (answer) {
1573 // For inactive and sendrecv answers, generate lists as if we were to accept
1574 // the offer's direction. See RFC 3264 Section 6.1.
1575 case RtpTransceiverDirection::kSendRecv:
1576 case RtpTransceiverDirection::kInactive:
1577 return GetAudioCodecsForOffer(
1578 webrtc::RtpTransceiverDirectionReversed(offer));
1579 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 03:29:38 -07001580 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 14:30:09 -08001581 case RtpTransceiverDirection::kRecvOnly:
1582 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001583 }
Steve Anton1d03a752017-11-27 14:30:09 -08001584 RTC_NOTREACHED();
1585 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001586}
1587
zhihuang1c378ed2017-08-17 14:10:50 -07001588void MergeCodecsFromDescription(const SessionDescription* description,
1589 AudioCodecs* audio_codecs,
1590 VideoCodecs* video_codecs,
1591 DataCodecs* data_codecs,
1592 UsedPayloadTypes* used_pltypes) {
1593 RTC_DCHECK(description);
1594 for (const ContentInfo& content : description->contents()) {
1595 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1596 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001597 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001598 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1599 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1600 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001601 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001602 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1603 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1604 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001605 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001606 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1607 }
1608 }
1609}
1610
1611// Getting codecs for an offer involves these steps:
1612//
1613// 1. Construct payload type -> codec mappings for current description.
1614// 2. Add any reference codecs that weren't already present
1615// 3. For each individual media description (m= section), filter codecs based
1616// on the directional attribute (happens in another method).
1617void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001618 const SessionDescription* current_description,
1619 AudioCodecs* audio_codecs,
1620 VideoCodecs* video_codecs,
1621 DataCodecs* data_codecs) const {
1622 UsedPayloadTypes used_pltypes;
1623 audio_codecs->clear();
1624 video_codecs->clear();
1625 data_codecs->clear();
1626
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001627 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001628 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1629 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001630 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001631 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1632 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001633 }
1634
1635 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001636 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1637 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1638 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1639}
1640
1641// Getting codecs for an answer involves these steps:
1642//
1643// 1. Construct payload type -> codec mappings for current description.
1644// 2. Add any codecs from the offer that weren't already present.
1645// 3. Add any remaining codecs that weren't already present.
1646// 4. For each individual media description (m= section), filter codecs based
1647// on the directional attribute (happens in another method).
1648void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1649 const SessionDescription* current_description,
1650 const SessionDescription* remote_offer,
1651 AudioCodecs* audio_codecs,
1652 VideoCodecs* video_codecs,
1653 DataCodecs* data_codecs) const {
1654 UsedPayloadTypes used_pltypes;
1655 audio_codecs->clear();
1656 video_codecs->clear();
1657 data_codecs->clear();
1658
1659 // First - get all codecs from the current description if the media type
1660 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1661 // new media type is added.
1662 if (current_description) {
1663 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1664 data_codecs, &used_pltypes);
1665 }
1666
1667 // Second - filter out codecs that we don't support at all and should ignore.
1668 AudioCodecs filtered_offered_audio_codecs;
1669 VideoCodecs filtered_offered_video_codecs;
1670 DataCodecs filtered_offered_data_codecs;
1671 for (const ContentInfo& content : remote_offer->contents()) {
1672 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1673 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001674 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001675 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1676 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1677 filtered_offered_audio_codecs,
1678 offered_audio_codec, nullptr) &&
1679 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1680 offered_audio_codec, nullptr)) {
1681 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1682 }
1683 }
1684 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1685 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001686 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001687 for (const VideoCodec& offered_video_codec : video->codecs()) {
1688 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1689 filtered_offered_video_codecs,
1690 offered_video_codec, nullptr) &&
1691 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1692 offered_video_codec, nullptr)) {
1693 filtered_offered_video_codecs.push_back(offered_video_codec);
1694 }
1695 }
1696 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1697 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 15:14:30 -08001698 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 14:10:50 -07001699 for (const DataCodec& offered_data_codec : data->codecs()) {
1700 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1701 filtered_offered_data_codecs,
1702 offered_data_codec, nullptr) &&
1703 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1704 offered_data_codec, nullptr)) {
1705 filtered_offered_data_codecs.push_back(offered_data_codec);
1706 }
1707 }
1708 }
1709 }
1710
1711 // Add codecs that are not in |current_description| but were in
1712 // |remote_offer|.
1713 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1714 &used_pltypes);
1715 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1716 &used_pltypes);
1717 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1718 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001719}
1720
1721void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1722 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001723 RtpHeaderExtensions* offer_audio_extensions,
1724 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001725 // All header extensions allocated from the same range to avoid potential
1726 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001727 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001728 RtpHeaderExtensions all_regular_extensions;
1729 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 14:10:50 -07001730 offer_audio_extensions->clear();
1731 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001732
1733 // First - get all extensions from the current description if the media type
1734 // is used.
1735 // Add them to |used_ids| so the local ids are not reused if a new media
1736 // type is added.
1737 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001738 for (const ContentInfo& content : current_description->contents()) {
1739 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1740 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 15:14:30 -08001741 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001742 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1743 &all_regular_extensions, &all_encrypted_extensions,
1744 &used_ids);
1745 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1746 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 15:14:30 -08001747 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001748 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1749 &all_regular_extensions, &all_encrypted_extensions,
1750 &used_ids);
1751 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001752 }
1753 }
1754
1755 // Add our default RTP header extensions that are not in
1756 // |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001757 MergeRtpHdrExts(audio_rtp_header_extensions(), offer_audio_extensions,
1758 &all_regular_extensions, &all_encrypted_extensions,
1759 &used_ids);
1760 MergeRtpHdrExts(video_rtp_header_extensions(), offer_video_extensions,
1761 &all_regular_extensions, &all_encrypted_extensions,
1762 &used_ids);
1763
jbauch5869f502017-06-29 12:31:36 -07001764 // TODO(jbauch): Support adding encrypted header extensions to existing
1765 // sessions.
1766 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001767 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1768 &all_encrypted_extensions, &used_ids);
1769 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1770 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001771 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001772}
1773
1774bool MediaSessionDescriptionFactory::AddTransportOffer(
1775 const std::string& content_name,
1776 const TransportOptions& transport_options,
1777 const SessionDescription* current_desc,
1778 SessionDescription* offer_desc) const {
1779 if (!transport_desc_factory_)
1780 return false;
1781 const TransportDescription* current_tdesc =
1782 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001783 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001784 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1785 bool ret = (new_tdesc.get() != NULL &&
1786 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1787 if (!ret) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001788 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
1789 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001790 }
1791 return ret;
1792}
1793
1794TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1795 const std::string& content_name,
1796 const SessionDescription* offer_desc,
1797 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08001798 const SessionDescription* current_desc,
1799 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001800 if (!transport_desc_factory_)
1801 return NULL;
1802 const TransportDescription* offer_tdesc =
1803 GetTransportDescription(content_name, offer_desc);
1804 const TransportDescription* current_tdesc =
1805 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08001806 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1807 require_transport_attributes,
1808 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001809}
1810
1811bool MediaSessionDescriptionFactory::AddTransportAnswer(
1812 const std::string& content_name,
1813 const TransportDescription& transport_desc,
1814 SessionDescription* answer_desc) const {
1815 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1816 transport_desc))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001817 RTC_LOG(LS_ERROR) << "Failed to AddTransportAnswer, content name="
1818 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001819 return false;
1820 }
1821 return true;
1822}
1823
zhihuang1c378ed2017-08-17 14:10:50 -07001824// |audio_codecs| = set of all possible codecs that can be used, with correct
1825// payload type mappings
1826//
1827// |supported_audio_codecs| = set of codecs that are supported for the direction
1828// of this m= section
1829//
1830// acd->codecs() = set of previously negotiated codecs for this m= section
1831//
1832// The payload types should come from audio_codecs, but the order should come
1833// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1834// change existing codec priority, and that new codecs are added with the right
1835// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001836bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001837 const MediaDescriptionOptions& media_description_options,
1838 const MediaSessionOptions& session_options,
1839 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001840 const SessionDescription* current_description,
1841 const RtpHeaderExtensions& audio_rtp_extensions,
1842 const AudioCodecs& audio_codecs,
1843 StreamParamsVec* current_streams,
1844 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001845 // Filter audio_codecs (which includes all codecs, with correctly remapped
1846 // payload types) based on transceiver direction.
1847 const AudioCodecs& supported_audio_codecs =
1848 GetAudioCodecsForOffer(media_description_options.direction);
1849
1850 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001851 // Add the codecs from current content if it exists and is not being recycled.
1852 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001853 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07001854 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001855 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07001856 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001857 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1858 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001859 filtered_codecs.push_back(codec);
1860 }
1861 }
1862 }
1863 // Add other supported audio codecs.
1864 AudioCodec found_codec;
1865 for (const AudioCodec& codec : supported_audio_codecs) {
1866 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1867 codec, &found_codec) &&
1868 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1869 codec, nullptr)) {
1870 // Use the |found_codec| from |audio_codecs| because it has the correctly
1871 // mapped payload type.
1872 filtered_codecs.push_back(found_codec);
1873 }
1874 }
deadbeef44f08192015-12-15 16:20:09 -08001875
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001876 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001877 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1878 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001879
kwiberg31022942016-03-11 14:18:21 -08001880 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001881 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001882 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1883 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001884 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001885 media_description_options.sender_options, session_options,
1886 filtered_codecs, sdes_policy, GetCryptos(current_content),
1887 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001888 return false;
1889 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001890
1891 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1892 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001893
Steve Anton4e70a722017-11-28 14:57:10 -08001894 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001895
Steve Anton5adfafd2017-12-20 16:34:00 -08001896 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001897 media_description_options.stopped, audio.release());
1898 if (!AddTransportOffer(media_description_options.mid,
1899 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001900 current_description, desc)) {
1901 return false;
1902 }
1903
1904 return true;
1905}
1906
1907bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001908 const MediaDescriptionOptions& media_description_options,
1909 const MediaSessionOptions& session_options,
1910 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001911 const SessionDescription* current_description,
1912 const RtpHeaderExtensions& video_rtp_extensions,
1913 const VideoCodecs& video_codecs,
1914 StreamParamsVec* current_streams,
1915 SessionDescription* desc) const {
1916 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001917 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1918 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001919
kwiberg31022942016-03-11 14:18:21 -08001920 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001921 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001922 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1923 &crypto_suites);
1924
1925 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08001926 // Add the codecs from current content if it exists and is not being recycled.
1927 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001928 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07001929 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08001930 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07001931 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001932 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07001933 nullptr)) {
1934 filtered_codecs.push_back(codec);
1935 }
1936 }
1937 }
1938 // Add other supported video codecs.
1939 VideoCodec found_codec;
1940 for (const VideoCodec& codec : video_codecs_) {
1941 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1942 &found_codec) &&
1943 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1944 nullptr)) {
1945 // Use the |found_codec| from |video_codecs| because it has the correctly
1946 // mapped payload type.
1947 filtered_codecs.push_back(found_codec);
1948 }
1949 }
1950
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001951 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001952 media_description_options.sender_options, session_options,
1953 filtered_codecs, sdes_policy, GetCryptos(current_content),
1954 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001955 return false;
1956 }
1957
zhihuang1c378ed2017-08-17 14:10:50 -07001958 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001959
1960 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1961 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001962
Steve Anton4e70a722017-11-28 14:57:10 -08001963 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001964
Steve Anton5adfafd2017-12-20 16:34:00 -08001965 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07001966 media_description_options.stopped, video.release());
1967 if (!AddTransportOffer(media_description_options.mid,
1968 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001969 current_description, desc)) {
1970 return false;
1971 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001972 return true;
1973}
1974
1975bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001976 const MediaDescriptionOptions& media_description_options,
1977 const MediaSessionOptions& session_options,
1978 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001979 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001980 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001981 StreamParamsVec* current_streams,
1982 SessionDescription* desc) const {
1983 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1984
kwiberg31022942016-03-11 14:18:21 -08001985 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07001986 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
1987 // If the DataChannel type is not specified, use the DataChannel type in
1988 // the current description.
1989 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001990 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
Steve Antonb1c1de12017-12-21 15:14:30 -08001991 is_sctp = (current_content->media_description()->protocol() ==
1992 kMediaProtocolSctp);
zhihuang1c378ed2017-08-17 14:10:50 -07001993 }
deadbeef44f08192015-12-15 16:20:09 -08001994
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001995 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001996 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1997 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001998 std::vector<std::string> crypto_suites;
1999 if (is_sctp) {
2000 // SDES doesn't make sense for SCTP, so we disable it, and we only
2001 // get SDES crypto suites for RTP-based data channels.
2002 sdes_policy = cricket::SEC_DISABLED;
2003 // Unlike SetMediaProtocol below, we need to set the protocol
2004 // before we call CreateMediaContentOffer. Otherwise,
2005 // CreateMediaContentOffer won't know this is SCTP and will
2006 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 09:38:55 -07002007 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2008 // it's safe to do so. Older versions of webrtc would reject these
2009 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002010 data->set_protocol(
2011 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
2012 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002013 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -07002014 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002015 }
2016
zhihuang1c378ed2017-08-17 14:10:50 -07002017 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002018 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002019 media_description_options.sender_options, session_options,
2020 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2021 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002022 return false;
2023 }
2024
2025 if (is_sctp) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002026 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
zhihuang1c378ed2017-08-17 14:10:50 -07002027 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002028 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002029 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002030 SetMediaProtocol(secure_transport, data.get());
Steve Anton5adfafd2017-12-20 16:34:00 -08002031 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 14:10:50 -07002032 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002033 }
zhihuang1c378ed2017-08-17 14:10:50 -07002034 if (!AddTransportOffer(media_description_options.mid,
2035 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002036 current_description, desc)) {
2037 return false;
2038 }
2039 return true;
2040}
2041
zhihuang1c378ed2017-08-17 14:10:50 -07002042// |audio_codecs| = set of all possible codecs that can be used, with correct
2043// payload type mappings
2044//
2045// |supported_audio_codecs| = set of codecs that are supported for the direction
2046// of this m= section
2047//
2048// acd->codecs() = set of previously negotiated codecs for this m= section
2049//
2050// The payload types should come from audio_codecs, but the order should come
2051// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2052// change existing codec priority, and that new codecs are added with the right
2053// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002054bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002055 const MediaDescriptionOptions& media_description_options,
2056 const MediaSessionOptions& session_options,
2057 const ContentInfo* offer_content,
2058 const SessionDescription* offer_description,
2059 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002060 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002061 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002062 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002063 StreamParamsVec* current_streams,
2064 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002065 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002066 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002067 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002068
deadbeefb7892532017-02-22 19:35:18 -08002069 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002070 CreateTransportAnswer(media_description_options.mid, offer_description,
2071 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002072 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002073 if (!audio_transport) {
2074 return false;
2075 }
2076
zhihuang1c378ed2017-08-17 14:10:50 -07002077 // Pick codecs based on the requested communications direction in the offer
2078 // and the selected direction in the answer.
2079 // Note these will be filtered one final time in CreateMediaContentAnswer.
2080 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 14:57:10 -08002081 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 03:29:38 -07002082 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002083 AudioCodecs supported_audio_codecs =
2084 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2085
2086 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002087 // Add the codecs from current content if it exists and is not being recycled.
2088 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002089 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002090 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002091 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 14:10:50 -07002092 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002093 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2094 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07002095 filtered_codecs.push_back(codec);
2096 }
2097 }
2098 }
2099 // Add other supported audio codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002100 for (const AudioCodec& codec : supported_audio_codecs) {
2101 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Zhi Huang6f367472017-11-22 13:20:02 -08002102 codec, nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002103 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2104 codec, nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002105 // We should use the local codec with local parameters and the codec id
2106 // would be correctly mapped in |NegotiateCodecs|.
2107 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002108 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002109 }
2110
zhihuang1c378ed2017-08-17 14:10:50 -07002111 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2112 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002113 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002114 new AudioContentDescription());
2115 // Do not require or create SDES cryptos if DTLS is used.
2116 cricket::SecurePolicy sdes_policy =
2117 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2118 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002119 offer_audio_description, media_description_options, session_options,
2120 filtered_codecs, sdes_policy, GetCryptos(current_content),
2121 audio_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2122 current_streams, bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002123 return false; // Fails the session setup.
2124 }
2125
deadbeefb7892532017-02-22 19:35:18 -08002126 bool secure = bundle_transport ? bundle_transport->description.secure()
2127 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002128 bool rejected = media_description_options.stopped ||
2129 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002130 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2131 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002132 if (!AddTransportAnswer(media_description_options.mid,
2133 *(audio_transport.get()), answer)) {
2134 return false;
2135 }
2136
2137 if (rejected) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002138 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2139 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002140 }
2141
zhihuang1c378ed2017-08-17 14:10:50 -07002142 answer->AddContent(media_description_options.mid, offer_content->type,
2143 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002144 return true;
2145}
2146
2147bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002148 const MediaDescriptionOptions& media_description_options,
2149 const MediaSessionOptions& session_options,
2150 const ContentInfo* offer_content,
2151 const SessionDescription* offer_description,
2152 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002153 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002154 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002155 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002156 StreamParamsVec* current_streams,
2157 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002158 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002159 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002160 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002161
deadbeefb7892532017-02-22 19:35:18 -08002162 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002163 CreateTransportAnswer(media_description_options.mid, offer_description,
2164 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002165 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002166 if (!video_transport) {
2167 return false;
2168 }
2169
zhihuang1c378ed2017-08-17 14:10:50 -07002170 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-22 16:02:54 -08002171 // Add the codecs from current content if it exists and is not being recycled.
2172 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002173 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002174 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 15:14:30 -08002175 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 14:10:50 -07002176 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002177 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07002178 nullptr)) {
2179 filtered_codecs.push_back(codec);
2180 }
2181 }
2182 }
2183 // Add other supported video codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002184 for (const VideoCodec& codec : video_codecs_) {
2185 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
Zhi Huang6f367472017-11-22 13:20:02 -08002186 nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002187 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2188 nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002189 // We should use the local codec with local parameters and the codec id
2190 // would be correctly mapped in |NegotiateCodecs|.
2191 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002192 }
2193 }
2194
2195 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2196 session_options.bundle_enabled;
2197
kwiberg31022942016-03-11 14:18:21 -08002198 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002199 new VideoContentDescription());
2200 // Do not require or create SDES cryptos if DTLS is used.
2201 cricket::SecurePolicy sdes_policy =
2202 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002203 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002204 offer_video_description, media_description_options, session_options,
2205 filtered_codecs, sdes_policy, GetCryptos(current_content),
2206 video_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2207 current_streams, bundle_enabled, video_answer.get())) {
2208 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002209 }
deadbeefb7892532017-02-22 19:35:18 -08002210 bool secure = bundle_transport ? bundle_transport->description.secure()
2211 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002212 bool rejected = media_description_options.stopped ||
2213 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002214 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2215 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002216 if (!AddTransportAnswer(media_description_options.mid,
2217 *(video_transport.get()), answer)) {
2218 return false;
2219 }
2220
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002221 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002222 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002223 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002224 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2225 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002226 }
zhihuang1c378ed2017-08-17 14:10:50 -07002227 answer->AddContent(media_description_options.mid, offer_content->type,
2228 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002229 return true;
2230}
2231
2232bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002233 const MediaDescriptionOptions& media_description_options,
2234 const MediaSessionOptions& session_options,
2235 const ContentInfo* offer_content,
2236 const SessionDescription* offer_description,
2237 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002238 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002239 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002240 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002241 StreamParamsVec* current_streams,
2242 SessionDescription* answer) const {
deadbeefb7892532017-02-22 19:35:18 -08002243 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002244 CreateTransportAnswer(media_description_options.mid, offer_description,
2245 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002246 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247 if (!data_transport) {
2248 return false;
2249 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002250
kwiberg31022942016-03-11 14:18:21 -08002251 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002252 new DataContentDescription());
2253 // Do not require or create SDES cryptos if DTLS is used.
2254 cricket::SecurePolicy sdes_policy =
2255 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002256 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2257 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002258 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2259 const DataContentDescription* offer_data_description =
Steve Antonb1c1de12017-12-21 15:14:30 -08002260 offer_content->media_description()->as_data();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002261 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002262 offer_data_description, media_description_options, session_options,
2263 data_codecs, sdes_policy, GetCryptos(current_content),
2264 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2265 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002266 return false; // Fails the session setup.
2267 }
2268
zstein4b2e0822017-02-17 19:48:38 -08002269 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-17 19:48:38 -08002270 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2271 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2272
deadbeefb7892532017-02-22 19:35:18 -08002273 bool secure = bundle_transport ? bundle_transport->description.secure()
2274 : data_transport->secure();
2275
zhihuang1c378ed2017-08-17 14:10:50 -07002276 bool rejected = session_options.data_channel_type == DCT_NONE ||
2277 media_description_options.stopped ||
2278 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002279 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2280 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 13:20:35 -08002281 if (!AddTransportAnswer(media_description_options.mid,
2282 *(data_transport.get()), answer)) {
2283 return false;
2284 }
2285
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002286 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002287 data_answer->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002288 } else {
2289 // RFC 3264
2290 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002291 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002292 }
zhihuang1c378ed2017-08-17 14:10:50 -07002293 answer->AddContent(media_description_options.mid, offer_content->type,
2294 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002295 return true;
2296}
2297
zhihuang1c378ed2017-08-17 14:10:50 -07002298void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2299 audio_sendrecv_codecs_.clear();
2300 all_audio_codecs_.clear();
2301 // Compute the audio codecs union.
2302 for (const AudioCodec& send : audio_send_codecs_) {
2303 all_audio_codecs_.push_back(send);
2304 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2305 send, nullptr)) {
2306 // It doesn't make sense to have an RTX codec we support sending but not
2307 // receiving.
2308 RTC_DCHECK(!IsRtxCodec(send));
2309 }
2310 }
2311 for (const AudioCodec& recv : audio_recv_codecs_) {
2312 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2313 recv, nullptr)) {
2314 all_audio_codecs_.push_back(recv);
2315 }
2316 }
2317 // Use NegotiateCodecs to merge our codec lists, since the operation is
2318 // essentially the same. Put send_codecs as the offered_codecs, which is the
2319 // order we'd like to follow. The reasoning is that encoding is usually more
2320 // expensive than decoding, and prioritizing a codec in the send list probably
2321 // means it's a codec we can handle efficiently.
2322 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2323 &audio_sendrecv_codecs_);
2324}
2325
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002326bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-20 16:34:00 -08002327 return (content && (content->type == MediaProtocolType::kRtp ||
2328 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002329}
2330
2331bool IsAudioContent(const ContentInfo* content) {
2332 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2333}
2334
2335bool IsVideoContent(const ContentInfo* content) {
2336 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2337}
2338
2339bool IsDataContent(const ContentInfo* content) {
2340 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2341}
2342
deadbeef0ed85b22016-02-23 17:24:52 -08002343const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2344 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002345 for (const ContentInfo& content : contents) {
2346 if (IsMediaContentOfType(&content, media_type)) {
2347 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002348 }
2349 }
deadbeef0ed85b22016-02-23 17:24:52 -08002350 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002351}
2352
2353const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2354 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2355}
2356
2357const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2358 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2359}
2360
2361const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2362 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2363}
2364
Steve Antonad7bffc2018-01-22 10:21:56 -08002365const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2366 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002367 if (sdesc == nullptr) {
2368 return nullptr;
2369 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002370
2371 return GetFirstMediaContent(sdesc->contents(), media_type);
2372}
2373
2374const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2375 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2376}
2377
2378const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2379 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2380}
2381
2382const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2383 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2384}
2385
2386const MediaContentDescription* GetFirstMediaContentDescription(
2387 const SessionDescription* sdesc, MediaType media_type) {
2388 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002389 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002390}
2391
2392const AudioContentDescription* GetFirstAudioContentDescription(
2393 const SessionDescription* sdesc) {
2394 return static_cast<const AudioContentDescription*>(
2395 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2396}
2397
2398const VideoContentDescription* GetFirstVideoContentDescription(
2399 const SessionDescription* sdesc) {
2400 return static_cast<const VideoContentDescription*>(
2401 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2402}
2403
2404const DataContentDescription* GetFirstDataContentDescription(
2405 const SessionDescription* sdesc) {
2406 return static_cast<const DataContentDescription*>(
2407 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2408}
2409
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002410//
2411// Non-const versions of the above functions.
2412//
2413
Steve Anton36b29d12017-10-30 09:57:42 -07002414ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002415 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002416 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002417 if (IsMediaContentOfType(&content, media_type)) {
2418 return &content;
2419 }
2420 }
2421 return nullptr;
2422}
2423
Steve Anton36b29d12017-10-30 09:57:42 -07002424ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002425 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2426}
2427
Steve Anton36b29d12017-10-30 09:57:42 -07002428ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002429 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2430}
2431
Steve Anton36b29d12017-10-30 09:57:42 -07002432ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002433 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2434}
2435
Steve Antonad7bffc2018-01-22 10:21:56 -08002436ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2437 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002438 if (sdesc == nullptr) {
2439 return nullptr;
2440 }
2441
Steve Anton36b29d12017-10-30 09:57:42 -07002442 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002443}
2444
2445ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2446 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2447}
2448
2449ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2450 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2451}
2452
2453ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2454 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2455}
2456
2457MediaContentDescription* GetFirstMediaContentDescription(
2458 SessionDescription* sdesc,
2459 MediaType media_type) {
2460 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 15:14:30 -08002461 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002462}
2463
2464AudioContentDescription* GetFirstAudioContentDescription(
2465 SessionDescription* sdesc) {
2466 return static_cast<AudioContentDescription*>(
2467 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2468}
2469
2470VideoContentDescription* GetFirstVideoContentDescription(
2471 SessionDescription* sdesc) {
2472 return static_cast<VideoContentDescription*>(
2473 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2474}
2475
2476DataContentDescription* GetFirstDataContentDescription(
2477 SessionDescription* sdesc) {
2478 return static_cast<DataContentDescription*>(
2479 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2480}
2481
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002482} // namespace cricket