blob: 29438321c22cd5e1b3b775c1300568ca304d6411 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellander65c7f672016-02-12 08:05:012 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellander65c7f672016-02-12 08:05:014 * 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:369 */
10
Mirko Bonadei92ea95e2017-09-15 04:47:3111#include "pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
deadbeef67cf2c12016-04-13 17:07:1613#include <algorithm> // For std::find_if, std::sort.
henrike@webrtc.org28e20752013-07-10 00:45:3614#include <functional>
15#include <map>
kwiberg31022942016-03-11 22:18:2116#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:3617#include <set>
deadbeef67cf2c12016-04-13 17:07:1618#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:3619#include <utility>
20
Danil Chapovalov66cadcc2018-06-19 14:47:4321#include "absl/types/optional.h"
Patrik Höglund7aee3d52017-11-15 12:15:1722#include "api/cryptoparams.h"
Mirko Bonadei71207422017-09-15 11:58:0923#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 04:47:3124#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 22:30:0928#include "pc/rtpmediautils.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3129#include "pc/srtpfilter.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3130#include "rtc_base/checks.h"
31#include "rtc_base/helpers.h"
32#include "rtc_base/logging.h"
33#include "rtc_base/stringutils.h"
Artem Titova76af0c2018-07-23 15:38:1234#include "rtc_base/third_party/base64/base64.h"
henrike@webrtc.org28e20752013-07-10 00:45:3635
36namespace {
Steve Anton1d03a752017-11-27 22:30:0937
38using webrtc::RtpTransceiverDirection;
39
henrike@webrtc.org28e20752013-07-10 00:45:3640const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-19 03:41:5341
deadbeef7914b8c2017-04-21 10:23:3342void 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-19 03:41:5346 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 12:20:3247 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-19 03:41:5348 for (const auto crypto : crypto_suites) {
49 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
50 }
Guo-wei Shieh521ed7b2015-11-19 03:41:5351}
terelius8c011e52016-04-26 12:28:1152} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:3653
54namespace cricket {
55
henrike@webrtc.org28e20752013-07-10 00:45:3656// 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:5961const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
62
deadbeeff3938292015-07-15 19:20:5363// 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:3665const 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:4871const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:2172const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:3673
deadbeef8b7e9ad2017-05-25 16:38:5574// 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 22:30:09104static 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 10:29:38113}
114
henrike@webrtc.org28e20752013-07-10 00:45:36115static bool IsMediaContentOfType(const ContentInfo* content,
116 MediaType media_type) {
Steve Antonb1c1de12017-12-21 23:14:30117 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36118 return false;
119 }
Steve Antonb1c1de12017-12-21 23:14:30120 return content->media_description()->type() == media_type;
henrike@webrtc.org28e20752013-07-10 00:45:36121}
122
Yves Gerey665174f2018-06-19 13:03:05123static bool CreateCryptoParams(int tag,
124 const std::string& cipher,
Steve Anton3a66edf2018-09-10 19:57:37125 CryptoParams* crypto_out) {
jbauchcb560652016-08-04 12:20:32126 int key_len;
127 int salt_len;
Yves Gerey665174f2018-06-19 13:03:05128 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
129 &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36130 return false;
131 }
jbauchcb560652016-08-04 12:20:32132
133 int master_key_len = key_len + salt_len;
134 std::string master_key;
135 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
136 return false;
137 }
138
kwiberg352444f2016-11-28 23:58:53139 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 12:20:32140 std::string key = rtc::Base64::Encode(master_key);
141
Steve Anton3a66edf2018-09-10 19:57:37142 crypto_out->tag = tag;
143 crypto_out->cipher_suite = cipher;
144 crypto_out->key_params = kInline;
145 crypto_out->key_params += key;
henrike@webrtc.org28e20752013-07-10 00:45:36146 return true;
147}
148
henrike@webrtc.org28e20752013-07-10 00:45:36149static bool AddCryptoParams(const std::string& cipher_suite,
Steve Anton3a66edf2018-09-10 19:57:37150 CryptoParamsVec* cryptos_out) {
151 int size = static_cast<int>(cryptos_out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36152
Steve Anton3a66edf2018-09-10 19:57:37153 cryptos_out->resize(size + 1);
154 return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
henrike@webrtc.org28e20752013-07-10 00:45:36155}
156
157void AddMediaCryptos(const CryptoParamsVec& cryptos,
158 MediaContentDescription* media) {
Steve Anton3a66edf2018-09-10 19:57:37159 for (const CryptoParams& crypto : cryptos) {
160 media->AddCrypto(crypto);
henrike@webrtc.org28e20752013-07-10 00:45:36161 }
162}
163
164bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
165 MediaContentDescription* media) {
166 CryptoParamsVec cryptos;
Steve Anton3a66edf2018-09-10 19:57:37167 for (const std::string& crypto_suite : crypto_suites) {
168 if (!AddCryptoParams(crypto_suite, &cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36169 return false;
170 }
171 }
172 AddMediaCryptos(cryptos, media);
173 return true;
174}
henrike@webrtc.org28e20752013-07-10 00:45:36175
zhihuang1c378ed2017-08-17 21:10:50176const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 23:14:30177 if (!content || !content->media_description()) {
zhihuang1c378ed2017-08-17 21:10:50178 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36179 }
Steve Antonb1c1de12017-12-21 23:14:30180 return &content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36181}
182
183bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
184 const CryptoParams& crypto,
Steve Anton3a66edf2018-09-10 19:57:37185 CryptoParams* crypto_out) {
186 auto it = std::find_if(
187 cryptos.begin(), cryptos.end(),
188 [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
189 if (it == cryptos.end()) {
190 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36191 }
Steve Anton3a66edf2018-09-10 19:57:37192 *crypto_out = *it;
193 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36194}
195
Taylor Brandstetter5e55fe82018-03-23 18:50:16196// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
197// low overhead.
deadbeef7914b8c2017-04-21 10:23:33198void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
199 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 12:20:32200 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 }
Taylor Brandstetter5e55fe82018-03-23 18:50:16204 if (crypto_options.enable_aes128_sha1_32_crypto_cipher) {
205 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
206 }
Guo-wei Shieh521ed7b2015-11-19 03:41:53207 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36208}
209
deadbeef7914b8c2017-04-21 10:23:33210void GetSupportedAudioSdesCryptoSuiteNames(
211 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-19 03:41:53212 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 10:23:33213 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
214 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36215}
216
deadbeef7914b8c2017-04-21 10:23:33217void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
218 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 12:20:32219 if (crypto_options.enable_gcm_crypto_suites) {
220 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
221 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
222 }
Guo-wei Shieh521ed7b2015-11-19 03:41:53223 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36224}
225
deadbeef7914b8c2017-04-21 10:23:33226void GetSupportedVideoSdesCryptoSuiteNames(
227 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-19 03:41:53228 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 10:23:33229 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
230 crypto_options, crypto_suite_names);
231}
232
233void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
234 std::vector<int>* crypto_suites) {
235 if (crypto_options.enable_gcm_crypto_suites) {
236 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
237 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
238 }
239 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
240}
241
242void GetSupportedDataSdesCryptoSuiteNames(
243 const rtc::CryptoOptions& crypto_options,
244 std::vector<std::string>* crypto_suite_names) {
245 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
246 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-19 03:41:53247}
248
jbauchcb560652016-08-04 12:20:32249// Support any GCM cipher (if enabled through options). For video support only
Taylor Brandstetter5e55fe82018-03-23 18:50:16250// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
251// bundle is enabled because it is low overhead.
jbauchcb560652016-08-04 12:20:32252// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36253static bool SelectCrypto(const MediaContentDescription* offer,
254 bool bundle,
jbauchcb560652016-08-04 12:20:32255 const rtc::CryptoOptions& crypto_options,
Steve Anton3a66edf2018-09-10 19:57:37256 CryptoParams* crypto_out) {
henrike@webrtc.org28e20752013-07-10 00:45:36257 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
258 const CryptoParamsVec& cryptos = offer->cryptos();
259
Steve Anton3a66edf2018-09-10 19:57:37260 for (const CryptoParams& crypto : cryptos) {
jbauchcb560652016-08-04 12:20:32261 if ((crypto_options.enable_gcm_crypto_suites &&
Steve Anton3a66edf2018-09-10 19:57:37262 rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
263 rtc::CS_AES_CM_128_HMAC_SHA1_80 == crypto.cipher_suite ||
264 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == crypto.cipher_suite && audio &&
Taylor Brandstetter5e55fe82018-03-23 18:50:16265 !bundle && crypto_options.enable_aes128_sha1_32_crypto_cipher)) {
Steve Anton3a66edf2018-09-10 19:57:37266 return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
henrike@webrtc.org28e20752013-07-10 00:45:36267 }
268 }
269 return false;
270}
271
henrike@webrtc.org28e20752013-07-10 00:45:36272// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12273// The generated values are added to |ssrcs|.
274// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36275static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12276 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 10:23:21277 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12278 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 10:23:21279 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36280 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52281 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41282 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36283 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
284 ssrcs->push_back(candidate);
285 }
286}
287
henrike@webrtc.org28e20752013-07-10 00:45:36288// Finds all StreamParams of all media types and attach them to stream_params.
289static void GetCurrentStreamParams(const SessionDescription* sdesc,
290 StreamParamsVec* stream_params) {
Steve Antonb1c1de12017-12-21 23:14:30291 RTC_DCHECK(stream_params);
292 if (!sdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36293 return;
Steve Antonb1c1de12017-12-21 23:14:30294 }
295 for (const ContentInfo& content : sdesc->contents()) {
296 if (!content.media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36297 continue;
298 }
Steve Antonb1c1de12017-12-21 23:14:30299 for (const StreamParams& params : content.media_description()->streams()) {
300 stream_params->push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36301 }
302 }
303}
304
jiayl@webrtc.org9c16c392014-05-01 18:30:30305// Filters the data codecs for the data channel type.
306void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
307 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 20:02:44308 const char* codec_name =
309 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
Steve Anton3a66edf2018-09-10 19:57:37310 codecs->erase(std::remove_if(codecs->begin(), codecs->end(),
311 [&codec_name](const DataCodec& codec) {
312 return CodecNamesEq(codec.name, codec_name);
313 }),
314 codecs->end());
jiayl@webrtc.org9c16c392014-05-01 18:30:30315}
316
henrike@webrtc.org28e20752013-07-10 00:45:36317template <typename IdStruct>
318class UsedIds {
319 public:
320 UsedIds(int min_allowed_id, int max_allowed_id)
321 : min_allowed_id_(min_allowed_id),
322 max_allowed_id_(max_allowed_id),
Yves Gerey665174f2018-06-19 13:03:05323 next_id_(max_allowed_id) {}
henrike@webrtc.org28e20752013-07-10 00:45:36324
325 // Loops through all Id in |ids| and changes its id if it is
326 // already in use by another IdStruct. Call this methods with all Id
327 // in a session description to make sure no duplicate ids exists.
328 // Note that typename Id must be a type of IdStruct.
329 template <typename Id>
330 void FindAndSetIdUsed(std::vector<Id>* ids) {
Steve Anton3a66edf2018-09-10 19:57:37331 for (const Id& id : *ids) {
332 FindAndSetIdUsed(&id);
henrike@webrtc.org28e20752013-07-10 00:45:36333 }
334 }
335
336 // Finds and sets an unused id if the |idstruct| id is already in use.
337 void FindAndSetIdUsed(IdStruct* idstruct) {
338 const int original_id = idstruct->id;
339 int new_id = idstruct->id;
340
341 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
342 // If the original id is not in range - this is an id that can't be
343 // dynamically changed.
344 return;
345 }
346
347 if (IsIdUsed(original_id)) {
348 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 10:09:25349 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
350 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36351 idstruct->id = new_id;
352 }
353 SetIdUsed(new_id);
354 }
355
356 private:
357 // Returns the first unused id in reverse order.
358 // This hopefully reduce the risk of more collisions. We want to change the
359 // default ids as little as possible.
360 int FindUnusedId() {
361 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
362 --next_id_;
363 }
nisseede5da42017-01-12 13:15:36364 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36365 return next_id_;
366 }
367
Yves Gerey665174f2018-06-19 13:03:05368 bool IsIdUsed(int new_id) { return id_set_.find(new_id) != id_set_.end(); }
henrike@webrtc.org28e20752013-07-10 00:45:36369
Yves Gerey665174f2018-06-19 13:03:05370 void SetIdUsed(int new_id) { id_set_.insert(new_id); }
henrike@webrtc.org28e20752013-07-10 00:45:36371
372 const int min_allowed_id_;
373 const int max_allowed_id_;
374 int next_id_;
375 std::set<int> id_set_;
376};
377
378// Helper class used for finding duplicate RTP payload types among audio, video
379// and data codecs. When bundle is used the payload types may not collide.
380class UsedPayloadTypes : public UsedIds<Codec> {
381 public:
382 UsedPayloadTypes()
Yves Gerey665174f2018-06-19 13:03:05383 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36384
385 private:
386 static const int kDynamicPayloadTypeMin = 96;
387 static const int kDynamicPayloadTypeMax = 127;
388};
389
390// Helper class used for finding duplicate RTP Header extension ids among
391// audio and video extensions.
isheriff6f8d6862016-05-26 18:24:55392class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36393 public:
394 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-26 02:15:09395 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
396 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36397
398 private:
henrike@webrtc.org28e20752013-07-10 00:45:36399};
400
zhihuang1c378ed2017-08-17 21:10:50401// Adds a StreamParams for each SenderOptions in |sender_options| to
402// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36403// |current_params| - All currently known StreamParams of any media type.
404template <class C>
zhihuang1c378ed2017-08-17 21:10:50405static bool AddStreamParams(
406 const std::vector<SenderOptions>& sender_options,
407 const std::string& rtcp_cname,
408 StreamParamsVec* current_streams,
409 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 20:15:27410 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 16:38:55411 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 20:15:27412 return true;
413 }
414
Noah Richards2e7a0982015-05-18 21:02:54415 const bool include_rtx_streams =
416 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36417
brandtr03d5fb12016-11-22 11:37:59418 const bool include_flexfec_stream =
419 ContainsFlexfecCodec(content_description->codecs());
420
zhihuang1c378ed2017-08-17 21:10:50421 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36422 // groupid is empty for StreamParams generated using
423 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 21:10:50424 StreamParams* param =
425 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41426 if (!param) {
zhihuang1c378ed2017-08-17 21:10:50427 // This is a new sender.
Peter Boström0c4e06b2015-10-07 10:23:21428 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 21:10:50429 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36430 StreamParams stream_param;
zhihuang1c378ed2017-08-17 21:10:50431 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12432 // Add the generated ssrc.
433 for (size_t i = 0; i < ssrcs.size(); ++i) {
434 stream_param.ssrcs.push_back(ssrcs[i]);
435 }
zhihuang1c378ed2017-08-17 21:10:50436 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12437 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
438 stream_param.ssrc_groups.push_back(group);
439 }
Noah Richards2e7a0982015-05-18 21:02:54440 // Generate extra ssrcs for include_rtx_streams case.
441 if (include_rtx_streams) {
442 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 10:23:21443 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 21:02:54444 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
445 &rtx_ssrcs);
446 for (size_t i = 0; i < ssrcs.size(); ++i) {
447 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
448 }
henrike@webrtc.org28e20752013-07-10 00:45:36449 }
brandtr03d5fb12016-11-22 11:37:59450 // Generate extra ssrc for include_flexfec_stream case.
451 if (include_flexfec_stream) {
452 // TODO(brandtr): Update when we support multistream protection.
453 if (ssrcs.size() == 1) {
454 std::vector<uint32_t> flexfec_ssrcs;
455 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
456 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
brandtr03d5fb12016-11-22 11:37:59457 } else if (!ssrcs.empty()) {
Mirko Bonadei675513b2017-11-09 10:09:25458 RTC_LOG(LS_WARNING)
brandtr03d5fb12016-11-22 11:37:59459 << "Our FlexFEC implementation only supports protecting "
Jonas Olsson45cc8902018-02-13 09:37:07460 "a single media streams. This session has multiple "
461 "media streams however, so no FlexFEC SSRC will be generated.";
brandtr03d5fb12016-11-22 11:37:59462 }
463 }
zhihuang1c378ed2017-08-17 21:10:50464 stream_param.cname = rtcp_cname;
Seth Hampson845e8782018-03-02 19:34:10465 stream_param.set_stream_ids(sender.stream_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36466 content_description->AddStream(stream_param);
467
468 // Store the new StreamParams in current_streams.
469 // This is necessary so that we can use the CNAME for other media types.
470 current_streams->push_back(stream_param);
471 } else {
deadbeef2f425aa2017-04-14 17:41:32472 // Use existing generated SSRCs/groups, but update the sync_label if
473 // necessary. This may be needed if a MediaStreamTrack was moved from one
474 // MediaStream to another.
Seth Hampson845e8782018-03-02 19:34:10475 param->set_stream_ids(sender.stream_ids);
tommi@webrtc.org586f2ed2015-01-22 23:00:41476 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36477 }
478 }
479 return true;
480}
481
482// Updates the transport infos of the |sdesc| according to the given
483// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d3652016-01-08 23:35:57484// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
485// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36486static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
487 SessionDescription* sdesc) {
488 // The bundle should not be empty.
489 if (!sdesc || !bundle_group.FirstContentName()) {
490 return false;
491 }
492
493 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 09:46:32494 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36495 const TransportInfo* selected_transport_info =
496 sdesc->GetTransportInfoByName(selected_content_name);
497 if (!selected_transport_info) {
498 return false;
499 }
500
501 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 09:46:32502 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36503 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 09:46:32504 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36505 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d3652016-01-08 23:35:57506 ConnectionRole selected_connection_role =
507 selected_transport_info->description.connection_role;
Steve Anton3a66edf2018-09-10 19:57:37508 for (TransportInfo& transport_info : sdesc->transport_infos()) {
509 if (bundle_group.HasContentName(transport_info.content_name) &&
510 transport_info.content_name != selected_content_name) {
511 transport_info.description.ice_ufrag = selected_ufrag;
512 transport_info.description.ice_pwd = selected_pwd;
513 transport_info.description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36514 }
515 }
516 return true;
517}
518
519// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
520// sets it to |cryptos|.
521static bool GetCryptosByName(const SessionDescription* sdesc,
522 const std::string& content_name,
523 CryptoParamsVec* cryptos) {
524 if (!sdesc || !cryptos) {
525 return false;
526 }
henrike@webrtc.org28e20752013-07-10 00:45:36527 const ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 23:14:30528 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36529 return false;
530 }
Steve Antonb1c1de12017-12-21 23:14:30531 *cryptos = content->media_description()->cryptos();
henrike@webrtc.org28e20752013-07-10 00:45:36532 return true;
533}
534
henrike@webrtc.org28e20752013-07-10 00:45:36535// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
536// which are not available in |filter|.
537static void PruneCryptos(const CryptoParamsVec& filter,
538 CryptoParamsVec* target_cryptos) {
539 if (!target_cryptos) {
540 return;
541 }
tzik21995802018-04-26 08:38:28542
543 target_cryptos->erase(
544 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
545 // Returns true if the |crypto|'s cipher_suite is not
546 // found in |filter|.
547 [&filter](const CryptoParams& crypto) {
548 for (const CryptoParams& entry : filter) {
549 if (entry.cipher_suite == crypto.cipher_suite)
550 return false;
551 }
552 return true;
553 }),
554 target_cryptos->end());
henrike@webrtc.org28e20752013-07-10 00:45:36555}
556
Steve Antonfa2260d2017-12-29 00:38:23557bool IsRtpProtocol(const std::string& protocol) {
deadbeefb5cb19b2015-11-24 00:39:12558 return protocol.empty() ||
559 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
560}
561
henrike@webrtc.org28e20752013-07-10 00:45:36562static bool IsRtpContent(SessionDescription* sdesc,
563 const std::string& content_name) {
564 bool is_rtp = false;
565 ContentInfo* content = sdesc->GetContentByName(content_name);
Steve Antonb1c1de12017-12-21 23:14:30566 if (content && content->media_description()) {
567 is_rtp = IsRtpProtocol(content->media_description()->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36568 }
569 return is_rtp;
570}
571
572// Updates the crypto parameters of the |sdesc| according to the given
573// |bundle_group|. The crypto parameters of all the contents within the
574// |bundle_group| should be updated to use the common subset of the
575// available cryptos.
576static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
577 SessionDescription* sdesc) {
578 // The bundle should not be empty.
579 if (!sdesc || !bundle_group.FirstContentName()) {
580 return false;
581 }
582
wu@webrtc.org78187522013-10-07 23:32:02583 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36584 // Get the common cryptos.
585 const ContentNames& content_names = bundle_group.content_names();
586 CryptoParamsVec common_cryptos;
Steve Anton3a66edf2018-09-10 19:57:37587 bool first = true;
588 for (const std::string& content_name : content_names) {
589 if (!IsRtpContent(sdesc, content_name)) {
henrike@webrtc.org28e20752013-07-10 00:45:36590 continue;
591 }
wu@webrtc.org78187522013-10-07 23:32:02592 // The common cryptos are needed if any of the content does not have DTLS
593 // enabled.
Steve Anton3a66edf2018-09-10 19:57:37594 if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
wu@webrtc.org78187522013-10-07 23:32:02595 common_cryptos_needed = true;
596 }
Steve Anton3a66edf2018-09-10 19:57:37597 if (first) {
598 first = false;
henrike@webrtc.org28e20752013-07-10 00:45:36599 // Initial the common_cryptos with the first content in the bundle group.
Steve Anton3a66edf2018-09-10 19:57:37600 if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36601 return false;
602 }
603 if (common_cryptos.empty()) {
604 // If there's no crypto params, we should just return.
605 return true;
606 }
607 } else {
608 CryptoParamsVec cryptos;
Steve Anton3a66edf2018-09-10 19:57:37609 if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
henrike@webrtc.org28e20752013-07-10 00:45:36610 return false;
611 }
612 PruneCryptos(cryptos, &common_cryptos);
613 }
614 }
615
wu@webrtc.org78187522013-10-07 23:32:02616 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36617 return false;
618 }
619
620 // Update to use the common cryptos.
Steve Anton3a66edf2018-09-10 19:57:37621 for (const std::string& content_name : content_names) {
622 if (!IsRtpContent(sdesc, content_name)) {
henrike@webrtc.org28e20752013-07-10 00:45:36623 continue;
624 }
Steve Anton3a66edf2018-09-10 19:57:37625 ContentInfo* content = sdesc->GetContentByName(content_name);
henrike@webrtc.org28e20752013-07-10 00:45:36626 if (IsMediaContent(content)) {
Steve Antonb1c1de12017-12-21 23:14:30627 MediaContentDescription* media_desc = content->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36628 if (!media_desc) {
629 return false;
630 }
631 media_desc->set_cryptos(common_cryptos);
632 }
633 }
634 return true;
635}
636
637template <class C>
638static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 11:37:59639 for (const auto& codec : codecs) {
640 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36641 return true;
642 }
643 }
644 return false;
645}
646
647template <class C>
648static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 13:01:01649 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36650}
651
brandtr03d5fb12016-11-22 11:37:59652template <class C>
653static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
654 for (const auto& codec : codecs) {
655 if (IsFlexfecCodec(codec)) {
656 return true;
657 }
658 }
659 return false;
660}
661
662template <class C>
663static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 13:01:01664 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 11:37:59665}
666
zhihuang1c378ed2017-08-17 21:10:50667// Create a media content to be offered for the given |sender_options|,
668// according to the given options.rtcp_mux, session_options.is_muc, codecs,
669// secure_transport, crypto, and current_streams. If we don't currently have
670// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
671// created (according to crypto_suites). The created content is added to the
672// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36673template <class C>
674static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 21:10:50675 const std::vector<SenderOptions>& sender_options,
676 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36677 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57678 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36679 const CryptoParamsVec* current_cryptos,
680 const std::vector<std::string>& crypto_suites,
681 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36682 StreamParamsVec* current_streams,
683 MediaContentDescriptionImpl<C>* offer) {
684 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36685
zhihuang1c378ed2017-08-17 21:10:50686 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 22:02:07687 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
688 offer->set_rtcp_reduced_size(true);
689 }
henrike@webrtc.org28e20752013-07-10 00:45:36690 offer->set_rtp_header_extensions(rtp_extensions);
691
zhihuang1c378ed2017-08-17 21:10:50692 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
693 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36694 return false;
695 }
696
henrike@webrtc.org28e20752013-07-10 00:45:36697 if (secure_policy != SEC_DISABLED) {
698 if (current_cryptos) {
699 AddMediaCryptos(*current_cryptos, offer);
700 }
701 if (offer->cryptos().empty()) {
702 if (!CreateMediaCryptos(crypto_suites, offer)) {
703 return false;
704 }
705 }
706 }
henrike@webrtc.org28e20752013-07-10 00:45:36707
deadbeef7af91dd2016-12-13 19:29:11708 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36709 return false;
710 }
711 return true;
712}
713
714template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34715static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 12:00:16716 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34717 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 12:00:16718 const int codec2_id) {
719 const C* codec1 = FindCodecById(codecs1, codec1_id);
720 const C* codec2 = FindCodecById(codecs2, codec2_id);
721 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34722}
723
724template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36725static void NegotiateCodecs(const std::vector<C>& local_codecs,
726 const std::vector<C>& offered_codecs,
727 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56728 for (const C& ours : local_codecs) {
729 C theirs;
deadbeef67cf2c12016-04-13 17:07:16730 // Note that we intentionally only find one matching codec for each of our
731 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56732 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
733 C negotiated = ours;
734 negotiated.IntersectFeedbackParams(theirs);
735 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 12:00:16736 const auto apt_it =
737 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-05 00:47:56738 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 12:00:16739 RTC_DCHECK(apt_it != theirs.params.end());
740 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36741 }
magjedf823ede2016-11-12 17:53:04742 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
743 webrtc::H264::GenerateProfileLevelIdForAnswer(
744 ours.params, theirs.params, &negotiated.params);
745 }
Taylor Brandstetter6ec641b2016-03-05 00:47:56746 negotiated.id = theirs.id;
ossu075af922016-06-14 10:29:38747 negotiated.name = theirs.name;
magjedb05fa242016-11-11 12:00:16748 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36749 }
750 }
deadbeef67cf2c12016-04-13 17:07:16751 // RFC3264: Although the answerer MAY list the formats in their desired
752 // order of preference, it is RECOMMENDED that unless there is a
753 // specific reason, the answerer list formats in the same relative order
754 // they were present in the offer.
755 std::unordered_map<int, int> payload_type_preferences;
756 int preference = static_cast<int>(offered_codecs.size() + 1);
757 for (const C& codec : offered_codecs) {
758 payload_type_preferences[codec.id] = preference--;
759 }
760 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
761 [&payload_type_preferences](const C& a, const C& b) {
762 return payload_type_preferences[a.id] >
763 payload_type_preferences[b.id];
764 });
henrike@webrtc.org28e20752013-07-10 00:45:36765}
766
Taylor Brandstetter6ec641b2016-03-05 00:47:56767// Finds a codec in |codecs2| that matches |codec_to_match|, which is
768// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
769// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36770template <class C>
Taylor Brandstetter6ec641b2016-03-05 00:47:56771static bool FindMatchingCodec(const std::vector<C>& codecs1,
772 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36773 const C& codec_to_match,
774 C* found_codec) {
Taylor Brandstetter1c349742017-10-04 01:25:36775 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
776 // codecs' associated codecs correctly. If not, that's a programming error.
777 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
778 [&codec_to_match](const C& codec) {
779 return &codec == &codec_to_match;
780 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-05 00:47:56781 for (const C& potential_match : codecs2) {
782 if (potential_match.Matches(codec_to_match)) {
783 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 12:00:16784 int apt_value_1 = 0;
785 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-05 00:47:56786 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
787 &apt_value_1) ||
788 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
789 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 10:09:25790 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-05 00:47:56791 continue;
792 }
793 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
794 apt_value_2)) {
795 continue;
796 }
797 }
798 if (found_codec) {
799 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36800 }
801 return true;
802 }
803 }
804 return false;
805}
806
zhihuang1c378ed2017-08-17 21:10:50807// Find the codec in |codec_list| that |rtx_codec| is associated with.
808template <class C>
809static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
810 const C& rtx_codec) {
811 std::string associated_pt_str;
812 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
813 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 10:09:25814 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
815 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 21:10:50816 return nullptr;
817 }
818
819 int associated_pt;
820 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 10:09:25821 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
822 << " of RTX codec " << rtx_codec.name
823 << " to an integer.";
zhihuang1c378ed2017-08-17 21:10:50824 return nullptr;
825 }
826
827 // Find the associated reference codec for the reference RTX codec.
828 const C* associated_codec = FindCodecById(codec_list, associated_pt);
829 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 10:09:25830 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
831 << associated_pt << " for RTX codec " << rtx_codec.name
832 << ".";
zhihuang1c378ed2017-08-17 21:10:50833 }
834 return associated_codec;
835}
836
837// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36838// already exist in |offered_codecs| and ensure the payload types don't
839// collide.
840template <class C>
zhihuang1c378ed2017-08-17 21:10:50841static void MergeCodecs(const std::vector<C>& reference_codecs,
842 std::vector<C>* offered_codecs,
843 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36844 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56845 for (const C& reference_codec : reference_codecs) {
846 if (!IsRtxCodec(reference_codec) &&
847 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
848 reference_codec, nullptr)) {
849 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36850 used_pltypes->FindAndSetIdUsed(&codec);
851 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36852 }
853 }
854
855 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56856 for (const C& reference_codec : reference_codecs) {
857 if (IsRtxCodec(reference_codec) &&
858 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
859 reference_codec, nullptr)) {
860 C rtx_codec = reference_codec;
olka3c747662017-08-17 13:50:32861 const C* associated_codec =
zhihuang1c378ed2017-08-17 21:10:50862 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 13:50:32863 if (!associated_codec) {
olka3c747662017-08-17 13:50:32864 continue;
865 }
Taylor Brandstetter6ec641b2016-03-05 00:47:56866 // Find a codec in the offered list that matches the reference codec.
867 // Its payload type may be different than the reference codec.
868 C matching_codec;
869 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 12:00:16870 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 10:09:25871 RTC_LOG(LS_WARNING)
872 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-05 00:47:56873 continue;
874 }
875
876 rtx_codec.params[kCodecParamAssociatedPayloadType] =
877 rtc::ToString(matching_codec.id);
878 used_pltypes->FindAndSetIdUsed(&rtx_codec);
879 offered_codecs->push_back(rtx_codec);
880 }
henrike@webrtc.org28e20752013-07-10 00:45:36881 }
882}
883
zhihuang1c378ed2017-08-17 21:10:50884static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
885 const webrtc::RtpExtension& ext_to_match,
886 webrtc::RtpExtension* found_extension) {
Steve Anton3a66edf2018-09-10 19:57:37887 auto it =
888 std::find_if(extensions.begin(), extensions.end(),
889 [&ext_to_match](const webrtc::RtpExtension& extension) {
890 // We assume that all URIs are given in a canonical
891 // format.
892 return extension.uri == ext_to_match.uri &&
893 extension.encrypt == ext_to_match.encrypt;
894 });
895 if (it == extensions.end()) {
896 return false;
zhihuang1c378ed2017-08-17 21:10:50897 }
Steve Anton3a66edf2018-09-10 19:57:37898 if (found_extension) {
899 *found_extension = *it;
900 }
901 return true;
zhihuang1c378ed2017-08-17 21:10:50902}
903
henrike@webrtc.org28e20752013-07-10 00:45:36904static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 18:24:55905 const webrtc::RtpExtension& ext_to_match,
906 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 19:31:36907 // We assume that all URIs are given in a canonical format.
908 const webrtc::RtpExtension* found =
909 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
910 ext_to_match.uri);
911 if (!found) {
912 return false;
913 }
914 if (found_extension) {
915 *found_extension = *found;
916 }
917 return true;
918}
919
920static bool FindByUriWithEncryptionPreference(
921 const RtpHeaderExtensions& extensions,
Yves Gerey665174f2018-06-19 13:03:05922 const webrtc::RtpExtension& ext_to_match,
923 bool encryption_preference,
jbauch5869f502017-06-29 19:31:36924 webrtc::RtpExtension* found_extension) {
925 const webrtc::RtpExtension* unencrypted_extension = nullptr;
Steve Anton3a66edf2018-09-10 19:57:37926 for (const webrtc::RtpExtension& extension : extensions) {
henrike@webrtc.org28e20752013-07-10 00:45:36927 // We assume that all URIs are given in a canonical format.
Steve Anton3a66edf2018-09-10 19:57:37928 if (extension.uri == ext_to_match.uri) {
929 if (!encryption_preference || extension.encrypt) {
jbauch5869f502017-06-29 19:31:36930 if (found_extension) {
Steve Anton3a66edf2018-09-10 19:57:37931 *found_extension = extension;
jbauch5869f502017-06-29 19:31:36932 }
933 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36934 }
Steve Anton3a66edf2018-09-10 19:57:37935 unencrypted_extension = &extension;
henrike@webrtc.org28e20752013-07-10 00:45:36936 }
937 }
jbauch5869f502017-06-29 19:31:36938 if (unencrypted_extension) {
939 if (found_extension) {
940 *found_extension = *unencrypted_extension;
941 }
942 return true;
943 }
henrike@webrtc.org28e20752013-07-10 00:45:36944 return false;
945}
946
zhihuang1c378ed2017-08-17 21:10:50947// Adds all extensions from |reference_extensions| to |offered_extensions| that
948// don't already exist in |offered_extensions| and ensure the IDs don't
949// collide. If an extension is added, it's also added to |regular_extensions| or
950// |encrypted_extensions|, and if the extension is in |regular_extensions| or
951// |encrypted_extensions|, its ID is marked as used in |used_ids|.
952// |offered_extensions| is for either audio or video while |regular_extensions|
953// and |encrypted_extensions| are used for both audio and video. There could be
954// overlap between audio extensions and video extensions.
955static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
956 RtpHeaderExtensions* offered_extensions,
957 RtpHeaderExtensions* regular_extensions,
958 RtpHeaderExtensions* encrypted_extensions,
959 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 13:50:32960 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 21:10:50961 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
962 nullptr)) {
olka3c747662017-08-17 13:50:32963 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 21:10:50964 if (reference_extension.encrypt) {
965 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
966 &existing)) {
967 offered_extensions->push_back(existing);
968 } else {
969 used_ids->FindAndSetIdUsed(&reference_extension);
970 encrypted_extensions->push_back(reference_extension);
971 offered_extensions->push_back(reference_extension);
972 }
olka3c747662017-08-17 13:50:32973 } else {
zhihuang1c378ed2017-08-17 21:10:50974 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
975 &existing)) {
976 offered_extensions->push_back(existing);
977 } else {
978 used_ids->FindAndSetIdUsed(&reference_extension);
979 regular_extensions->push_back(reference_extension);
980 offered_extensions->push_back(reference_extension);
981 }
henrike@webrtc.org79047f92014-03-06 23:46:59982 }
henrike@webrtc.org28e20752013-07-10 00:45:36983 }
984 }
985}
986
jbauch5869f502017-06-29 19:31:36987static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
988 RtpHeaderExtensions* all_extensions,
989 UsedRtpHeaderExtensionIds* used_ids) {
990 RtpHeaderExtensions encrypted_extensions;
991 for (const webrtc::RtpExtension& extension : *extensions) {
992 webrtc::RtpExtension existing;
993 // Don't add encrypted extensions again that were already included in a
994 // previous offer or regular extensions that are also included as encrypted
995 // extensions.
996 if (extension.encrypt ||
997 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
998 (FindByUriWithEncryptionPreference(*extensions, extension, true,
Yves Gerey665174f2018-06-19 13:03:05999 &existing) &&
1000 existing.encrypt)) {
jbauch5869f502017-06-29 19:31:361001 continue;
1002 }
1003
1004 if (FindByUri(*all_extensions, extension, &existing)) {
1005 encrypted_extensions.push_back(existing);
1006 } else {
1007 webrtc::RtpExtension encrypted(extension);
1008 encrypted.encrypt = true;
1009 used_ids->FindAndSetIdUsed(&encrypted);
1010 all_extensions->push_back(encrypted);
1011 encrypted_extensions.push_back(encrypted);
1012 }
1013 }
1014 extensions->insert(extensions->end(), encrypted_extensions.begin(),
Yves Gerey665174f2018-06-19 13:03:051015 encrypted_extensions.end());
jbauch5869f502017-06-29 19:31:361016}
1017
henrike@webrtc.org28e20752013-07-10 00:45:361018static void NegotiateRtpHeaderExtensions(
1019 const RtpHeaderExtensions& local_extensions,
1020 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 19:31:361021 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:361022 RtpHeaderExtensions* negotiated_extenstions) {
Steve Anton3a66edf2018-09-10 19:57:371023 for (const webrtc::RtpExtension& ours : local_extensions) {
isheriff6f8d6862016-05-26 18:24:551024 webrtc::RtpExtension theirs;
Yves Gerey665174f2018-06-19 13:03:051025 if (FindByUriWithEncryptionPreference(
Steve Anton3a66edf2018-09-10 19:57:371026 offered_extensions, ours, enable_encrypted_rtp_header_extensions,
Yves Gerey665174f2018-06-19 13:03:051027 &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:361028 // We respond with their RTP header extension id.
1029 negotiated_extenstions->push_back(theirs);
1030 }
1031 }
1032}
1033
1034static void StripCNCodecs(AudioCodecs* audio_codecs) {
Steve Anton3a66edf2018-09-10 19:57:371035 audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
1036 [](const AudioCodec& codec) {
1037 return STR_CASE_CMP(
1038 codec.name.c_str(),
1039 kComfortNoiseCodecName) == 0;
1040 }),
1041 audio_codecs->end());
henrike@webrtc.org28e20752013-07-10 00:45:361042}
1043
zhihuang1c378ed2017-08-17 21:10:501044// Create a media content to be answered for the given |sender_options|
1045// according to the given session_options.rtcp_mux, session_options.streams,
1046// codecs, crypto, and current_streams. If we don't currently have crypto (in
1047// current_cryptos) and it is enabled (in secure_policy), crypto is created
1048// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1049// negotiated with the offer. If the negotiation fails, this method returns
1050// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:361051template <class C>
1052static bool CreateMediaContentAnswer(
1053 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 21:10:501054 const MediaDescriptionOptions& media_description_options,
1055 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361056 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:571057 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:361058 const CryptoParamsVec* current_cryptos,
1059 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 19:31:361060 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:361061 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:361062 bool bundle_enabled,
1063 MediaContentDescriptionImpl<C>* answer) {
1064 std::vector<C> negotiated_codecs;
1065 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1066 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:361067 answer->set_protocol(offer->protocol());
1068 RtpHeaderExtensions negotiated_rtp_extensions;
Yves Gerey665174f2018-06-19 13:03:051069 NegotiateRtpHeaderExtensions(
1070 local_rtp_extenstions, offer->rtp_header_extensions(),
1071 enable_encrypted_rtp_header_extensions, &negotiated_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:361072 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1073
zhihuang1c378ed2017-08-17 21:10:501074 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 22:02:071075 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1076 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1077 }
henrike@webrtc.org28e20752013-07-10 00:45:361078
1079 if (sdes_policy != SEC_DISABLED) {
1080 CryptoParams crypto;
zhihuang1c378ed2017-08-17 21:10:501081 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1082 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:361083 if (current_cryptos) {
1084 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1085 }
1086 answer->AddCrypto(crypto);
1087 }
1088 }
1089
deadbeef7af91dd2016-12-13 19:29:111090 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:361091 return false;
1092 }
1093
zhihuang1c378ed2017-08-17 21:10:501094 if (!AddStreamParams(media_description_options.sender_options,
1095 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:361096 return false; // Something went seriously wrong.
1097 }
1098
Steve Anton4e70a722017-11-28 22:57:101099 answer->set_direction(NegotiateRtpTransceiverDirection(
1100 offer->direction(), media_description_options.direction));
henrike@webrtc.org28e20752013-07-10 00:45:361101 return true;
1102}
1103
1104static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591105 const std::string& protocol,
1106 bool secure_transport) {
zhihuangcf5b37c2016-05-05 18:44:351107 // Since not all applications serialize and deserialize the media protocol,
1108 // we will have to accept |protocol| to be empty.
1109 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:361110 return true;
1111 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591112
zhihuangcf5b37c2016-05-05 18:44:351113 if (type == MEDIA_TYPE_DATA) {
1114 // Check for SCTP, but also for RTP for RTP-based data channels.
1115 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1116 if (secure_transport) {
1117 // Most likely scenarios first.
1118 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1119 IsPlainRtp(protocol);
1120 } else {
1121 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1122 }
1123 }
1124
1125 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1126 // JSEP specifies.
1127 if (secure_transport) {
1128 // Most likely scenarios first.
1129 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1130 } else {
1131 return IsPlainRtp(protocol);
1132 }
henrike@webrtc.org28e20752013-07-10 00:45:361133}
1134
1135static void SetMediaProtocol(bool secure_transport,
1136 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 19:20:531137 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:361138 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 19:20:531139 else if (secure_transport)
1140 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:361141 else
1142 desc->set_protocol(kMediaProtocolAvpf);
1143}
1144
mallinath@webrtc.org19f27e62013-10-13 17:18:271145// Gets the TransportInfo of the given |content_name| from the
1146// |current_description|. If doesn't exist, returns a new one.
1147static const TransportDescription* GetTransportDescription(
1148 const std::string& content_name,
1149 const SessionDescription* current_description) {
1150 const TransportDescription* desc = NULL;
1151 if (current_description) {
1152 const TransportInfo* info =
1153 current_description->GetTransportInfoByName(content_name);
1154 if (info) {
1155 desc = &info->description;
1156 }
1157 }
1158 return desc;
1159}
1160
1161// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 21:10:501162static bool IsDtlsActive(const ContentInfo* content,
1163 const SessionDescription* current_description) {
1164 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:271165 return false;
zhihuang1c378ed2017-08-17 21:10:501166 }
mallinath@webrtc.org19f27e62013-10-13 17:18:271167
zhihuang1c378ed2017-08-17 21:10:501168 size_t msection_index = content - &current_description->contents()[0];
1169
1170 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:271171 return false;
zhihuang1c378ed2017-08-17 21:10:501172 }
mallinath@webrtc.org19f27e62013-10-13 17:18:271173
zhihuang1c378ed2017-08-17 21:10:501174 return current_description->transport_infos()[msection_index]
1175 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:271176}
1177
Steve Anton8ffb9c32017-08-31 22:45:381178void MediaDescriptionOptions::AddAudioSender(
1179 const std::string& track_id,
1180 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 21:10:501181 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 22:45:381182 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:121183}
1184
Steve Anton8ffb9c32017-08-31 22:45:381185void MediaDescriptionOptions::AddVideoSender(
1186 const std::string& track_id,
1187 const std::vector<std::string>& stream_ids,
1188 int num_sim_layers) {
zhihuang1c378ed2017-08-17 21:10:501189 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 22:45:381190 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:121191}
1192
zhihuang1c378ed2017-08-17 21:10:501193void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1194 const std::string& stream_id) {
1195 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 22:45:381196 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1197 // than one stream?
1198 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:361199}
1200
Steve Anton8ffb9c32017-08-31 22:45:381201void MediaDescriptionOptions::AddSenderInternal(
1202 const std::string& track_id,
1203 const std::vector<std::string>& stream_ids,
1204 int num_sim_layers) {
1205 // TODO(steveanton): Support any number of stream ids.
1206 RTC_CHECK(stream_ids.size() == 1U);
1207 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:361208}
1209
zhihuang1c378ed2017-08-17 21:10:501210bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1211 return std::find_if(media_description_options.begin(),
1212 media_description_options.end(),
1213 [type](const MediaDescriptionOptions& t) {
1214 return t.type == type;
1215 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:431216}
1217
henrike@webrtc.org28e20752013-07-10 00:45:361218MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1219 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 21:10:501220 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:361221
1222MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1223 ChannelManager* channel_manager,
1224 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 21:10:501225 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 14:12:391226 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1227 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:361228 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 11:36:531229 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:361230 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1231 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 21:10:501232 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 10:29:381233}
1234
ossudedfd282016-06-14 14:12:391235const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1236 const {
ossu075af922016-06-14 10:29:381237 return audio_sendrecv_codecs_;
1238}
1239
1240const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1241 return audio_send_codecs_;
1242}
1243
1244const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1245 return audio_recv_codecs_;
1246}
1247
1248void MediaSessionDescriptionFactory::set_audio_codecs(
Yves Gerey665174f2018-06-19 13:03:051249 const AudioCodecs& send_codecs,
1250 const AudioCodecs& recv_codecs) {
ossu075af922016-06-14 10:29:381251 audio_send_codecs_ = send_codecs;
1252 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 21:10:501253 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:361254}
1255
1256SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 21:10:501257 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361258 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 22:18:211259 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:361260
1261 StreamParamsVec current_streams;
1262 GetCurrentStreamParams(current_description, &current_streams);
1263
zhihuang1c378ed2017-08-17 21:10:501264 AudioCodecs offer_audio_codecs;
1265 VideoCodecs offer_video_codecs;
1266 DataCodecs offer_data_codecs;
1267 GetCodecsForOffer(current_description, &offer_audio_codecs,
1268 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 10:29:381269
zhihuang1c378ed2017-08-17 21:10:501270 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:361271 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 21:10:501272 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:361273 }
zhihuang1c378ed2017-08-17 21:10:501274 FilterDataCodecs(&offer_data_codecs,
1275 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:361276
1277 RtpHeaderExtensions audio_rtp_extensions;
1278 RtpHeaderExtensions video_rtp_extensions;
Steve Anton1b8773d2018-04-06 18:13:341279 GetRtpHdrExtsToOffer(session_options, current_description,
1280 &audio_rtp_extensions, &video_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:361281
zhihuang1c378ed2017-08-17 21:10:501282 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:051283 if (current_description) {
zhihuang1c378ed2017-08-17 21:10:501284 RTC_DCHECK(current_description->contents().size() <=
1285 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:361286 }
jiayl@webrtc.org742922b2014-10-07 21:32:431287
zhihuang1c378ed2017-08-17 21:10:501288 // Iterate through the media description options, matching with existing media
1289 // descriptions in |current_description|.
Steve Antondcc3c022017-12-23 00:02:541290 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 21:10:501291 for (const MediaDescriptionOptions& media_description_options :
1292 session_options.media_description_options) {
1293 const ContentInfo* current_content = nullptr;
1294 if (current_description &&
Steve Antondcc3c022017-12-23 00:02:541295 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 21:10:501296 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-23 00:02:541297 // Media type must match unless this media section is being recycled.
1298 RTC_DCHECK(current_content->rejected ||
1299 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 21:10:501300 media_description_options.type));
1301 }
1302 switch (media_description_options.type) {
1303 case MEDIA_TYPE_AUDIO:
1304 if (!AddAudioContentForOffer(media_description_options, session_options,
1305 current_content, current_description,
1306 audio_rtp_extensions, offer_audio_codecs,
1307 &current_streams, offer.get())) {
1308 return nullptr;
1309 }
1310 break;
1311 case MEDIA_TYPE_VIDEO:
1312 if (!AddVideoContentForOffer(media_description_options, session_options,
1313 current_content, current_description,
1314 video_rtp_extensions, offer_video_codecs,
1315 &current_streams, offer.get())) {
1316 return nullptr;
1317 }
1318 break;
1319 case MEDIA_TYPE_DATA:
1320 if (!AddDataContentForOffer(media_description_options, session_options,
1321 current_content, current_description,
1322 offer_data_codecs, &current_streams,
1323 offer.get())) {
1324 return nullptr;
1325 }
1326 break;
1327 default:
1328 RTC_NOTREACHED();
1329 }
1330 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:361331 }
1332
1333 // Bundle the contents together, if we've been asked to do so, and update any
1334 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 21:10:501335 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:361336 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 21:10:501337 for (const ContentInfo& content : offer->contents()) {
1338 // TODO(deadbeef): There are conditions that make bundling two media
1339 // descriptions together illegal. For example, they use the same payload
1340 // type to represent different codecs, or same IDs for different header
1341 // extensions. We need to detect this and not try to bundle those media
1342 // descriptions together.
1343 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:361344 }
1345 offer->AddGroup(offer_bundle);
1346 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 10:09:251347 RTC_LOG(LS_ERROR)
1348 << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 21:10:501349 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:361350 }
1351 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 10:09:251352 RTC_LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 21:10:501353 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:361354 }
1355 }
Steve Antone831b8c2018-02-01 20:22:161356
1357 // The following determines how to signal MSIDs to ensure compatibility with
1358 // older endpoints (in particular, older Plan B endpoints).
1359 if (session_options.is_unified_plan) {
1360 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1361 // Plan answerers will look at a=msid and Plan B answerers will look at the
1362 // a=ssrc MSID line.
1363 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1364 cricket::kMsidSignalingSsrcAttribute);
1365 } else {
1366 // Plan B always signals MSID using a=ssrc lines.
1367 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1368 }
1369
henrike@webrtc.org28e20752013-07-10 00:45:361370 return offer.release();
1371}
1372
1373SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 21:10:501374 const SessionDescription* offer,
1375 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361376 const SessionDescription* current_description) const {
deadbeefb7892532017-02-23 03:35:181377 if (!offer) {
1378 return nullptr;
1379 }
henrike@webrtc.org28e20752013-07-10 00:45:361380 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 17:07:161381 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1382 // from the offer in the answer.
kwiberg31022942016-03-11 22:18:211383 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:361384
1385 StreamParamsVec current_streams;
1386 GetCurrentStreamParams(current_description, &current_streams);
1387
deadbeefb7892532017-02-23 03:35:181388 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1389 // group in the answer with the appropriate content names.
1390 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1391 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1392 // Transport info shared by the bundle group.
1393 std::unique_ptr<TransportInfo> bundle_transport;
1394
zhihuang1c378ed2017-08-17 21:10:501395 // Get list of all possible codecs that respects existing payload type
1396 // mappings and uses a single payload type space.
1397 //
1398 // Note that these lists may be further filtered for each m= section; this
1399 // step is done just to establish the payload type mappings shared by all
1400 // sections.
1401 AudioCodecs answer_audio_codecs;
1402 VideoCodecs answer_video_codecs;
1403 DataCodecs answer_data_codecs;
1404 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1405 &answer_video_codecs, &answer_data_codecs);
1406
1407 if (!session_options.vad_enabled) {
1408 // If application doesn't want CN codecs in answer.
1409 StripCNCodecs(&answer_audio_codecs);
1410 }
1411 FilterDataCodecs(&answer_data_codecs,
1412 session_options.data_channel_type == DCT_SCTP);
1413
1414 // Must have options for exactly as many sections as in the offer.
1415 RTC_DCHECK(offer->contents().size() ==
1416 session_options.media_description_options.size());
1417 // Iterate through the media description options, matching with existing
1418 // media descriptions in |current_description|.
Steve Antondcc3c022017-12-23 00:02:541419 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 21:10:501420 for (const MediaDescriptionOptions& media_description_options :
1421 session_options.media_description_options) {
1422 const ContentInfo* offer_content = &offer->contents()[msection_index];
1423 // Media types and MIDs must match between the remote offer and the
1424 // MediaDescriptionOptions.
1425 RTC_DCHECK(
1426 IsMediaContentOfType(offer_content, media_description_options.type));
1427 RTC_DCHECK(media_description_options.mid == offer_content->name);
1428 const ContentInfo* current_content = nullptr;
1429 if (current_description &&
Steve Antondcc3c022017-12-23 00:02:541430 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 21:10:501431 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-23 03:35:181432 }
zhihuang1c378ed2017-08-17 21:10:501433 switch (media_description_options.type) {
1434 case MEDIA_TYPE_AUDIO:
1435 if (!AddAudioContentForAnswer(
1436 media_description_options, session_options, offer_content,
1437 offer, current_content, current_description,
1438 bundle_transport.get(), answer_audio_codecs, &current_streams,
1439 answer.get())) {
1440 return nullptr;
1441 }
1442 break;
1443 case MEDIA_TYPE_VIDEO:
1444 if (!AddVideoContentForAnswer(
1445 media_description_options, session_options, offer_content,
1446 offer, current_content, current_description,
1447 bundle_transport.get(), answer_video_codecs, &current_streams,
1448 answer.get())) {
1449 return nullptr;
1450 }
1451 break;
1452 case MEDIA_TYPE_DATA:
1453 if (!AddDataContentForAnswer(media_description_options, session_options,
1454 offer_content, offer, current_content,
1455 current_description,
1456 bundle_transport.get(), answer_data_codecs,
1457 &current_streams, answer.get())) {
1458 return nullptr;
1459 }
1460 break;
1461 default:
1462 RTC_NOTREACHED();
1463 }
1464 ++msection_index;
deadbeefb7892532017-02-23 03:35:181465 // See if we can add the newly generated m= section to the BUNDLE group in
1466 // the answer.
1467 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 21:10:501468 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-23 03:35:181469 offer_bundle->HasContentName(added.name)) {
1470 answer_bundle.AddContentName(added.name);
1471 bundle_transport.reset(
1472 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:361473 }
henrike@webrtc.org28e20752013-07-10 00:45:361474 }
1475
Taylor Brandstetter0ab56512018-04-12 17:30:481476 // If a BUNDLE group was offered, put a BUNDLE group in the answer even if
1477 // it's empty. RFC5888 says:
1478 //
1479 // A SIP entity that receives an offer that contains an "a=group" line
1480 // with semantics that are understood MUST return an answer that
1481 // contains an "a=group" line with the same semantics.
1482 if (offer_bundle) {
deadbeefb7892532017-02-23 03:35:181483 answer->AddGroup(answer_bundle);
Taylor Brandstetter0ab56512018-04-12 17:30:481484 }
deadbeefb7892532017-02-23 03:35:181485
Taylor Brandstetter0ab56512018-04-12 17:30:481486 if (answer_bundle.FirstContentName()) {
deadbeefb7892532017-02-23 03:35:181487 // Share the same ICE credentials and crypto params across all contents,
1488 // as BUNDLE requires.
1489 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 10:09:251490 RTC_LOG(LS_ERROR)
1491 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-23 03:35:181492 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:361493 }
henrike@webrtc.org28e20752013-07-10 00:45:361494
deadbeefb7892532017-02-23 03:35:181495 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 10:09:251496 RTC_LOG(LS_ERROR)
1497 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-23 03:35:181498 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:361499 }
1500 }
1501
Steve Antone831b8c2018-02-01 20:22:161502 // The following determines how to signal MSIDs to ensure compatibility with
1503 // older endpoints (in particular, older Plan B endpoints).
1504 if (session_options.is_unified_plan) {
1505 // Unified Plan needs to look at what the offer included to find the most
1506 // compatible answer.
1507 if (offer->msid_signaling() == 0) {
1508 // We end up here in one of three cases:
1509 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1510 // matter what we pick here.
1511 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1512 // it also doesn't matter what we pick here.
1513 // 3. Media that's either sendonly or inactive from the remote endpoint.
1514 // We don't have any information to say whether the endpoint is Plan B
1515 // or Unified Plan, so be conservative and send both.
1516 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1517 cricket::kMsidSignalingSsrcAttribute);
1518 } else if (offer->msid_signaling() ==
1519 (cricket::kMsidSignalingMediaSection |
1520 cricket::kMsidSignalingSsrcAttribute)) {
1521 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1522 // probably talking to a Unified Plan endpoint so respond with just
1523 // a=msid.
1524 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1525 } else {
1526 // Otherwise, it's clear which method the offerer is using so repeat that
1527 // back to them.
1528 answer->set_msid_signaling(offer->msid_signaling());
1529 }
1530 } else {
1531 // Plan B always signals MSID using a=ssrc lines.
1532 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1533 }
1534
henrike@webrtc.org28e20752013-07-10 00:45:361535 return answer.release();
1536}
1537
ossu075af922016-06-14 10:29:381538const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1539 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 22:30:091540 switch (direction) {
1541 // If stream is inactive - generate list as if sendrecv.
1542 case RtpTransceiverDirection::kSendRecv:
1543 case RtpTransceiverDirection::kInactive:
1544 return audio_sendrecv_codecs_;
1545 case RtpTransceiverDirection::kSendOnly:
1546 return audio_send_codecs_;
1547 case RtpTransceiverDirection::kRecvOnly:
1548 return audio_recv_codecs_;
ossu075af922016-06-14 10:29:381549 }
Steve Anton1d03a752017-11-27 22:30:091550 RTC_NOTREACHED();
1551 return audio_sendrecv_codecs_;
ossu075af922016-06-14 10:29:381552}
1553
1554const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1555 const RtpTransceiverDirection& offer,
1556 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 22:30:091557 switch (answer) {
1558 // For inactive and sendrecv answers, generate lists as if we were to accept
1559 // the offer's direction. See RFC 3264 Section 6.1.
1560 case RtpTransceiverDirection::kSendRecv:
1561 case RtpTransceiverDirection::kInactive:
1562 return GetAudioCodecsForOffer(
1563 webrtc::RtpTransceiverDirectionReversed(offer));
1564 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 10:29:381565 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 22:30:091566 case RtpTransceiverDirection::kRecvOnly:
1567 return audio_recv_codecs_;
ossu075af922016-06-14 10:29:381568 }
Steve Anton1d03a752017-11-27 22:30:091569 RTC_NOTREACHED();
1570 return audio_sendrecv_codecs_;
ossu075af922016-06-14 10:29:381571}
1572
zhihuang1c378ed2017-08-17 21:10:501573void MergeCodecsFromDescription(const SessionDescription* description,
1574 AudioCodecs* audio_codecs,
1575 VideoCodecs* video_codecs,
1576 DataCodecs* data_codecs,
1577 UsedPayloadTypes* used_pltypes) {
1578 RTC_DCHECK(description);
1579 for (const ContentInfo& content : description->contents()) {
1580 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1581 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 23:14:301582 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:501583 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1584 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1585 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 23:14:301586 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:501587 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1588 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1589 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 23:14:301590 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 21:10:501591 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1592 }
1593 }
1594}
1595
1596// Getting codecs for an offer involves these steps:
1597//
1598// 1. Construct payload type -> codec mappings for current description.
1599// 2. Add any reference codecs that weren't already present
1600// 3. For each individual media description (m= section), filter codecs based
1601// on the directional attribute (happens in another method).
1602void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:361603 const SessionDescription* current_description,
1604 AudioCodecs* audio_codecs,
1605 VideoCodecs* video_codecs,
1606 DataCodecs* data_codecs) const {
1607 UsedPayloadTypes used_pltypes;
1608 audio_codecs->clear();
1609 video_codecs->clear();
1610 data_codecs->clear();
1611
henrike@webrtc.org28e20752013-07-10 00:45:361612 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 21:10:501613 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1614 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:361615 if (current_description) {
zhihuang1c378ed2017-08-17 21:10:501616 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1617 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:361618 }
1619
1620 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 21:10:501621 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1622 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1623 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1624}
1625
1626// Getting codecs for an answer involves these steps:
1627//
1628// 1. Construct payload type -> codec mappings for current description.
1629// 2. Add any codecs from the offer that weren't already present.
1630// 3. Add any remaining codecs that weren't already present.
1631// 4. For each individual media description (m= section), filter codecs based
1632// on the directional attribute (happens in another method).
1633void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1634 const SessionDescription* current_description,
1635 const SessionDescription* remote_offer,
1636 AudioCodecs* audio_codecs,
1637 VideoCodecs* video_codecs,
1638 DataCodecs* data_codecs) const {
1639 UsedPayloadTypes used_pltypes;
1640 audio_codecs->clear();
1641 video_codecs->clear();
1642 data_codecs->clear();
1643
1644 // First - get all codecs from the current description if the media type
1645 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1646 // new media type is added.
1647 if (current_description) {
1648 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1649 data_codecs, &used_pltypes);
1650 }
1651
1652 // Second - filter out codecs that we don't support at all and should ignore.
1653 AudioCodecs filtered_offered_audio_codecs;
1654 VideoCodecs filtered_offered_video_codecs;
1655 DataCodecs filtered_offered_data_codecs;
1656 for (const ContentInfo& content : remote_offer->contents()) {
1657 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1658 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 23:14:301659 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:501660 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1661 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1662 filtered_offered_audio_codecs,
1663 offered_audio_codec, nullptr) &&
1664 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1665 offered_audio_codec, nullptr)) {
1666 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1667 }
1668 }
1669 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1670 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 23:14:301671 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:501672 for (const VideoCodec& offered_video_codec : video->codecs()) {
1673 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1674 filtered_offered_video_codecs,
1675 offered_video_codec, nullptr) &&
1676 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1677 offered_video_codec, nullptr)) {
1678 filtered_offered_video_codecs.push_back(offered_video_codec);
1679 }
1680 }
1681 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1682 const DataContentDescription* data =
Steve Antonb1c1de12017-12-21 23:14:301683 content.media_description()->as_data();
zhihuang1c378ed2017-08-17 21:10:501684 for (const DataCodec& offered_data_codec : data->codecs()) {
1685 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1686 filtered_offered_data_codecs,
1687 offered_data_codec, nullptr) &&
1688 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1689 offered_data_codec, nullptr)) {
1690 filtered_offered_data_codecs.push_back(offered_data_codec);
1691 }
1692 }
1693 }
1694 }
1695
1696 // Add codecs that are not in |current_description| but were in
1697 // |remote_offer|.
1698 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1699 &used_pltypes);
1700 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1701 &used_pltypes);
1702 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1703 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:361704}
1705
1706void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
Steve Anton1b8773d2018-04-06 18:13:341707 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361708 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 21:10:501709 RtpHeaderExtensions* offer_audio_extensions,
1710 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:591711 // All header extensions allocated from the same range to avoid potential
1712 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:361713 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 19:31:361714 RtpHeaderExtensions all_regular_extensions;
1715 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 21:10:501716 offer_audio_extensions->clear();
1717 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:361718
1719 // First - get all extensions from the current description if the media type
1720 // is used.
1721 // Add them to |used_ids| so the local ids are not reused if a new media
1722 // type is added.
1723 if (current_description) {
zhihuang1c378ed2017-08-17 21:10:501724 for (const ContentInfo& content : current_description->contents()) {
1725 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1726 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 23:14:301727 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:501728 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1729 &all_regular_extensions, &all_encrypted_extensions,
1730 &used_ids);
1731 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1732 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 23:14:301733 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:501734 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1735 &all_regular_extensions, &all_encrypted_extensions,
1736 &used_ids);
1737 }
henrike@webrtc.org28e20752013-07-10 00:45:361738 }
1739 }
1740
1741 // Add our default RTP header extensions that are not in
1742 // |current_description|.
Steve Anton1b8773d2018-04-06 18:13:341743 MergeRtpHdrExts(audio_rtp_header_extensions(session_options.is_unified_plan),
1744 offer_audio_extensions, &all_regular_extensions,
1745 &all_encrypted_extensions, &used_ids);
1746 MergeRtpHdrExts(video_rtp_header_extensions(session_options.is_unified_plan),
1747 offer_video_extensions, &all_regular_extensions,
1748 &all_encrypted_extensions, &used_ids);
zhihuang1c378ed2017-08-17 21:10:501749
jbauch5869f502017-06-29 19:31:361750 // TODO(jbauch): Support adding encrypted header extensions to existing
1751 // sessions.
1752 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 21:10:501753 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1754 &all_encrypted_extensions, &used_ids);
1755 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1756 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 19:31:361757 }
henrike@webrtc.org28e20752013-07-10 00:45:361758}
1759
1760bool MediaSessionDescriptionFactory::AddTransportOffer(
Yves Gerey665174f2018-06-19 13:03:051761 const std::string& content_name,
1762 const TransportOptions& transport_options,
1763 const SessionDescription* current_desc,
1764 SessionDescription* offer_desc) const {
henrike@webrtc.org28e20752013-07-10 00:45:361765 if (!transport_desc_factory_)
Yves Gerey665174f2018-06-19 13:03:051766 return false;
henrike@webrtc.org28e20752013-07-10 00:45:361767 const TransportDescription* current_tdesc =
1768 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 22:18:211769 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:361770 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
Yves Gerey665174f2018-06-19 13:03:051771 bool ret =
1772 (new_tdesc.get() != NULL &&
1773 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
henrike@webrtc.org28e20752013-07-10 00:45:361774 if (!ret) {
Mirko Bonadei675513b2017-11-09 10:09:251775 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
1776 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:361777 }
1778 return ret;
1779}
1780
1781TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1782 const std::string& content_name,
1783 const SessionDescription* offer_desc,
1784 const TransportOptions& transport_options,
deadbeefb7892532017-02-23 03:35:181785 const SessionDescription* current_desc,
1786 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:361787 if (!transport_desc_factory_)
1788 return NULL;
1789 const TransportDescription* offer_tdesc =
1790 GetTransportDescription(content_name, offer_desc);
1791 const TransportDescription* current_tdesc =
1792 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-23 03:35:181793 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1794 require_transport_attributes,
1795 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:361796}
1797
1798bool MediaSessionDescriptionFactory::AddTransportAnswer(
1799 const std::string& content_name,
1800 const TransportDescription& transport_desc,
1801 SessionDescription* answer_desc) const {
Yves Gerey665174f2018-06-19 13:03:051802 if (!answer_desc->AddTransportInfo(
1803 TransportInfo(content_name, transport_desc))) {
Mirko Bonadei675513b2017-11-09 10:09:251804 RTC_LOG(LS_ERROR) << "Failed to AddTransportAnswer, content name="
1805 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:361806 return false;
1807 }
1808 return true;
1809}
1810
zhihuang1c378ed2017-08-17 21:10:501811// |audio_codecs| = set of all possible codecs that can be used, with correct
1812// payload type mappings
1813//
1814// |supported_audio_codecs| = set of codecs that are supported for the direction
1815// of this m= section
1816//
1817// acd->codecs() = set of previously negotiated codecs for this m= section
1818//
1819// The payload types should come from audio_codecs, but the order should come
1820// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1821// change existing codec priority, and that new codecs are added with the right
1822// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:051823bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 21:10:501824 const MediaDescriptionOptions& media_description_options,
1825 const MediaSessionOptions& session_options,
1826 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051827 const SessionDescription* current_description,
1828 const RtpHeaderExtensions& audio_rtp_extensions,
1829 const AudioCodecs& audio_codecs,
1830 StreamParamsVec* current_streams,
1831 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 21:10:501832 // Filter audio_codecs (which includes all codecs, with correctly remapped
1833 // payload types) based on transceiver direction.
1834 const AudioCodecs& supported_audio_codecs =
1835 GetAudioCodecsForOffer(media_description_options.direction);
1836
1837 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-23 00:02:541838 // Add the codecs from current content if it exists and is not being recycled.
1839 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-13 03:37:381840 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 21:10:501841 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 23:14:301842 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:501843 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-04 01:25:361844 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1845 nullptr)) {
zhihuang1c378ed2017-08-17 21:10:501846 filtered_codecs.push_back(codec);
1847 }
1848 }
1849 }
1850 // Add other supported audio codecs.
1851 AudioCodec found_codec;
1852 for (const AudioCodec& codec : supported_audio_codecs) {
1853 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1854 codec, &found_codec) &&
1855 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1856 codec, nullptr)) {
1857 // Use the |found_codec| from |audio_codecs| because it has the correctly
1858 // mapped payload type.
1859 filtered_codecs.push_back(found_codec);
1860 }
1861 }
deadbeef44f08192015-12-16 00:20:091862
jiayl@webrtc.orge7d47a12014-08-05 19:19:051863 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 21:10:501864 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1865 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051866
kwiberg31022942016-03-11 22:18:211867 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051868 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 21:10:501869 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1870 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051871 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 21:10:501872 media_description_options.sender_options, session_options,
1873 filtered_codecs, sdes_policy, GetCryptos(current_content),
1874 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:051875 return false;
1876 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:051877
1878 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1879 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:151880
Steve Anton4e70a722017-11-28 22:57:101881 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:431882
Steve Anton5adfafd2017-12-21 00:34:001883 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 21:10:501884 media_description_options.stopped, audio.release());
1885 if (!AddTransportOffer(media_description_options.mid,
1886 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051887 current_description, desc)) {
1888 return false;
1889 }
1890
1891 return true;
1892}
1893
1894bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 21:10:501895 const MediaDescriptionOptions& media_description_options,
1896 const MediaSessionOptions& session_options,
1897 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051898 const SessionDescription* current_description,
1899 const RtpHeaderExtensions& video_rtp_extensions,
1900 const VideoCodecs& video_codecs,
1901 StreamParamsVec* current_streams,
1902 SessionDescription* desc) const {
1903 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 21:10:501904 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1905 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051906
kwiberg31022942016-03-11 22:18:211907 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051908 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 21:10:501909 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1910 &crypto_suites);
1911
1912 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-23 00:02:541913 // Add the codecs from current content if it exists and is not being recycled.
1914 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-13 03:37:381915 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 21:10:501916 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 23:14:301917 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:501918 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-04 01:25:361919 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 21:10:501920 nullptr)) {
1921 filtered_codecs.push_back(codec);
1922 }
1923 }
1924 }
1925 // Add other supported video codecs.
1926 VideoCodec found_codec;
1927 for (const VideoCodec& codec : video_codecs_) {
1928 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1929 &found_codec) &&
1930 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1931 nullptr)) {
1932 // Use the |found_codec| from |video_codecs| because it has the correctly
1933 // mapped payload type.
1934 filtered_codecs.push_back(found_codec);
1935 }
1936 }
1937
jiayl@webrtc.orge7d47a12014-08-05 19:19:051938 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 21:10:501939 media_description_options.sender_options, session_options,
1940 filtered_codecs, sdes_policy, GetCryptos(current_content),
1941 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:051942 return false;
1943 }
1944
zhihuang1c378ed2017-08-17 21:10:501945 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051946
1947 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1948 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:431949
Steve Anton4e70a722017-11-28 22:57:101950 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:431951
Steve Anton5adfafd2017-12-21 00:34:001952 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 21:10:501953 media_description_options.stopped, video.release());
1954 if (!AddTransportOffer(media_description_options.mid,
1955 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051956 current_description, desc)) {
1957 return false;
1958 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:051959 return true;
1960}
1961
1962bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 21:10:501963 const MediaDescriptionOptions& media_description_options,
1964 const MediaSessionOptions& session_options,
1965 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051966 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 21:10:501967 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051968 StreamParamsVec* current_streams,
1969 SessionDescription* desc) const {
1970 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1971
kwiberg31022942016-03-11 22:18:211972 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 21:10:501973 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
1974 // If the DataChannel type is not specified, use the DataChannel type in
1975 // the current description.
1976 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-13 03:37:381977 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
Steve Antonb1c1de12017-12-21 23:14:301978 is_sctp = (current_content->media_description()->protocol() ==
1979 kMediaProtocolSctp);
zhihuang1c378ed2017-08-17 21:10:501980 }
deadbeef44f08192015-12-16 00:20:091981
jiayl@webrtc.orge7d47a12014-08-05 19:19:051982 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 21:10:501983 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1984 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051985 std::vector<std::string> crypto_suites;
1986 if (is_sctp) {
1987 // SDES doesn't make sense for SCTP, so we disable it, and we only
1988 // get SDES crypto suites for RTP-based data channels.
1989 sdes_policy = cricket::SEC_DISABLED;
1990 // Unlike SetMediaProtocol below, we need to set the protocol
1991 // before we call CreateMediaContentOffer. Otherwise,
1992 // CreateMediaContentOffer won't know this is SCTP and will
1993 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 16:38:551994 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
1995 // it's safe to do so. Older versions of webrtc would reject these
1996 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
Yves Gerey665174f2018-06-19 13:03:051997 data->set_protocol(secure_transport ? kMediaProtocolDtlsSctp
1998 : kMediaProtocolSctp);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051999 } else {
zhihuang1c378ed2017-08-17 21:10:502000 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 10:23:332001 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052002 }
2003
zhihuang1c378ed2017-08-17 21:10:502004 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:052005 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 21:10:502006 media_description_options.sender_options, session_options,
2007 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2008 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052009 return false;
2010 }
2011
2012 if (is_sctp) {
Steve Anton5adfafd2017-12-21 00:34:002013 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
zhihuang1c378ed2017-08-17 21:10:502014 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:052015 } else {
zhihuang1c378ed2017-08-17 21:10:502016 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052017 SetMediaProtocol(secure_transport, data.get());
Steve Anton5adfafd2017-12-21 00:34:002018 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
zhihuang1c378ed2017-08-17 21:10:502019 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:052020 }
zhihuang1c378ed2017-08-17 21:10:502021 if (!AddTransportOffer(media_description_options.mid,
2022 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052023 current_description, desc)) {
2024 return false;
2025 }
2026 return true;
2027}
2028
zhihuang1c378ed2017-08-17 21:10:502029// |audio_codecs| = set of all possible codecs that can be used, with correct
2030// payload type mappings
2031//
2032// |supported_audio_codecs| = set of codecs that are supported for the direction
2033// of this m= section
2034//
2035// acd->codecs() = set of previously negotiated codecs for this m= section
2036//
2037// The payload types should come from audio_codecs, but the order should come
2038// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2039// change existing codec priority, and that new codecs are added with the right
2040// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052041bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502042 const MediaDescriptionOptions& media_description_options,
2043 const MediaSessionOptions& session_options,
2044 const ContentInfo* offer_content,
2045 const SessionDescription* offer_description,
2046 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052047 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182048 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 21:10:502049 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052050 StreamParamsVec* current_streams,
2051 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-13 03:37:382052 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 21:10:502053 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 23:14:302054 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:052055
deadbeefb7892532017-02-23 03:35:182056 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 21:10:502057 CreateTransportAnswer(media_description_options.mid, offer_description,
2058 media_description_options.transport_options,
deadbeefb7892532017-02-23 03:35:182059 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052060 if (!audio_transport) {
2061 return false;
2062 }
2063
zhihuang1c378ed2017-08-17 21:10:502064 // Pick codecs based on the requested communications direction in the offer
2065 // and the selected direction in the answer.
2066 // Note these will be filtered one final time in CreateMediaContentAnswer.
2067 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 22:57:102068 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 10:29:382069 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 21:10:502070 AudioCodecs supported_audio_codecs =
2071 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2072
2073 AudioCodecs filtered_codecs;
Steve Antondcc3c022017-12-23 00:02:542074 // Add the codecs from current content if it exists and is not being recycled.
2075 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-13 03:37:382076 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 21:10:502077 const AudioContentDescription* acd =
Steve Antonb1c1de12017-12-21 23:14:302078 current_content->media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:502079 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-04 01:25:362080 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2081 nullptr)) {
zhihuang1c378ed2017-08-17 21:10:502082 filtered_codecs.push_back(codec);
2083 }
2084 }
2085 }
2086 // Add other supported audio codecs.
zhihuang1c378ed2017-08-17 21:10:502087 for (const AudioCodec& codec : supported_audio_codecs) {
2088 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Zhi Huang6f367472017-11-22 21:20:022089 codec, nullptr) &&
zhihuang1c378ed2017-08-17 21:10:502090 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2091 codec, nullptr)) {
Zhi Huang6f367472017-11-22 21:20:022092 // We should use the local codec with local parameters and the codec id
2093 // would be correctly mapped in |NegotiateCodecs|.
2094 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 21:10:502095 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052096 }
2097
zhihuang1c378ed2017-08-17 21:10:502098 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2099 session_options.bundle_enabled;
kwiberg31022942016-03-11 22:18:212100 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:052101 new AudioContentDescription());
2102 // Do not require or create SDES cryptos if DTLS is used.
2103 cricket::SecurePolicy sdes_policy =
2104 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2105 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 21:10:502106 offer_audio_description, media_description_options, session_options,
2107 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 18:13:342108 audio_rtp_header_extensions(session_options.is_unified_plan),
2109 enable_encrypted_rtp_header_extensions_, current_streams,
2110 bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052111 return false; // Fails the session setup.
2112 }
2113
deadbeefb7892532017-02-23 03:35:182114 bool secure = bundle_transport ? bundle_transport->description.secure()
2115 : audio_transport->secure();
zhihuang1c378ed2017-08-17 21:10:502116 bool rejected = media_description_options.stopped ||
2117 offer_content->rejected ||
deadbeefb7892532017-02-23 03:35:182118 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2119 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352120 if (!AddTransportAnswer(media_description_options.mid,
2121 *(audio_transport.get()), answer)) {
2122 return false;
2123 }
2124
2125 if (rejected) {
Mirko Bonadei675513b2017-11-09 10:09:252126 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2127 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:052128 }
2129
zhihuang1c378ed2017-08-17 21:10:502130 answer->AddContent(media_description_options.mid, offer_content->type,
2131 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:052132 return true;
2133}
2134
2135bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502136 const MediaDescriptionOptions& media_description_options,
2137 const MediaSessionOptions& session_options,
2138 const ContentInfo* offer_content,
2139 const SessionDescription* offer_description,
2140 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052141 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182142 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 21:10:502143 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052144 StreamParamsVec* current_streams,
2145 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-13 03:37:382146 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 21:10:502147 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 23:14:302148 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:502149
deadbeefb7892532017-02-23 03:35:182150 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 21:10:502151 CreateTransportAnswer(media_description_options.mid, offer_description,
2152 media_description_options.transport_options,
deadbeefb7892532017-02-23 03:35:182153 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052154 if (!video_transport) {
2155 return false;
2156 }
2157
zhihuang1c378ed2017-08-17 21:10:502158 VideoCodecs filtered_codecs;
Steve Antondcc3c022017-12-23 00:02:542159 // Add the codecs from current content if it exists and is not being recycled.
2160 if (current_content && !current_content->rejected) {
Taylor Brandstetter80cfb522017-10-13 03:37:382161 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 21:10:502162 const VideoContentDescription* vcd =
Steve Antonb1c1de12017-12-21 23:14:302163 current_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:502164 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-04 01:25:362165 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 21:10:502166 nullptr)) {
2167 filtered_codecs.push_back(codec);
2168 }
2169 }
2170 }
2171 // Add other supported video codecs.
zhihuang1c378ed2017-08-17 21:10:502172 for (const VideoCodec& codec : video_codecs_) {
2173 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
Zhi Huang6f367472017-11-22 21:20:022174 nullptr) &&
zhihuang1c378ed2017-08-17 21:10:502175 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2176 nullptr)) {
Zhi Huang6f367472017-11-22 21:20:022177 // We should use the local codec with local parameters and the codec id
2178 // would be correctly mapped in |NegotiateCodecs|.
2179 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 21:10:502180 }
2181 }
2182
2183 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2184 session_options.bundle_enabled;
2185
kwiberg31022942016-03-11 22:18:212186 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:052187 new VideoContentDescription());
2188 // Do not require or create SDES cryptos if DTLS is used.
2189 cricket::SecurePolicy sdes_policy =
2190 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:052191 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 21:10:502192 offer_video_description, media_description_options, session_options,
2193 filtered_codecs, sdes_policy, GetCryptos(current_content),
Steve Anton1b8773d2018-04-06 18:13:342194 video_rtp_header_extensions(session_options.is_unified_plan),
2195 enable_encrypted_rtp_header_extensions_, current_streams,
2196 bundle_enabled, video_answer.get())) {
zhihuang1c378ed2017-08-17 21:10:502197 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052198 }
deadbeefb7892532017-02-23 03:35:182199 bool secure = bundle_transport ? bundle_transport->description.secure()
2200 : video_transport->secure();
zhihuang1c378ed2017-08-17 21:10:502201 bool rejected = media_description_options.stopped ||
2202 offer_content->rejected ||
deadbeefb7892532017-02-23 03:35:182203 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2204 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352205 if (!AddTransportAnswer(media_description_options.mid,
2206 *(video_transport.get()), answer)) {
2207 return false;
2208 }
2209
jiayl@webrtc.orge7d47a12014-08-05 19:19:052210 if (!rejected) {
zhihuang1c378ed2017-08-17 21:10:502211 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052212 } else {
Mirko Bonadei675513b2017-11-09 10:09:252213 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2214 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:052215 }
zhihuang1c378ed2017-08-17 21:10:502216 answer->AddContent(media_description_options.mid, offer_content->type,
2217 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:052218 return true;
2219}
2220
2221bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502222 const MediaDescriptionOptions& media_description_options,
2223 const MediaSessionOptions& session_options,
2224 const ContentInfo* offer_content,
2225 const SessionDescription* offer_description,
2226 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052227 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182228 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 21:10:502229 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052230 StreamParamsVec* current_streams,
2231 SessionDescription* answer) const {
deadbeefb7892532017-02-23 03:35:182232 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 21:10:502233 CreateTransportAnswer(media_description_options.mid, offer_description,
2234 media_description_options.transport_options,
deadbeefb7892532017-02-23 03:35:182235 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052236 if (!data_transport) {
2237 return false;
2238 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052239
kwiberg31022942016-03-11 22:18:212240 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:052241 new DataContentDescription());
2242 // Do not require or create SDES cryptos if DTLS is used.
2243 cricket::SecurePolicy sdes_policy =
2244 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 21:10:502245 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2246 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-13 03:37:382247 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2248 const DataContentDescription* offer_data_description =
Steve Antonb1c1de12017-12-21 23:14:302249 offer_content->media_description()->as_data();
jiayl@webrtc.orge7d47a12014-08-05 19:19:052250 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-13 03:37:382251 offer_data_description, media_description_options, session_options,
2252 data_codecs, sdes_policy, GetCryptos(current_content),
2253 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2254 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052255 return false; // Fails the session setup.
2256 }
2257
zstein4b2e0822017-02-18 03:48:382258 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-18 03:48:382259 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2260 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2261
deadbeefb7892532017-02-23 03:35:182262 bool secure = bundle_transport ? bundle_transport->description.secure()
2263 : data_transport->secure();
2264
zhihuang1c378ed2017-08-17 21:10:502265 bool rejected = session_options.data_channel_type == DCT_NONE ||
2266 media_description_options.stopped ||
2267 offer_content->rejected ||
deadbeefb7892532017-02-23 03:35:182268 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2269 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352270 if (!AddTransportAnswer(media_description_options.mid,
2271 *(data_transport.get()), answer)) {
2272 return false;
2273 }
2274
jiayl@webrtc.orge7d47a12014-08-05 19:19:052275 if (!rejected) {
zhihuang1c378ed2017-08-17 21:10:502276 data_answer->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052277 } else {
2278 // RFC 3264
2279 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 10:09:252280 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:052281 }
zhihuang1c378ed2017-08-17 21:10:502282 answer->AddContent(media_description_options.mid, offer_content->type,
2283 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:052284 return true;
2285}
2286
zhihuang1c378ed2017-08-17 21:10:502287void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2288 audio_sendrecv_codecs_.clear();
2289 all_audio_codecs_.clear();
2290 // Compute the audio codecs union.
2291 for (const AudioCodec& send : audio_send_codecs_) {
2292 all_audio_codecs_.push_back(send);
2293 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2294 send, nullptr)) {
2295 // It doesn't make sense to have an RTX codec we support sending but not
2296 // receiving.
2297 RTC_DCHECK(!IsRtxCodec(send));
2298 }
2299 }
2300 for (const AudioCodec& recv : audio_recv_codecs_) {
2301 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2302 recv, nullptr)) {
2303 all_audio_codecs_.push_back(recv);
2304 }
2305 }
2306 // Use NegotiateCodecs to merge our codec lists, since the operation is
2307 // essentially the same. Put send_codecs as the offered_codecs, which is the
2308 // order we'd like to follow. The reasoning is that encoding is usually more
2309 // expensive than decoding, and prioritizing a codec in the send list probably
2310 // means it's a codec we can handle efficiently.
2311 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2312 &audio_sendrecv_codecs_);
2313}
2314
henrike@webrtc.org28e20752013-07-10 00:45:362315bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-21 00:34:002316 return (content && (content->type == MediaProtocolType::kRtp ||
2317 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:362318}
2319
2320bool IsAudioContent(const ContentInfo* content) {
2321 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2322}
2323
2324bool IsVideoContent(const ContentInfo* content) {
2325 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2326}
2327
2328bool IsDataContent(const ContentInfo* content) {
2329 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2330}
2331
deadbeef0ed85b22016-02-24 01:24:522332const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2333 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502334 for (const ContentInfo& content : contents) {
2335 if (IsMediaContentOfType(&content, media_type)) {
2336 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:362337 }
2338 }
deadbeef0ed85b22016-02-24 01:24:522339 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:362340}
2341
2342const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2343 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2344}
2345
2346const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2347 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2348}
2349
2350const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2351 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2352}
2353
Steve Antonad7bffc2018-01-22 18:21:562354const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2355 MediaType media_type) {
deadbeef0ed85b22016-02-24 01:24:522356 if (sdesc == nullptr) {
2357 return nullptr;
2358 }
henrike@webrtc.org28e20752013-07-10 00:45:362359
2360 return GetFirstMediaContent(sdesc->contents(), media_type);
2361}
2362
2363const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2364 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2365}
2366
2367const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2368 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2369}
2370
2371const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2372 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2373}
2374
2375const MediaContentDescription* GetFirstMediaContentDescription(
Yves Gerey665174f2018-06-19 13:03:052376 const SessionDescription* sdesc,
2377 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:362378 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 23:14:302379 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:362380}
2381
2382const AudioContentDescription* GetFirstAudioContentDescription(
2383 const SessionDescription* sdesc) {
2384 return static_cast<const AudioContentDescription*>(
2385 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2386}
2387
2388const VideoContentDescription* GetFirstVideoContentDescription(
2389 const SessionDescription* sdesc) {
2390 return static_cast<const VideoContentDescription*>(
2391 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2392}
2393
2394const DataContentDescription* GetFirstDataContentDescription(
2395 const SessionDescription* sdesc) {
2396 return static_cast<const DataContentDescription*>(
2397 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2398}
2399
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502400//
2401// Non-const versions of the above functions.
2402//
2403
Steve Anton36b29d12017-10-30 16:57:422404ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502405 MediaType media_type) {
Steve Anton36b29d12017-10-30 16:57:422406 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502407 if (IsMediaContentOfType(&content, media_type)) {
2408 return &content;
2409 }
2410 }
2411 return nullptr;
2412}
2413
Steve Anton36b29d12017-10-30 16:57:422414ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502415 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2416}
2417
Steve Anton36b29d12017-10-30 16:57:422418ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502419 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2420}
2421
Steve Anton36b29d12017-10-30 16:57:422422ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502423 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2424}
2425
Steve Antonad7bffc2018-01-22 18:21:562426ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2427 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502428 if (sdesc == nullptr) {
2429 return nullptr;
2430 }
2431
Steve Anton36b29d12017-10-30 16:57:422432 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502433}
2434
2435ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2436 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2437}
2438
2439ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2440 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2441}
2442
2443ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2444 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2445}
2446
2447MediaContentDescription* GetFirstMediaContentDescription(
2448 SessionDescription* sdesc,
2449 MediaType media_type) {
2450 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 23:14:302451 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502452}
2453
2454AudioContentDescription* GetFirstAudioContentDescription(
2455 SessionDescription* sdesc) {
2456 return static_cast<AudioContentDescription*>(
2457 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2458}
2459
2460VideoContentDescription* GetFirstVideoContentDescription(
2461 SessionDescription* sdesc) {
2462 return static_cast<VideoContentDescription*>(
2463 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2464}
2465
2466DataContentDescription* GetFirstDataContentDescription(
2467 SessionDescription* sdesc) {
2468 return static_cast<DataContentDescription*>(
2469 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2470}
2471
henrike@webrtc.org28e20752013-07-10 00:45:362472} // namespace cricket