blob: 9c31f50d41e985902cea46fe9dab80d8483f6ad3 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
deadbeef67cf2c12016-04-13 10:07:16 -070013#include <algorithm> // For std::find_if, std::sort.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <functional>
15#include <map>
kwiberg31022942016-03-11 14:18:21 -080016#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <set>
deadbeef67cf2c12016-04-13 10:07:16 -070018#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <utility>
20
Patrik Höglund7aee3d52017-11-15 13:15:17 +010021#include "api/cryptoparams.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "api/optional.h"
Mirko Bonadei71207422017-09-15 13:58:09 +020023#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "media/base/h264_profile_level_id.h"
25#include "media/base/mediaconstants.h"
26#include "p2p/base/p2pconstants.h"
27#include "pc/channelmanager.h"
Steve Anton1d03a752017-11-27 14:30:09 -080028#include "pc/rtpmediautils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "pc/srtpfilter.h"
30#include "rtc_base/base64.h"
31#include "rtc_base/checks.h"
32#include "rtc_base/helpers.h"
33#include "rtc_base/logging.h"
34#include "rtc_base/stringutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035
36namespace {
Steve Anton1d03a752017-11-27 14:30:09 -080037
38using webrtc::RtpTransceiverDirection;
39
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080041
deadbeef7914b8c2017-04-21 03:23:33 -070042void GetSupportedSdesCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
43 std::vector<int>*),
44 const rtc::CryptoOptions& crypto_options,
45 std::vector<std::string>* names) {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080046 std::vector<int> crypto_suites;
jbauchcb560652016-08-04 05:20:32 -070047 func(crypto_options, &crypto_suites);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080048 for (const auto crypto : crypto_suites) {
49 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
50 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080051}
terelius8c011e52016-04-26 05:28:11 -070052} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053
54namespace cricket {
55
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056// RTP Profile names
57// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
58// RFC4585
59const char kMediaProtocolAvpf[] = "RTP/AVPF";
60// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000061const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
62
deadbeeff3938292015-07-15 12:20:53 -070063// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
64// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065const char kMediaProtocolSavpf[] = "RTP/SAVPF";
66
67const char kMediaProtocolRtpPrefix[] = "RTP/";
68
69const char kMediaProtocolSctp[] = "SCTP";
70const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000071const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000072const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073
deadbeef8b7e9ad2017-05-25 09:38:55 -070074// Note that the below functions support some protocol strings purely for
75// legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
76// and Interoperability.
77
78static bool IsDtlsRtp(const std::string& protocol) {
79 // Most-likely values first.
80 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
81 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
82}
83
84static bool IsPlainRtp(const std::string& protocol) {
85 // Most-likely values first.
86 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
87 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
88}
89
90static bool IsDtlsSctp(const std::string& protocol) {
91 return protocol == kMediaProtocolDtlsSctp ||
92 protocol == kMediaProtocolUdpDtlsSctp ||
93 protocol == kMediaProtocolTcpDtlsSctp;
94}
95
96static bool IsPlainSctp(const std::string& protocol) {
97 return protocol == kMediaProtocolSctp;
98}
99
100static bool IsSctp(const std::string& protocol) {
101 return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
102}
103
Steve Anton1d03a752017-11-27 14:30:09 -0800104RtpTransceiverDirection RtpTransceiverDirectionFromMediaContentDirection(
105 MediaContentDirection direction) {
106 switch (direction) {
107 case MD_SENDRECV:
108 return RtpTransceiverDirection::kSendRecv;
109 case MD_SENDONLY:
110 return RtpTransceiverDirection::kSendOnly;
111 case MD_RECVONLY:
112 return RtpTransceiverDirection::kRecvOnly;
113 case MD_INACTIVE:
114 return RtpTransceiverDirection::kInactive;
115 }
116 RTC_NOTREACHED();
117 return RtpTransceiverDirection::kInactive;
ossu075af922016-06-14 03:29:38 -0700118}
119
Steve Anton1d03a752017-11-27 14:30:09 -0800120MediaContentDirection MediaContentDirectionFromRtpTransceiverDirection(
121 RtpTransceiverDirection direction) {
122 switch (direction) {
123 case RtpTransceiverDirection::kSendRecv:
124 return MD_SENDRECV;
125 case RtpTransceiverDirection::kSendOnly:
126 return MD_SENDONLY;
127 case RtpTransceiverDirection::kRecvOnly:
128 return MD_RECVONLY;
129 case RtpTransceiverDirection::kInactive:
130 return MD_INACTIVE;
ossu075af922016-06-14 03:29:38 -0700131 }
Steve Anton1d03a752017-11-27 14:30:09 -0800132 RTC_NOTREACHED();
ossu075af922016-06-14 03:29:38 -0700133 return MD_INACTIVE;
134}
135
Steve Anton1d03a752017-11-27 14:30:09 -0800136static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
137 RtpTransceiverDirection offer,
138 RtpTransceiverDirection wants) {
139 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
140 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
141 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
142 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
143 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
144 offer_send && wants_recv);
ossu075af922016-06-14 03:29:38 -0700145}
146
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147static bool IsMediaContentOfType(const ContentInfo* content,
148 MediaType media_type) {
149 if (!IsMediaContent(content)) {
150 return false;
151 }
152
153 const MediaContentDescription* mdesc =
154 static_cast<const MediaContentDescription*>(content->description);
155 return mdesc && mdesc->type() == media_type;
156}
157
158static bool CreateCryptoParams(int tag, const std::string& cipher,
159 CryptoParams *out) {
jbauchcb560652016-08-04 05:20:32 -0700160 int key_len;
161 int salt_len;
162 if (!rtc::GetSrtpKeyAndSaltLengths(
163 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164 return false;
165 }
jbauchcb560652016-08-04 05:20:32 -0700166
167 int master_key_len = key_len + salt_len;
168 std::string master_key;
169 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
170 return false;
171 }
172
kwiberg352444f2016-11-28 15:58:53 -0800173 RTC_CHECK_EQ(master_key_len, master_key.size());
jbauchcb560652016-08-04 05:20:32 -0700174 std::string key = rtc::Base64::Encode(master_key);
175
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176 out->tag = tag;
177 out->cipher_suite = cipher;
178 out->key_params = kInline;
179 out->key_params += key;
180 return true;
181}
182
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183static bool AddCryptoParams(const std::string& cipher_suite,
184 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000185 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000186
187 out->resize(size + 1);
188 return CreateCryptoParams(size, cipher_suite, &out->at(size));
189}
190
191void AddMediaCryptos(const CryptoParamsVec& cryptos,
192 MediaContentDescription* media) {
193 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
194 crypto != cryptos.end(); ++crypto) {
195 media->AddCrypto(*crypto);
196 }
197}
198
199bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
200 MediaContentDescription* media) {
201 CryptoParamsVec cryptos;
202 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
203 it != crypto_suites.end(); ++it) {
204 if (!AddCryptoParams(*it, &cryptos)) {
205 return false;
206 }
207 }
208 AddMediaCryptos(cryptos, media);
209 return true;
210}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211
zhihuang1c378ed2017-08-17 14:10:50 -0700212const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
213 if (!content) {
214 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215 }
zhihuang1c378ed2017-08-17 14:10:50 -0700216
217 RTC_DCHECK(IsMediaContent(content));
218 return &(static_cast<const MediaContentDescription*>(content->description)
219 ->cryptos());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220}
221
222bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
223 const CryptoParams& crypto,
224 CryptoParams* out) {
225 for (CryptoParamsVec::const_iterator it = cryptos.begin();
226 it != cryptos.end(); ++it) {
227 if (crypto.Matches(*it)) {
228 *out = *it;
229 return true;
230 }
231 }
232 return false;
233}
234
jbauchcb560652016-08-04 05:20:32 -0700235// For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
deadbeef7914b8c2017-04-21 03:23:33 -0700236void GetSupportedAudioSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
237 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700238 if (crypto_options.enable_gcm_crypto_suites) {
239 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
240 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
241 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800242 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
243 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244}
245
deadbeef7914b8c2017-04-21 03:23:33 -0700246void GetSupportedAudioSdesCryptoSuiteNames(
247 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800248 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700249 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
250 crypto_options, crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251}
252
deadbeef7914b8c2017-04-21 03:23:33 -0700253void GetSupportedVideoSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
254 std::vector<int>* crypto_suites) {
jbauchcb560652016-08-04 05:20:32 -0700255 if (crypto_options.enable_gcm_crypto_suites) {
256 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
257 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
258 }
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800259 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260}
261
deadbeef7914b8c2017-04-21 03:23:33 -0700262void GetSupportedVideoSdesCryptoSuiteNames(
263 const rtc::CryptoOptions& crypto_options,
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800264 std::vector<std::string>* crypto_suite_names) {
deadbeef7914b8c2017-04-21 03:23:33 -0700265 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
266 crypto_options, crypto_suite_names);
267}
268
269void GetSupportedDataSdesCryptoSuites(const rtc::CryptoOptions& crypto_options,
270 std::vector<int>* crypto_suites) {
271 if (crypto_options.enable_gcm_crypto_suites) {
272 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
273 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
274 }
275 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
276}
277
278void GetSupportedDataSdesCryptoSuiteNames(
279 const rtc::CryptoOptions& crypto_options,
280 std::vector<std::string>* crypto_suite_names) {
281 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
282 crypto_options, crypto_suite_names);
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800283}
284
jbauchcb560652016-08-04 05:20:32 -0700285// Support any GCM cipher (if enabled through options). For video support only
286// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
287// because it is low overhead.
288// Pick the crypto in the list that is supported.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289static bool SelectCrypto(const MediaContentDescription* offer,
290 bool bundle,
jbauchcb560652016-08-04 05:20:32 -0700291 const rtc::CryptoOptions& crypto_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292 CryptoParams *crypto) {
293 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
294 const CryptoParamsVec& cryptos = offer->cryptos();
295
296 for (CryptoParamsVec::const_iterator i = cryptos.begin();
297 i != cryptos.end(); ++i) {
jbauchcb560652016-08-04 05:20:32 -0700298 if ((crypto_options.enable_gcm_crypto_suites &&
299 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
300 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700301 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
302 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
304 }
305 }
306 return false;
307}
308
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000310// The generated values are added to |ssrcs|.
311// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000312static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000313 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200314 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000315 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200316 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000317 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000318 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000319 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000320 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
321 ssrcs->push_back(candidate);
322 }
323}
324
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000325// Finds all StreamParams of all media types and attach them to stream_params.
326static void GetCurrentStreamParams(const SessionDescription* sdesc,
327 StreamParamsVec* stream_params) {
328 if (!sdesc)
329 return;
330
331 const ContentInfos& contents = sdesc->contents();
332 for (ContentInfos::const_iterator content = contents.begin();
333 content != contents.end(); ++content) {
334 if (!IsMediaContent(&*content)) {
335 continue;
336 }
337 const MediaContentDescription* media =
338 static_cast<const MediaContentDescription*>(
339 content->description);
340 const StreamParamsVec& streams = media->streams();
341 for (StreamParamsVec::const_iterator it = streams.begin();
342 it != streams.end(); ++it) {
343 stream_params->push_back(*it);
344 }
345 }
346}
347
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000348// Filters the data codecs for the data channel type.
349void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
350 // Filter RTP codec for SCTP and vice versa.
solenberg9fa49752016-10-08 13:02:44 -0700351 const char* codec_name =
352 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000353 for (std::vector<DataCodec>::iterator iter = codecs->begin();
354 iter != codecs->end();) {
solenberg9fa49752016-10-08 13:02:44 -0700355 if (CodecNamesEq(iter->name, codec_name)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000356 iter = codecs->erase(iter);
357 } else {
358 ++iter;
359 }
360 }
361}
362
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000363template <typename IdStruct>
364class UsedIds {
365 public:
366 UsedIds(int min_allowed_id, int max_allowed_id)
367 : min_allowed_id_(min_allowed_id),
368 max_allowed_id_(max_allowed_id),
369 next_id_(max_allowed_id) {
370 }
371
372 // Loops through all Id in |ids| and changes its id if it is
373 // already in use by another IdStruct. Call this methods with all Id
374 // in a session description to make sure no duplicate ids exists.
375 // Note that typename Id must be a type of IdStruct.
376 template <typename Id>
377 void FindAndSetIdUsed(std::vector<Id>* ids) {
378 for (typename std::vector<Id>::iterator it = ids->begin();
379 it != ids->end(); ++it) {
380 FindAndSetIdUsed(&*it);
381 }
382 }
383
384 // Finds and sets an unused id if the |idstruct| id is already in use.
385 void FindAndSetIdUsed(IdStruct* idstruct) {
386 const int original_id = idstruct->id;
387 int new_id = idstruct->id;
388
389 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
390 // If the original id is not in range - this is an id that can't be
391 // dynamically changed.
392 return;
393 }
394
395 if (IsIdUsed(original_id)) {
396 new_id = FindUnusedId();
Mirko Bonadei675513b2017-11-09 11:09:25 +0100397 RTC_LOG(LS_WARNING) << "Duplicate id found. Reassigning from "
398 << original_id << " to " << new_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000399 idstruct->id = new_id;
400 }
401 SetIdUsed(new_id);
402 }
403
404 private:
405 // Returns the first unused id in reverse order.
406 // This hopefully reduce the risk of more collisions. We want to change the
407 // default ids as little as possible.
408 int FindUnusedId() {
409 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
410 --next_id_;
411 }
nisseede5da42017-01-12 05:15:36 -0800412 RTC_DCHECK(next_id_ >= min_allowed_id_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000413 return next_id_;
414 }
415
416 bool IsIdUsed(int new_id) {
417 return id_set_.find(new_id) != id_set_.end();
418 }
419
420 void SetIdUsed(int new_id) {
421 id_set_.insert(new_id);
422 }
423
424 const int min_allowed_id_;
425 const int max_allowed_id_;
426 int next_id_;
427 std::set<int> id_set_;
428};
429
430// Helper class used for finding duplicate RTP payload types among audio, video
431// and data codecs. When bundle is used the payload types may not collide.
432class UsedPayloadTypes : public UsedIds<Codec> {
433 public:
434 UsedPayloadTypes()
435 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
436 }
437
438
439 private:
440 static const int kDynamicPayloadTypeMin = 96;
441 static const int kDynamicPayloadTypeMax = 127;
442};
443
444// Helper class used for finding duplicate RTP Header extension ids among
445// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700446class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447 public:
448 UsedRtpHeaderExtensionIds()
deadbeefe814a0d2017-02-25 18:15:09 -0800449 : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
450 webrtc::RtpExtension::kMaxId) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000451
452 private:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000453};
454
zhihuang1c378ed2017-08-17 14:10:50 -0700455// Adds a StreamParams for each SenderOptions in |sender_options| to
456// content_description.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457// |current_params| - All currently known StreamParams of any media type.
458template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700459static bool AddStreamParams(
460 const std::vector<SenderOptions>& sender_options,
461 const std::string& rtcp_cname,
462 StreamParamsVec* current_streams,
463 MediaContentDescriptionImpl<C>* content_description) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700464 // SCTP streams are not negotiated using SDP/ContentDescriptions.
deadbeef8b7e9ad2017-05-25 09:38:55 -0700465 if (IsSctp(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -0700466 return true;
467 }
468
Noah Richards2e7a0982015-05-18 14:02:54 -0700469 const bool include_rtx_streams =
470 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000472
brandtr03d5fb12016-11-22 03:37:59 -0800473 const bool include_flexfec_stream =
474 ContainsFlexfecCodec(content_description->codecs());
475
zhihuang1c378ed2017-08-17 14:10:50 -0700476 for (const SenderOptions& sender : sender_options) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000477 // groupid is empty for StreamParams generated using
478 // MediaSessionDescriptionFactory.
zhihuang1c378ed2017-08-17 14:10:50 -0700479 StreamParams* param =
480 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000481 if (!param) {
zhihuang1c378ed2017-08-17 14:10:50 -0700482 // This is a new sender.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200483 std::vector<uint32_t> ssrcs;
zhihuang1c378ed2017-08-17 14:10:50 -0700484 GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000485 StreamParams stream_param;
zhihuang1c378ed2017-08-17 14:10:50 -0700486 stream_param.id = sender.track_id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000487 // Add the generated ssrc.
488 for (size_t i = 0; i < ssrcs.size(); ++i) {
489 stream_param.ssrcs.push_back(ssrcs[i]);
490 }
zhihuang1c378ed2017-08-17 14:10:50 -0700491 if (sender.num_sim_layers > 1) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000492 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
493 stream_param.ssrc_groups.push_back(group);
494 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700495 // Generate extra ssrcs for include_rtx_streams case.
496 if (include_rtx_streams) {
497 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200498 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700499 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
500 &rtx_ssrcs);
501 for (size_t i = 0; i < ssrcs.size(); ++i) {
502 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
503 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000504 content_description->set_multistream(true);
505 }
brandtr03d5fb12016-11-22 03:37:59 -0800506 // Generate extra ssrc for include_flexfec_stream case.
507 if (include_flexfec_stream) {
508 // TODO(brandtr): Update when we support multistream protection.
509 if (ssrcs.size() == 1) {
510 std::vector<uint32_t> flexfec_ssrcs;
511 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
512 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
513 content_description->set_multistream(true);
514 } else if (!ssrcs.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100515 RTC_LOG(LS_WARNING)
brandtr03d5fb12016-11-22 03:37:59 -0800516 << "Our FlexFEC implementation only supports protecting "
517 << "a single media streams. This session has multiple "
518 << "media streams however, so no FlexFEC SSRC will be generated.";
519 }
520 }
zhihuang1c378ed2017-08-17 14:10:50 -0700521 stream_param.cname = rtcp_cname;
Steve Anton8ffb9c32017-08-31 15:45:38 -0700522 // TODO(steveanton): Support any number of stream ids.
523 RTC_CHECK(sender.stream_ids.size() == 1U);
524 stream_param.sync_label = sender.stream_ids[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000525 content_description->AddStream(stream_param);
526
527 // Store the new StreamParams in current_streams.
528 // This is necessary so that we can use the CNAME for other media types.
529 current_streams->push_back(stream_param);
530 } else {
deadbeef2f425aa2017-04-14 10:41:32 -0700531 // Use existing generated SSRCs/groups, but update the sync_label if
532 // necessary. This may be needed if a MediaStreamTrack was moved from one
533 // MediaStream to another.
Steve Anton8ffb9c32017-08-31 15:45:38 -0700534 // TODO(steveanton): Support any number of stream ids.
535 RTC_CHECK(sender.stream_ids.size() == 1U);
536 param->sync_label = sender.stream_ids[0];
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000537 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000538 }
539 }
540 return true;
541}
542
543// Updates the transport infos of the |sdesc| according to the given
544// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800545// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
546// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000547static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
548 SessionDescription* sdesc) {
549 // The bundle should not be empty.
550 if (!sdesc || !bundle_group.FirstContentName()) {
551 return false;
552 }
553
554 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700555 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000556 const TransportInfo* selected_transport_info =
557 sdesc->GetTransportInfoByName(selected_content_name);
558 if (!selected_transport_info) {
559 return false;
560 }
561
562 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700563 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000564 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700565 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000566 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800567 ConnectionRole selected_connection_role =
568 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000569 for (TransportInfos::iterator it =
570 sdesc->transport_infos().begin();
571 it != sdesc->transport_infos().end(); ++it) {
572 if (bundle_group.HasContentName(it->content_name) &&
573 it->content_name != selected_content_name) {
574 it->description.ice_ufrag = selected_ufrag;
575 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800576 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000577 }
578 }
579 return true;
580}
581
582// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
583// sets it to |cryptos|.
584static bool GetCryptosByName(const SessionDescription* sdesc,
585 const std::string& content_name,
586 CryptoParamsVec* cryptos) {
587 if (!sdesc || !cryptos) {
588 return false;
589 }
590
591 const ContentInfo* content = sdesc->GetContentByName(content_name);
592 if (!IsMediaContent(content) || !content->description) {
593 return false;
594 }
595
596 const MediaContentDescription* media_desc =
597 static_cast<const MediaContentDescription*>(content->description);
598 *cryptos = media_desc->cryptos();
599 return true;
600}
601
602// Predicate function used by the remove_if.
603// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
604static bool CryptoNotFound(const CryptoParams crypto,
605 const CryptoParamsVec* filter) {
606 if (filter == NULL) {
607 return true;
608 }
609 for (CryptoParamsVec::const_iterator it = filter->begin();
610 it != filter->end(); ++it) {
611 if (it->cipher_suite == crypto.cipher_suite) {
612 return false;
613 }
614 }
615 return true;
616}
617
618// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
619// which are not available in |filter|.
620static void PruneCryptos(const CryptoParamsVec& filter,
621 CryptoParamsVec* target_cryptos) {
622 if (!target_cryptos) {
623 return;
624 }
625 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
626 target_cryptos->end(),
627 bind2nd(ptr_fun(CryptoNotFound),
628 &filter)),
629 target_cryptos->end());
630}
631
deadbeefb5cb19b2015-11-23 16:39:12 -0800632static bool IsRtpProtocol(const std::string& protocol) {
633 return protocol.empty() ||
634 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
635}
636
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000637static bool IsRtpContent(SessionDescription* sdesc,
638 const std::string& content_name) {
639 bool is_rtp = false;
640 ContentInfo* content = sdesc->GetContentByName(content_name);
641 if (IsMediaContent(content)) {
642 MediaContentDescription* media_desc =
643 static_cast<MediaContentDescription*>(content->description);
644 if (!media_desc) {
645 return false;
646 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800647 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000648 }
649 return is_rtp;
650}
651
652// Updates the crypto parameters of the |sdesc| according to the given
653// |bundle_group|. The crypto parameters of all the contents within the
654// |bundle_group| should be updated to use the common subset of the
655// available cryptos.
656static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
657 SessionDescription* sdesc) {
658 // The bundle should not be empty.
659 if (!sdesc || !bundle_group.FirstContentName()) {
660 return false;
661 }
662
wu@webrtc.org78187522013-10-07 23:32:02 +0000663 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000664 // Get the common cryptos.
665 const ContentNames& content_names = bundle_group.content_names();
666 CryptoParamsVec common_cryptos;
667 for (ContentNames::const_iterator it = content_names.begin();
668 it != content_names.end(); ++it) {
669 if (!IsRtpContent(sdesc, *it)) {
670 continue;
671 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000672 // The common cryptos are needed if any of the content does not have DTLS
673 // enabled.
674 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
675 common_cryptos_needed = true;
676 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677 if (it == content_names.begin()) {
678 // Initial the common_cryptos with the first content in the bundle group.
679 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
680 return false;
681 }
682 if (common_cryptos.empty()) {
683 // If there's no crypto params, we should just return.
684 return true;
685 }
686 } else {
687 CryptoParamsVec cryptos;
688 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
689 return false;
690 }
691 PruneCryptos(cryptos, &common_cryptos);
692 }
693 }
694
wu@webrtc.org78187522013-10-07 23:32:02 +0000695 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000696 return false;
697 }
698
699 // Update to use the common cryptos.
700 for (ContentNames::const_iterator it = content_names.begin();
701 it != content_names.end(); ++it) {
702 if (!IsRtpContent(sdesc, *it)) {
703 continue;
704 }
705 ContentInfo* content = sdesc->GetContentByName(*it);
706 if (IsMediaContent(content)) {
707 MediaContentDescription* media_desc =
708 static_cast<MediaContentDescription*>(content->description);
709 if (!media_desc) {
710 return false;
711 }
712 media_desc->set_cryptos(common_cryptos);
713 }
714 }
715 return true;
716}
717
718template <class C>
719static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 03:37:59 -0800720 for (const auto& codec : codecs) {
721 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 return true;
723 }
724 }
725 return false;
726}
727
728template <class C>
729static bool IsRtxCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800730 return STR_CASE_CMP(codec.name.c_str(), kRtxCodecName) == 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731}
732
brandtr03d5fb12016-11-22 03:37:59 -0800733template <class C>
734static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
735 for (const auto& codec : codecs) {
736 if (IsFlexfecCodec(codec)) {
737 return true;
738 }
739 }
740 return false;
741}
742
743template <class C>
744static bool IsFlexfecCodec(const C& codec) {
nisse21e4e0b2017-02-20 05:01:01 -0800745 return STR_CASE_CMP(codec.name.c_str(), kFlexfecCodecName) == 0;
brandtr03d5fb12016-11-22 03:37:59 -0800746}
747
zhihuang1c378ed2017-08-17 14:10:50 -0700748// Create a media content to be offered for the given |sender_options|,
749// according to the given options.rtcp_mux, session_options.is_muc, codecs,
750// secure_transport, crypto, and current_streams. If we don't currently have
751// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
752// created (according to crypto_suites). The created content is added to the
753// offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754template <class C>
755static bool CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -0700756 const std::vector<SenderOptions>& sender_options,
757 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000758 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000759 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000760 const CryptoParamsVec* current_cryptos,
761 const std::vector<std::string>& crypto_suites,
762 const RtpHeaderExtensions& rtp_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000763 StreamParamsVec* current_streams,
764 MediaContentDescriptionImpl<C>* offer) {
765 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000766
zhihuang1c378ed2017-08-17 14:10:50 -0700767 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700768 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
769 offer->set_rtcp_reduced_size(true);
770 }
zhihuang1c378ed2017-08-17 14:10:50 -0700771 offer->set_multistream(session_options.is_muc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000772 offer->set_rtp_header_extensions(rtp_extensions);
773
zhihuang1c378ed2017-08-17 14:10:50 -0700774 if (!AddStreamParams(sender_options, session_options.rtcp_cname,
775 current_streams, offer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000776 return false;
777 }
778
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000779 if (secure_policy != SEC_DISABLED) {
780 if (current_cryptos) {
781 AddMediaCryptos(*current_cryptos, offer);
782 }
783 if (offer->cryptos().empty()) {
784 if (!CreateMediaCryptos(crypto_suites, offer)) {
785 return false;
786 }
787 }
788 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000789
deadbeef7af91dd2016-12-13 11:29:11 -0800790 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000791 return false;
792 }
793 return true;
794}
795
796template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000797static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
magjedb05fa242016-11-11 04:00:16 -0800798 const int codec1_id,
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000799 const std::vector<C>& codecs2,
magjedb05fa242016-11-11 04:00:16 -0800800 const int codec2_id) {
801 const C* codec1 = FindCodecById(codecs1, codec1_id);
802 const C* codec2 = FindCodecById(codecs2, codec2_id);
803 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000804}
805
806template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000807static void NegotiateCodecs(const std::vector<C>& local_codecs,
808 const std::vector<C>& offered_codecs,
809 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800810 for (const C& ours : local_codecs) {
811 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700812 // Note that we intentionally only find one matching codec for each of our
813 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800814 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
815 C negotiated = ours;
816 negotiated.IntersectFeedbackParams(theirs);
817 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 04:00:16 -0800818 const auto apt_it =
819 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800820 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 04:00:16 -0800821 RTC_DCHECK(apt_it != theirs.params.end());
822 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000823 }
magjedf823ede2016-11-12 09:53:04 -0800824 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
825 webrtc::H264::GenerateProfileLevelIdForAnswer(
826 ours.params, theirs.params, &negotiated.params);
827 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800828 negotiated.id = theirs.id;
ossu075af922016-06-14 03:29:38 -0700829 negotiated.name = theirs.name;
magjedb05fa242016-11-11 04:00:16 -0800830 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000831 }
832 }
deadbeef67cf2c12016-04-13 10:07:16 -0700833 // RFC3264: Although the answerer MAY list the formats in their desired
834 // order of preference, it is RECOMMENDED that unless there is a
835 // specific reason, the answerer list formats in the same relative order
836 // they were present in the offer.
837 std::unordered_map<int, int> payload_type_preferences;
838 int preference = static_cast<int>(offered_codecs.size() + 1);
839 for (const C& codec : offered_codecs) {
840 payload_type_preferences[codec.id] = preference--;
841 }
842 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
843 [&payload_type_preferences](const C& a, const C& b) {
844 return payload_type_preferences[a.id] >
845 payload_type_preferences[b.id];
846 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000847}
848
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800849// Finds a codec in |codecs2| that matches |codec_to_match|, which is
850// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
851// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800853static bool FindMatchingCodec(const std::vector<C>& codecs1,
854 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000855 const C& codec_to_match,
856 C* found_codec) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -0700857 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
858 // codecs' associated codecs correctly. If not, that's a programming error.
859 RTC_DCHECK(std::find_if(codecs1.begin(), codecs1.end(),
860 [&codec_to_match](const C& codec) {
861 return &codec == &codec_to_match;
862 }) != codecs1.end());
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800863 for (const C& potential_match : codecs2) {
864 if (potential_match.Matches(codec_to_match)) {
865 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 04:00:16 -0800866 int apt_value_1 = 0;
867 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800868 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
869 &apt_value_1) ||
870 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
871 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100872 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800873 continue;
874 }
875 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
876 apt_value_2)) {
877 continue;
878 }
879 }
880 if (found_codec) {
881 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000882 }
883 return true;
884 }
885 }
886 return false;
887}
888
zhihuang1c378ed2017-08-17 14:10:50 -0700889// Find the codec in |codec_list| that |rtx_codec| is associated with.
890template <class C>
891static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
892 const C& rtx_codec) {
893 std::string associated_pt_str;
894 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
895 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100896 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
897 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 14:10:50 -0700898 return nullptr;
899 }
900
901 int associated_pt;
902 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100903 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
904 << " of RTX codec " << rtx_codec.name
905 << " to an integer.";
zhihuang1c378ed2017-08-17 14:10:50 -0700906 return nullptr;
907 }
908
909 // Find the associated reference codec for the reference RTX codec.
910 const C* associated_codec = FindCodecById(codec_list, associated_pt);
911 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100912 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
913 << associated_pt << " for RTX codec " << rtx_codec.name
914 << ".";
zhihuang1c378ed2017-08-17 14:10:50 -0700915 }
916 return associated_codec;
917}
918
919// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000920// already exist in |offered_codecs| and ensure the payload types don't
921// collide.
922template <class C>
zhihuang1c378ed2017-08-17 14:10:50 -0700923static void MergeCodecs(const std::vector<C>& reference_codecs,
924 std::vector<C>* offered_codecs,
925 UsedPayloadTypes* used_pltypes) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000926 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800927 for (const C& reference_codec : reference_codecs) {
928 if (!IsRtxCodec(reference_codec) &&
929 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
930 reference_codec, nullptr)) {
931 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000932 used_pltypes->FindAndSetIdUsed(&codec);
933 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000934 }
935 }
936
937 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800938 for (const C& reference_codec : reference_codecs) {
939 if (IsRtxCodec(reference_codec) &&
940 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
941 reference_codec, nullptr)) {
942 C rtx_codec = reference_codec;
olka3c747662017-08-17 06:50:32 -0700943 const C* associated_codec =
zhihuang1c378ed2017-08-17 14:10:50 -0700944 GetAssociatedCodec(reference_codecs, rtx_codec);
olka3c747662017-08-17 06:50:32 -0700945 if (!associated_codec) {
olka3c747662017-08-17 06:50:32 -0700946 continue;
947 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800948 // Find a codec in the offered list that matches the reference codec.
949 // Its payload type may be different than the reference codec.
950 C matching_codec;
951 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
magjedb05fa242016-11-11 04:00:16 -0800952 *associated_codec, &matching_codec)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100953 RTC_LOG(LS_WARNING)
954 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800955 continue;
956 }
957
958 rtx_codec.params[kCodecParamAssociatedPayloadType] =
959 rtc::ToString(matching_codec.id);
960 used_pltypes->FindAndSetIdUsed(&rtx_codec);
961 offered_codecs->push_back(rtx_codec);
962 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000963 }
964}
965
zhihuang1c378ed2017-08-17 14:10:50 -0700966static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
967 const webrtc::RtpExtension& ext_to_match,
968 webrtc::RtpExtension* found_extension) {
969 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
970 it != extensions.end(); ++it) {
971 // We assume that all URIs are given in a canonical format.
972 if (it->uri == ext_to_match.uri && it->encrypt == ext_to_match.encrypt) {
973 if (found_extension) {
974 *found_extension = *it;
975 }
976 return true;
977 }
978 }
979 return false;
980}
981
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000982static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700983 const webrtc::RtpExtension& ext_to_match,
984 webrtc::RtpExtension* found_extension) {
jbauch5869f502017-06-29 12:31:36 -0700985 // We assume that all URIs are given in a canonical format.
986 const webrtc::RtpExtension* found =
987 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
988 ext_to_match.uri);
989 if (!found) {
990 return false;
991 }
992 if (found_extension) {
993 *found_extension = *found;
994 }
995 return true;
996}
997
998static bool FindByUriWithEncryptionPreference(
999 const RtpHeaderExtensions& extensions,
1000 const webrtc::RtpExtension& ext_to_match, bool encryption_preference,
1001 webrtc::RtpExtension* found_extension) {
1002 const webrtc::RtpExtension* unencrypted_extension = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001003 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
1004 it != extensions.end(); ++it) {
1005 // We assume that all URIs are given in a canonical format.
1006 if (it->uri == ext_to_match.uri) {
jbauch5869f502017-06-29 12:31:36 -07001007 if (!encryption_preference || it->encrypt) {
1008 if (found_extension) {
1009 *found_extension = *it;
1010 }
1011 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001012 }
jbauch5869f502017-06-29 12:31:36 -07001013 unencrypted_extension = &(*it);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001014 }
1015 }
jbauch5869f502017-06-29 12:31:36 -07001016 if (unencrypted_extension) {
1017 if (found_extension) {
1018 *found_extension = *unencrypted_extension;
1019 }
1020 return true;
1021 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001022 return false;
1023}
1024
zhihuang1c378ed2017-08-17 14:10:50 -07001025// Adds all extensions from |reference_extensions| to |offered_extensions| that
1026// don't already exist in |offered_extensions| and ensure the IDs don't
1027// collide. If an extension is added, it's also added to |regular_extensions| or
1028// |encrypted_extensions|, and if the extension is in |regular_extensions| or
1029// |encrypted_extensions|, its ID is marked as used in |used_ids|.
1030// |offered_extensions| is for either audio or video while |regular_extensions|
1031// and |encrypted_extensions| are used for both audio and video. There could be
1032// overlap between audio extensions and video extensions.
1033static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1034 RtpHeaderExtensions* offered_extensions,
1035 RtpHeaderExtensions* regular_extensions,
1036 RtpHeaderExtensions* encrypted_extensions,
1037 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 06:50:32 -07001038 for (auto reference_extension : reference_extensions) {
zhihuang1c378ed2017-08-17 14:10:50 -07001039 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
1040 nullptr)) {
olka3c747662017-08-17 06:50:32 -07001041 webrtc::RtpExtension existing;
zhihuang1c378ed2017-08-17 14:10:50 -07001042 if (reference_extension.encrypt) {
1043 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
1044 &existing)) {
1045 offered_extensions->push_back(existing);
1046 } else {
1047 used_ids->FindAndSetIdUsed(&reference_extension);
1048 encrypted_extensions->push_back(reference_extension);
1049 offered_extensions->push_back(reference_extension);
1050 }
olka3c747662017-08-17 06:50:32 -07001051 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07001052 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
1053 &existing)) {
1054 offered_extensions->push_back(existing);
1055 } else {
1056 used_ids->FindAndSetIdUsed(&reference_extension);
1057 regular_extensions->push_back(reference_extension);
1058 offered_extensions->push_back(reference_extension);
1059 }
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001060 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001061 }
1062 }
1063}
1064
jbauch5869f502017-06-29 12:31:36 -07001065static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1066 RtpHeaderExtensions* all_extensions,
1067 UsedRtpHeaderExtensionIds* used_ids) {
1068 RtpHeaderExtensions encrypted_extensions;
1069 for (const webrtc::RtpExtension& extension : *extensions) {
1070 webrtc::RtpExtension existing;
1071 // Don't add encrypted extensions again that were already included in a
1072 // previous offer or regular extensions that are also included as encrypted
1073 // extensions.
1074 if (extension.encrypt ||
1075 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1076 (FindByUriWithEncryptionPreference(*extensions, extension, true,
1077 &existing) && existing.encrypt)) {
1078 continue;
1079 }
1080
1081 if (FindByUri(*all_extensions, extension, &existing)) {
1082 encrypted_extensions.push_back(existing);
1083 } else {
1084 webrtc::RtpExtension encrypted(extension);
1085 encrypted.encrypt = true;
1086 used_ids->FindAndSetIdUsed(&encrypted);
1087 all_extensions->push_back(encrypted);
1088 encrypted_extensions.push_back(encrypted);
1089 }
1090 }
1091 extensions->insert(extensions->end(), encrypted_extensions.begin(),
1092 encrypted_extensions.end());
1093}
1094
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001095static void NegotiateRtpHeaderExtensions(
1096 const RtpHeaderExtensions& local_extensions,
1097 const RtpHeaderExtensions& offered_extensions,
jbauch5869f502017-06-29 12:31:36 -07001098 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 RtpHeaderExtensions* negotiated_extenstions) {
1100 RtpHeaderExtensions::const_iterator ours;
1101 for (ours = local_extensions.begin();
1102 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -07001103 webrtc::RtpExtension theirs;
jbauch5869f502017-06-29 12:31:36 -07001104 if (FindByUriWithEncryptionPreference(offered_extensions, *ours,
1105 enable_encrypted_rtp_header_extensions, &theirs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001106 // We respond with their RTP header extension id.
1107 negotiated_extenstions->push_back(theirs);
1108 }
1109 }
1110}
1111
1112static void StripCNCodecs(AudioCodecs* audio_codecs) {
1113 AudioCodecs::iterator iter = audio_codecs->begin();
1114 while (iter != audio_codecs->end()) {
nisse21e4e0b2017-02-20 05:01:01 -08001115 if (STR_CASE_CMP(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001116 iter = audio_codecs->erase(iter);
1117 } else {
1118 ++iter;
1119 }
1120 }
1121}
1122
zhihuang1c378ed2017-08-17 14:10:50 -07001123// Create a media content to be answered for the given |sender_options|
1124// according to the given session_options.rtcp_mux, session_options.streams,
1125// codecs, crypto, and current_streams. If we don't currently have crypto (in
1126// current_cryptos) and it is enabled (in secure_policy), crypto is created
1127// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1128// negotiated with the offer. If the negotiation fails, this method returns
1129// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001130template <class C>
1131static bool CreateMediaContentAnswer(
1132 const MediaContentDescriptionImpl<C>* offer,
zhihuang1c378ed2017-08-17 14:10:50 -07001133 const MediaDescriptionOptions& media_description_options,
1134 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001135 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001136 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001137 const CryptoParamsVec* current_cryptos,
1138 const RtpHeaderExtensions& local_rtp_extenstions,
jbauch5869f502017-06-29 12:31:36 -07001139 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001140 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141 bool bundle_enabled,
1142 MediaContentDescriptionImpl<C>* answer) {
1143 std::vector<C> negotiated_codecs;
1144 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1145 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001146 answer->set_protocol(offer->protocol());
1147 RtpHeaderExtensions negotiated_rtp_extensions;
1148 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1149 offer->rtp_header_extensions(),
jbauch5869f502017-06-29 12:31:36 -07001150 enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001151 &negotiated_rtp_extensions);
1152 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1153
zhihuang1c378ed2017-08-17 14:10:50 -07001154 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001155 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1156 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1157 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001158
1159 if (sdes_policy != SEC_DISABLED) {
1160 CryptoParams crypto;
zhihuang1c378ed2017-08-17 14:10:50 -07001161 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1162 &crypto)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001163 if (current_cryptos) {
1164 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1165 }
1166 answer->AddCrypto(crypto);
1167 }
1168 }
1169
deadbeef7af91dd2016-12-13 11:29:11 -08001170 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001171 return false;
1172 }
1173
zhihuang1c378ed2017-08-17 14:10:50 -07001174 if (!AddStreamParams(media_description_options.sender_options,
1175 session_options.rtcp_cname, current_streams, answer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001176 return false; // Something went seriously wrong.
1177 }
1178
ossu075af922016-06-14 03:29:38 -07001179 auto offer_rtd =
Steve Anton1d03a752017-11-27 14:30:09 -08001180 RtpTransceiverDirectionFromMediaContentDirection(offer->direction());
zhihuang1c378ed2017-08-17 14:10:50 -07001181
Steve Anton1d03a752017-11-27 14:30:09 -08001182 answer->set_direction(
1183 cricket::MediaContentDirectionFromRtpTransceiverDirection(
1184 NegotiateRtpTransceiverDirection(
1185 offer_rtd, media_description_options.direction)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001186 return true;
1187}
1188
1189static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001190 const std::string& protocol,
1191 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001192 // Since not all applications serialize and deserialize the media protocol,
1193 // we will have to accept |protocol| to be empty.
1194 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001195 return true;
1196 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001197
zhihuangcf5b37c2016-05-05 11:44:35 -07001198 if (type == MEDIA_TYPE_DATA) {
1199 // Check for SCTP, but also for RTP for RTP-based data channels.
1200 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1201 if (secure_transport) {
1202 // Most likely scenarios first.
1203 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1204 IsPlainRtp(protocol);
1205 } else {
1206 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1207 }
1208 }
1209
1210 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1211 // JSEP specifies.
1212 if (secure_transport) {
1213 // Most likely scenarios first.
1214 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1215 } else {
1216 return IsPlainRtp(protocol);
1217 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001218}
1219
1220static void SetMediaProtocol(bool secure_transport,
1221 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001222 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001223 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001224 else if (secure_transport)
1225 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001226 else
1227 desc->set_protocol(kMediaProtocolAvpf);
1228}
1229
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001230// Gets the TransportInfo of the given |content_name| from the
1231// |current_description|. If doesn't exist, returns a new one.
1232static const TransportDescription* GetTransportDescription(
1233 const std::string& content_name,
1234 const SessionDescription* current_description) {
1235 const TransportDescription* desc = NULL;
1236 if (current_description) {
1237 const TransportInfo* info =
1238 current_description->GetTransportInfoByName(content_name);
1239 if (info) {
1240 desc = &info->description;
1241 }
1242 }
1243 return desc;
1244}
1245
1246// Gets the current DTLS state from the transport description.
zhihuang1c378ed2017-08-17 14:10:50 -07001247static bool IsDtlsActive(const ContentInfo* content,
1248 const SessionDescription* current_description) {
1249 if (!content) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001250 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001251 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001252
zhihuang1c378ed2017-08-17 14:10:50 -07001253 size_t msection_index = content - &current_description->contents()[0];
1254
1255 if (current_description->transport_infos().size() <= msection_index) {
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001256 return false;
zhihuang1c378ed2017-08-17 14:10:50 -07001257 }
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001258
zhihuang1c378ed2017-08-17 14:10:50 -07001259 return current_description->transport_infos()[msection_index]
1260 .description.secure();
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001261}
1262
ossu075af922016-06-14 03:29:38 -07001263std::string MediaContentDirectionToString(MediaContentDirection direction) {
1264 std::string dir_str;
1265 switch (direction) {
1266 case MD_INACTIVE:
1267 dir_str = "inactive";
1268 break;
1269 case MD_SENDONLY:
1270 dir_str = "sendonly";
1271 break;
1272 case MD_RECVONLY:
1273 dir_str = "recvonly";
1274 break;
1275 case MD_SENDRECV:
1276 dir_str = "sendrecv";
1277 break;
1278 default:
nissec80e7412017-01-11 05:56:46 -08001279 RTC_NOTREACHED();
ossu075af922016-06-14 03:29:38 -07001280 break;
1281 }
1282
1283 return dir_str;
1284}
1285
Steve Anton8ffb9c32017-08-31 15:45:38 -07001286void MediaDescriptionOptions::AddAudioSender(
1287 const std::string& track_id,
1288 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 14:10:50 -07001289 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001290 AddSenderInternal(track_id, stream_ids, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001291}
1292
Steve Anton8ffb9c32017-08-31 15:45:38 -07001293void MediaDescriptionOptions::AddVideoSender(
1294 const std::string& track_id,
1295 const std::vector<std::string>& stream_ids,
1296 int num_sim_layers) {
zhihuang1c378ed2017-08-17 14:10:50 -07001297 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001298 AddSenderInternal(track_id, stream_ids, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001299}
1300
zhihuang1c378ed2017-08-17 14:10:50 -07001301void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1302 const std::string& stream_id) {
1303 RTC_DCHECK(type == MEDIA_TYPE_DATA);
Steve Anton8ffb9c32017-08-31 15:45:38 -07001304 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1305 // than one stream?
1306 AddSenderInternal(track_id, {stream_id}, 1);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001307}
1308
Steve Anton8ffb9c32017-08-31 15:45:38 -07001309void MediaDescriptionOptions::AddSenderInternal(
1310 const std::string& track_id,
1311 const std::vector<std::string>& stream_ids,
1312 int num_sim_layers) {
1313 // TODO(steveanton): Support any number of stream ids.
1314 RTC_CHECK(stream_ids.size() == 1U);
1315 sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers});
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001316}
1317
zhihuang1c378ed2017-08-17 14:10:50 -07001318bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1319 return std::find_if(media_description_options.begin(),
1320 media_description_options.end(),
1321 [type](const MediaDescriptionOptions& t) {
1322 return t.type == type;
1323 }) != media_description_options.end();
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001324}
1325
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001326MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1327 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001328 : transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001329
1330MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1331 ChannelManager* channel_manager,
1332 const TransportDescriptionFactory* transport_desc_factory)
zhihuang1c378ed2017-08-17 14:10:50 -07001333 : transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 07:12:39 -07001334 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1335 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001336 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
magjed3cf8ece2016-11-10 03:36:53 -08001337 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001338 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1339 channel_manager->GetSupportedDataCodecs(&data_codecs_);
zhihuang1c378ed2017-08-17 14:10:50 -07001340 ComputeAudioCodecsIntersectionAndUnion();
ossu075af922016-06-14 03:29:38 -07001341}
1342
ossudedfd282016-06-14 07:12:39 -07001343const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1344 const {
ossu075af922016-06-14 03:29:38 -07001345 return audio_sendrecv_codecs_;
1346}
1347
1348const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1349 return audio_send_codecs_;
1350}
1351
1352const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1353 return audio_recv_codecs_;
1354}
1355
1356void MediaSessionDescriptionFactory::set_audio_codecs(
1357 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1358 audio_send_codecs_ = send_codecs;
1359 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 14:10:50 -07001360 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001361}
1362
1363SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001364 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001366 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001367
1368 StreamParamsVec current_streams;
1369 GetCurrentStreamParams(current_description, &current_streams);
1370
zhihuang1c378ed2017-08-17 14:10:50 -07001371 AudioCodecs offer_audio_codecs;
1372 VideoCodecs offer_video_codecs;
1373 DataCodecs offer_data_codecs;
1374 GetCodecsForOffer(current_description, &offer_audio_codecs,
1375 &offer_video_codecs, &offer_data_codecs);
ossu075af922016-06-14 03:29:38 -07001376
zhihuang1c378ed2017-08-17 14:10:50 -07001377 if (!session_options.vad_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001378 // If application doesn't want CN codecs in offer.
zhihuang1c378ed2017-08-17 14:10:50 -07001379 StripCNCodecs(&offer_audio_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 }
zhihuang1c378ed2017-08-17 14:10:50 -07001381 FilterDataCodecs(&offer_data_codecs,
1382 session_options.data_channel_type == DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001383
1384 RtpHeaderExtensions audio_rtp_extensions;
1385 RtpHeaderExtensions video_rtp_extensions;
1386 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1387 &video_rtp_extensions);
1388
zhihuang1c378ed2017-08-17 14:10:50 -07001389 // Must have options for each existing section.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001390 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001391 RTC_DCHECK(current_description->contents().size() <=
1392 session_options.media_description_options.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001393 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001394
zhihuang1c378ed2017-08-17 14:10:50 -07001395 // Iterate through the media description options, matching with existing media
1396 // descriptions in |current_description|.
1397 int msection_index = 0;
1398 for (const MediaDescriptionOptions& media_description_options :
1399 session_options.media_description_options) {
1400 const ContentInfo* current_content = nullptr;
1401 if (current_description &&
1402 msection_index <
1403 static_cast<int>(current_description->contents().size())) {
1404 current_content = &current_description->contents()[msection_index];
1405 // Media type must match.
1406 RTC_DCHECK(IsMediaContentOfType(current_content,
1407 media_description_options.type));
1408 }
1409 switch (media_description_options.type) {
1410 case MEDIA_TYPE_AUDIO:
1411 if (!AddAudioContentForOffer(media_description_options, session_options,
1412 current_content, current_description,
1413 audio_rtp_extensions, offer_audio_codecs,
1414 &current_streams, offer.get())) {
1415 return nullptr;
1416 }
1417 break;
1418 case MEDIA_TYPE_VIDEO:
1419 if (!AddVideoContentForOffer(media_description_options, session_options,
1420 current_content, current_description,
1421 video_rtp_extensions, offer_video_codecs,
1422 &current_streams, offer.get())) {
1423 return nullptr;
1424 }
1425 break;
1426 case MEDIA_TYPE_DATA:
1427 if (!AddDataContentForOffer(media_description_options, session_options,
1428 current_content, current_description,
1429 offer_data_codecs, &current_streams,
1430 offer.get())) {
1431 return nullptr;
1432 }
1433 break;
1434 default:
1435 RTC_NOTREACHED();
1436 }
1437 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001438 }
1439
1440 // Bundle the contents together, if we've been asked to do so, and update any
1441 // parameters that need to be tweaked for BUNDLE.
zhihuang1c378ed2017-08-17 14:10:50 -07001442 if (session_options.bundle_enabled && offer->contents().size() > 0u) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001443 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 14:10:50 -07001444 for (const ContentInfo& content : offer->contents()) {
1445 // TODO(deadbeef): There are conditions that make bundling two media
1446 // descriptions together illegal. For example, they use the same payload
1447 // type to represent different codecs, or same IDs for different header
1448 // extensions. We need to detect this and not try to bundle those media
1449 // descriptions together.
1450 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001451 }
1452 offer->AddGroup(offer_bundle);
1453 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001454 RTC_LOG(LS_ERROR)
1455 << "CreateOffer failed to UpdateTransportInfoForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001456 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001457 }
1458 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001459 RTC_LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
zhihuang1c378ed2017-08-17 14:10:50 -07001460 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001461 }
1462 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001463 return offer.release();
1464}
1465
1466SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07001467 const SessionDescription* offer,
1468 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001469 const SessionDescription* current_description) const {
deadbeefb7892532017-02-22 19:35:18 -08001470 if (!offer) {
1471 return nullptr;
1472 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001473 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001474 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1475 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001476 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001477
1478 StreamParamsVec current_streams;
1479 GetCurrentStreamParams(current_description, &current_streams);
1480
deadbeefb7892532017-02-22 19:35:18 -08001481 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1482 // group in the answer with the appropriate content names.
1483 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1484 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1485 // Transport info shared by the bundle group.
1486 std::unique_ptr<TransportInfo> bundle_transport;
1487
zhihuang1c378ed2017-08-17 14:10:50 -07001488 // Get list of all possible codecs that respects existing payload type
1489 // mappings and uses a single payload type space.
1490 //
1491 // Note that these lists may be further filtered for each m= section; this
1492 // step is done just to establish the payload type mappings shared by all
1493 // sections.
1494 AudioCodecs answer_audio_codecs;
1495 VideoCodecs answer_video_codecs;
1496 DataCodecs answer_data_codecs;
1497 GetCodecsForAnswer(current_description, offer, &answer_audio_codecs,
1498 &answer_video_codecs, &answer_data_codecs);
1499
1500 if (!session_options.vad_enabled) {
1501 // If application doesn't want CN codecs in answer.
1502 StripCNCodecs(&answer_audio_codecs);
1503 }
1504 FilterDataCodecs(&answer_data_codecs,
1505 session_options.data_channel_type == DCT_SCTP);
1506
1507 // Must have options for exactly as many sections as in the offer.
1508 RTC_DCHECK(offer->contents().size() ==
1509 session_options.media_description_options.size());
1510 // Iterate through the media description options, matching with existing
1511 // media descriptions in |current_description|.
1512 int msection_index = 0;
1513 for (const MediaDescriptionOptions& media_description_options :
1514 session_options.media_description_options) {
1515 const ContentInfo* offer_content = &offer->contents()[msection_index];
1516 // Media types and MIDs must match between the remote offer and the
1517 // MediaDescriptionOptions.
1518 RTC_DCHECK(
1519 IsMediaContentOfType(offer_content, media_description_options.type));
1520 RTC_DCHECK(media_description_options.mid == offer_content->name);
1521 const ContentInfo* current_content = nullptr;
1522 if (current_description &&
1523 msection_index <
1524 static_cast<int>(current_description->contents().size())) {
1525 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-22 19:35:18 -08001526 }
zhihuang1c378ed2017-08-17 14:10:50 -07001527 switch (media_description_options.type) {
1528 case MEDIA_TYPE_AUDIO:
1529 if (!AddAudioContentForAnswer(
1530 media_description_options, session_options, offer_content,
1531 offer, current_content, current_description,
1532 bundle_transport.get(), answer_audio_codecs, &current_streams,
1533 answer.get())) {
1534 return nullptr;
1535 }
1536 break;
1537 case MEDIA_TYPE_VIDEO:
1538 if (!AddVideoContentForAnswer(
1539 media_description_options, session_options, offer_content,
1540 offer, current_content, current_description,
1541 bundle_transport.get(), answer_video_codecs, &current_streams,
1542 answer.get())) {
1543 return nullptr;
1544 }
1545 break;
1546 case MEDIA_TYPE_DATA:
1547 if (!AddDataContentForAnswer(media_description_options, session_options,
1548 offer_content, offer, current_content,
1549 current_description,
1550 bundle_transport.get(), answer_data_codecs,
1551 &current_streams, answer.get())) {
1552 return nullptr;
1553 }
1554 break;
1555 default:
1556 RTC_NOTREACHED();
1557 }
1558 ++msection_index;
deadbeefb7892532017-02-22 19:35:18 -08001559 // See if we can add the newly generated m= section to the BUNDLE group in
1560 // the answer.
1561 ContentInfo& added = answer->contents().back();
zhihuang1c378ed2017-08-17 14:10:50 -07001562 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
deadbeefb7892532017-02-22 19:35:18 -08001563 offer_bundle->HasContentName(added.name)) {
1564 answer_bundle.AddContentName(added.name);
1565 bundle_transport.reset(
1566 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001567 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001568 }
1569
deadbeefb7892532017-02-22 19:35:18 -08001570 // Only put BUNDLE group in answer if nonempty.
1571 if (answer_bundle.FirstContentName()) {
1572 answer->AddGroup(answer_bundle);
1573
1574 // Share the same ICE credentials and crypto params across all contents,
1575 // as BUNDLE requires.
1576 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001577 RTC_LOG(LS_ERROR)
1578 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001579 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001580 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001581
deadbeefb7892532017-02-22 19:35:18 -08001582 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001583 RTC_LOG(LS_ERROR)
1584 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
deadbeefb7892532017-02-22 19:35:18 -08001585 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001586 }
1587 }
1588
1589 return answer.release();
1590}
1591
ossu075af922016-06-14 03:29:38 -07001592const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1593 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001594 switch (direction) {
1595 // If stream is inactive - generate list as if sendrecv.
1596 case RtpTransceiverDirection::kSendRecv:
1597 case RtpTransceiverDirection::kInactive:
1598 return audio_sendrecv_codecs_;
1599 case RtpTransceiverDirection::kSendOnly:
1600 return audio_send_codecs_;
1601 case RtpTransceiverDirection::kRecvOnly:
1602 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001603 }
Steve Anton1d03a752017-11-27 14:30:09 -08001604 RTC_NOTREACHED();
1605 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001606}
1607
1608const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1609 const RtpTransceiverDirection& offer,
1610 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 14:30:09 -08001611 switch (answer) {
1612 // For inactive and sendrecv answers, generate lists as if we were to accept
1613 // the offer's direction. See RFC 3264 Section 6.1.
1614 case RtpTransceiverDirection::kSendRecv:
1615 case RtpTransceiverDirection::kInactive:
1616 return GetAudioCodecsForOffer(
1617 webrtc::RtpTransceiverDirectionReversed(offer));
1618 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 03:29:38 -07001619 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 14:30:09 -08001620 case RtpTransceiverDirection::kRecvOnly:
1621 return audio_recv_codecs_;
ossu075af922016-06-14 03:29:38 -07001622 }
Steve Anton1d03a752017-11-27 14:30:09 -08001623 RTC_NOTREACHED();
1624 return audio_sendrecv_codecs_;
ossu075af922016-06-14 03:29:38 -07001625}
1626
zhihuang1c378ed2017-08-17 14:10:50 -07001627void MergeCodecsFromDescription(const SessionDescription* description,
1628 AudioCodecs* audio_codecs,
1629 VideoCodecs* video_codecs,
1630 DataCodecs* data_codecs,
1631 UsedPayloadTypes* used_pltypes) {
1632 RTC_DCHECK(description);
1633 for (const ContentInfo& content : description->contents()) {
1634 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1635 const AudioContentDescription* audio =
1636 static_cast<AudioContentDescription*>(content.description);
1637 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1638 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1639 const VideoContentDescription* video =
1640 static_cast<VideoContentDescription*>(content.description);
1641 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1642 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1643 const DataContentDescription* data =
1644 static_cast<DataContentDescription*>(content.description);
1645 MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
1646 }
1647 }
1648}
1649
1650// Getting codecs for an offer involves these steps:
1651//
1652// 1. Construct payload type -> codec mappings for current description.
1653// 2. Add any reference codecs that weren't already present
1654// 3. For each individual media description (m= section), filter codecs based
1655// on the directional attribute (happens in another method).
1656void MediaSessionDescriptionFactory::GetCodecsForOffer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001657 const SessionDescription* current_description,
1658 AudioCodecs* audio_codecs,
1659 VideoCodecs* video_codecs,
1660 DataCodecs* data_codecs) const {
1661 UsedPayloadTypes used_pltypes;
1662 audio_codecs->clear();
1663 video_codecs->clear();
1664 data_codecs->clear();
1665
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001666 // First - get all codecs from the current description if the media type
zhihuang1c378ed2017-08-17 14:10:50 -07001667 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1668 // new media type is added.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001669 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001670 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1671 data_codecs, &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001672 }
1673
1674 // Add our codecs that are not in |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001675 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1676 MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1677 MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1678}
1679
1680// Getting codecs for an answer involves these steps:
1681//
1682// 1. Construct payload type -> codec mappings for current description.
1683// 2. Add any codecs from the offer that weren't already present.
1684// 3. Add any remaining codecs that weren't already present.
1685// 4. For each individual media description (m= section), filter codecs based
1686// on the directional attribute (happens in another method).
1687void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1688 const SessionDescription* current_description,
1689 const SessionDescription* remote_offer,
1690 AudioCodecs* audio_codecs,
1691 VideoCodecs* video_codecs,
1692 DataCodecs* data_codecs) const {
1693 UsedPayloadTypes used_pltypes;
1694 audio_codecs->clear();
1695 video_codecs->clear();
1696 data_codecs->clear();
1697
1698 // First - get all codecs from the current description if the media type
1699 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1700 // new media type is added.
1701 if (current_description) {
1702 MergeCodecsFromDescription(current_description, audio_codecs, video_codecs,
1703 data_codecs, &used_pltypes);
1704 }
1705
1706 // Second - filter out codecs that we don't support at all and should ignore.
1707 AudioCodecs filtered_offered_audio_codecs;
1708 VideoCodecs filtered_offered_video_codecs;
1709 DataCodecs filtered_offered_data_codecs;
1710 for (const ContentInfo& content : remote_offer->contents()) {
1711 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1712 const AudioContentDescription* audio =
1713 static_cast<AudioContentDescription*>(content.description);
1714 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1715 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1716 filtered_offered_audio_codecs,
1717 offered_audio_codec, nullptr) &&
1718 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1719 offered_audio_codec, nullptr)) {
1720 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1721 }
1722 }
1723 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1724 const VideoContentDescription* video =
1725 static_cast<VideoContentDescription*>(content.description);
1726 for (const VideoCodec& offered_video_codec : video->codecs()) {
1727 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1728 filtered_offered_video_codecs,
1729 offered_video_codec, nullptr) &&
1730 FindMatchingCodec<VideoCodec>(video->codecs(), video_codecs_,
1731 offered_video_codec, nullptr)) {
1732 filtered_offered_video_codecs.push_back(offered_video_codec);
1733 }
1734 }
1735 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1736 const DataContentDescription* data =
1737 static_cast<DataContentDescription*>(content.description);
1738 for (const DataCodec& offered_data_codec : data->codecs()) {
1739 if (!FindMatchingCodec<DataCodec>(data->codecs(),
1740 filtered_offered_data_codecs,
1741 offered_data_codec, nullptr) &&
1742 FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
1743 offered_data_codec, nullptr)) {
1744 filtered_offered_data_codecs.push_back(offered_data_codec);
1745 }
1746 }
1747 }
1748 }
1749
1750 // Add codecs that are not in |current_description| but were in
1751 // |remote_offer|.
1752 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1753 &used_pltypes);
1754 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1755 &used_pltypes);
1756 MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
1757 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001758}
1759
1760void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1761 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07001762 RtpHeaderExtensions* offer_audio_extensions,
1763 RtpHeaderExtensions* offer_video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001764 // All header extensions allocated from the same range to avoid potential
1765 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001766 UsedRtpHeaderExtensionIds used_ids;
jbauch5869f502017-06-29 12:31:36 -07001767 RtpHeaderExtensions all_regular_extensions;
1768 RtpHeaderExtensions all_encrypted_extensions;
zhihuang1c378ed2017-08-17 14:10:50 -07001769 offer_audio_extensions->clear();
1770 offer_video_extensions->clear();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001771
1772 // First - get all extensions from the current description if the media type
1773 // is used.
1774 // Add them to |used_ids| so the local ids are not reused if a new media
1775 // type is added.
1776 if (current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001777 for (const ContentInfo& content : current_description->contents()) {
1778 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1779 const AudioContentDescription* audio =
1780 static_cast<const AudioContentDescription*>(content.description);
1781 MergeRtpHdrExts(audio->rtp_header_extensions(), offer_audio_extensions,
1782 &all_regular_extensions, &all_encrypted_extensions,
1783 &used_ids);
1784 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1785 const VideoContentDescription* video =
1786 static_cast<const VideoContentDescription*>(content.description);
1787 MergeRtpHdrExts(video->rtp_header_extensions(), offer_video_extensions,
1788 &all_regular_extensions, &all_encrypted_extensions,
1789 &used_ids);
1790 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001791 }
1792 }
1793
1794 // Add our default RTP header extensions that are not in
1795 // |current_description|.
zhihuang1c378ed2017-08-17 14:10:50 -07001796 MergeRtpHdrExts(audio_rtp_header_extensions(), offer_audio_extensions,
1797 &all_regular_extensions, &all_encrypted_extensions,
1798 &used_ids);
1799 MergeRtpHdrExts(video_rtp_header_extensions(), offer_video_extensions,
1800 &all_regular_extensions, &all_encrypted_extensions,
1801 &used_ids);
1802
jbauch5869f502017-06-29 12:31:36 -07001803 // TODO(jbauch): Support adding encrypted header extensions to existing
1804 // sessions.
1805 if (enable_encrypted_rtp_header_extensions_ && !current_description) {
zhihuang1c378ed2017-08-17 14:10:50 -07001806 AddEncryptedVersionsOfHdrExts(offer_audio_extensions,
1807 &all_encrypted_extensions, &used_ids);
1808 AddEncryptedVersionsOfHdrExts(offer_video_extensions,
1809 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 12:31:36 -07001810 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001811}
1812
1813bool MediaSessionDescriptionFactory::AddTransportOffer(
1814 const std::string& content_name,
1815 const TransportOptions& transport_options,
1816 const SessionDescription* current_desc,
1817 SessionDescription* offer_desc) const {
1818 if (!transport_desc_factory_)
1819 return false;
1820 const TransportDescription* current_tdesc =
1821 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001822 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001823 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1824 bool ret = (new_tdesc.get() != NULL &&
1825 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1826 if (!ret) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001827 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
1828 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001829 }
1830 return ret;
1831}
1832
1833TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1834 const std::string& content_name,
1835 const SessionDescription* offer_desc,
1836 const TransportOptions& transport_options,
deadbeefb7892532017-02-22 19:35:18 -08001837 const SessionDescription* current_desc,
1838 bool require_transport_attributes) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001839 if (!transport_desc_factory_)
1840 return NULL;
1841 const TransportDescription* offer_tdesc =
1842 GetTransportDescription(content_name, offer_desc);
1843 const TransportDescription* current_tdesc =
1844 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-22 19:35:18 -08001845 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1846 require_transport_attributes,
1847 current_tdesc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001848}
1849
1850bool MediaSessionDescriptionFactory::AddTransportAnswer(
1851 const std::string& content_name,
1852 const TransportDescription& transport_desc,
1853 SessionDescription* answer_desc) const {
1854 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1855 transport_desc))) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001856 RTC_LOG(LS_ERROR) << "Failed to AddTransportAnswer, content name="
1857 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001858 return false;
1859 }
1860 return true;
1861}
1862
zhihuang1c378ed2017-08-17 14:10:50 -07001863// |audio_codecs| = set of all possible codecs that can be used, with correct
1864// payload type mappings
1865//
1866// |supported_audio_codecs| = set of codecs that are supported for the direction
1867// of this m= section
1868//
1869// acd->codecs() = set of previously negotiated codecs for this m= section
1870//
1871// The payload types should come from audio_codecs, but the order should come
1872// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
1873// change existing codec priority, and that new codecs are added with the right
1874// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001875bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001876 const MediaDescriptionOptions& media_description_options,
1877 const MediaSessionOptions& session_options,
1878 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001879 const SessionDescription* current_description,
1880 const RtpHeaderExtensions& audio_rtp_extensions,
1881 const AudioCodecs& audio_codecs,
1882 StreamParamsVec* current_streams,
1883 SessionDescription* desc) const {
zhihuang1c378ed2017-08-17 14:10:50 -07001884 // Filter audio_codecs (which includes all codecs, with correctly remapped
1885 // payload types) based on transceiver direction.
1886 const AudioCodecs& supported_audio_codecs =
1887 GetAudioCodecsForOffer(media_description_options.direction);
1888
1889 AudioCodecs filtered_codecs;
1890 // Add the codecs from current content if exists.
1891 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001892 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07001893 const AudioContentDescription* acd =
1894 static_cast<const AudioContentDescription*>(
1895 current_content->description);
1896 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001897 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
1898 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07001899 filtered_codecs.push_back(codec);
1900 }
1901 }
1902 }
1903 // Add other supported audio codecs.
1904 AudioCodec found_codec;
1905 for (const AudioCodec& codec : supported_audio_codecs) {
1906 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
1907 codec, &found_codec) &&
1908 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
1909 codec, nullptr)) {
1910 // Use the |found_codec| from |audio_codecs| because it has the correctly
1911 // mapped payload type.
1912 filtered_codecs.push_back(found_codec);
1913 }
1914 }
deadbeef44f08192015-12-15 16:20:09 -08001915
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001916 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001917 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1918 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001919
kwiberg31022942016-03-11 14:18:21 -08001920 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001921 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001922 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
1923 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001924 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001925 media_description_options.sender_options, session_options,
1926 filtered_codecs, sdes_policy, GetCryptos(current_content),
1927 crypto_suites, audio_rtp_extensions, current_streams, audio.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001928 return false;
1929 }
1930 audio->set_lang(lang_);
1931
1932 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1933 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001934
zhihuang1c378ed2017-08-17 14:10:50 -07001935 audio->set_direction(
Steve Anton1d03a752017-11-27 14:30:09 -08001936 cricket::MediaContentDirectionFromRtpTransceiverDirection(
1937 media_description_options.direction));
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001938
zhihuang1c378ed2017-08-17 14:10:50 -07001939 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
1940 media_description_options.stopped, audio.release());
1941 if (!AddTransportOffer(media_description_options.mid,
1942 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001943 current_description, desc)) {
1944 return false;
1945 }
1946
1947 return true;
1948}
1949
1950bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001951 const MediaDescriptionOptions& media_description_options,
1952 const MediaSessionOptions& session_options,
1953 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001954 const SessionDescription* current_description,
1955 const RtpHeaderExtensions& video_rtp_extensions,
1956 const VideoCodecs& video_codecs,
1957 StreamParamsVec* current_streams,
1958 SessionDescription* desc) const {
1959 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07001960 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
1961 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001962
kwiberg31022942016-03-11 14:18:21 -08001963 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001964 std::vector<std::string> crypto_suites;
zhihuang1c378ed2017-08-17 14:10:50 -07001965 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
1966 &crypto_suites);
1967
1968 VideoCodecs filtered_codecs;
1969 // Add the codecs from current content if exists.
1970 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07001971 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07001972 const VideoContentDescription* vcd =
1973 static_cast<const VideoContentDescription*>(
1974 current_content->description);
1975 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07001976 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07001977 nullptr)) {
1978 filtered_codecs.push_back(codec);
1979 }
1980 }
1981 }
1982 // Add other supported video codecs.
1983 VideoCodec found_codec;
1984 for (const VideoCodec& codec : video_codecs_) {
1985 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
1986 &found_codec) &&
1987 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
1988 nullptr)) {
1989 // Use the |found_codec| from |video_codecs| because it has the correctly
1990 // mapped payload type.
1991 filtered_codecs.push_back(found_codec);
1992 }
1993 }
1994
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001995 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07001996 media_description_options.sender_options, session_options,
1997 filtered_codecs, sdes_policy, GetCryptos(current_content),
1998 crypto_suites, video_rtp_extensions, current_streams, video.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001999 return false;
2000 }
2001
zhihuang1c378ed2017-08-17 14:10:50 -07002002 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002003
2004 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2005 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00002006
zhihuang1c378ed2017-08-17 14:10:50 -07002007 video->set_direction(
Steve Anton1d03a752017-11-27 14:30:09 -08002008 cricket::MediaContentDirectionFromRtpTransceiverDirection(
2009 media_description_options.direction));
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00002010
zhihuang1c378ed2017-08-17 14:10:50 -07002011 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
2012 media_description_options.stopped, video.release());
2013 if (!AddTransportOffer(media_description_options.mid,
2014 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002015 current_description, desc)) {
2016 return false;
2017 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002018 return true;
2019}
2020
2021bool MediaSessionDescriptionFactory::AddDataContentForOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002022 const MediaDescriptionOptions& media_description_options,
2023 const MediaSessionOptions& session_options,
2024 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002025 const SessionDescription* current_description,
zhihuang1c378ed2017-08-17 14:10:50 -07002026 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002027 StreamParamsVec* current_streams,
2028 SessionDescription* desc) const {
2029 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2030
kwiberg31022942016-03-11 14:18:21 -08002031 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
zhihuang1c378ed2017-08-17 14:10:50 -07002032 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
2033 // If the DataChannel type is not specified, use the DataChannel type in
2034 // the current description.
2035 if (session_options.data_channel_type == DCT_NONE && current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002036 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
zhihuang1c378ed2017-08-17 14:10:50 -07002037 is_sctp = (static_cast<const DataContentDescription*>(
2038 current_content->description)
2039 ->protocol() == kMediaProtocolSctp);
2040 }
deadbeef44f08192015-12-15 16:20:09 -08002041
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002042 cricket::SecurePolicy sdes_policy =
zhihuang1c378ed2017-08-17 14:10:50 -07002043 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2044 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002045 std::vector<std::string> crypto_suites;
2046 if (is_sctp) {
2047 // SDES doesn't make sense for SCTP, so we disable it, and we only
2048 // get SDES crypto suites for RTP-based data channels.
2049 sdes_policy = cricket::SEC_DISABLED;
2050 // Unlike SetMediaProtocol below, we need to set the protocol
2051 // before we call CreateMediaContentOffer. Otherwise,
2052 // CreateMediaContentOffer won't know this is SCTP and will
2053 // generate SSRCs rather than SIDs.
deadbeef8b7e9ad2017-05-25 09:38:55 -07002054 // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
2055 // it's safe to do so. Older versions of webrtc would reject these
2056 // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002057 data->set_protocol(
2058 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
2059 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002060 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
deadbeef7914b8c2017-04-21 03:23:33 -07002061 &crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002062 }
2063
zhihuang1c378ed2017-08-17 14:10:50 -07002064 // Even SCTP uses a "codec".
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002065 if (!CreateMediaContentOffer(
zhihuang1c378ed2017-08-17 14:10:50 -07002066 media_description_options.sender_options, session_options,
2067 data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites,
2068 RtpHeaderExtensions(), current_streams, data.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002069 return false;
2070 }
2071
2072 if (is_sctp) {
zhihuang1c378ed2017-08-17 14:10:50 -07002073 desc->AddContent(media_description_options.mid, NS_JINGLE_DRAFT_SCTP,
2074 data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002075 } else {
zhihuang1c378ed2017-08-17 14:10:50 -07002076 data->set_bandwidth(kDataMaxBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002077 SetMediaProtocol(secure_transport, data.get());
zhihuang1c378ed2017-08-17 14:10:50 -07002078 desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
2079 media_description_options.stopped, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002080 }
zhihuang1c378ed2017-08-17 14:10:50 -07002081 if (!AddTransportOffer(media_description_options.mid,
2082 media_description_options.transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002083 current_description, desc)) {
2084 return false;
2085 }
2086 return true;
2087}
2088
zhihuang1c378ed2017-08-17 14:10:50 -07002089// |audio_codecs| = set of all possible codecs that can be used, with correct
2090// payload type mappings
2091//
2092// |supported_audio_codecs| = set of codecs that are supported for the direction
2093// of this m= section
2094//
2095// acd->codecs() = set of previously negotiated codecs for this m= section
2096//
2097// The payload types should come from audio_codecs, but the order should come
2098// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2099// change existing codec priority, and that new codecs are added with the right
2100// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002101bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002102 const MediaDescriptionOptions& media_description_options,
2103 const MediaSessionOptions& session_options,
2104 const ContentInfo* offer_content,
2105 const SessionDescription* offer_description,
2106 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002107 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002108 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002109 const AudioCodecs& audio_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002110 StreamParamsVec* current_streams,
2111 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002112 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002113 const AudioContentDescription* offer_audio_description =
2114 static_cast<const AudioContentDescription*>(offer_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002115
deadbeefb7892532017-02-22 19:35:18 -08002116 std::unique_ptr<TransportDescription> audio_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002117 CreateTransportAnswer(media_description_options.mid, offer_description,
2118 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002119 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002120 if (!audio_transport) {
2121 return false;
2122 }
2123
zhihuang1c378ed2017-08-17 14:10:50 -07002124 // Pick codecs based on the requested communications direction in the offer
2125 // and the selected direction in the answer.
2126 // Note these will be filtered one final time in CreateMediaContentAnswer.
2127 auto wants_rtd = media_description_options.direction;
Steve Anton1d03a752017-11-27 14:30:09 -08002128 auto offer_rtd = RtpTransceiverDirectionFromMediaContentDirection(
zhihuang1c378ed2017-08-17 14:10:50 -07002129 offer_audio_description->direction());
ossu075af922016-06-14 03:29:38 -07002130 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 14:10:50 -07002131 AudioCodecs supported_audio_codecs =
2132 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2133
2134 AudioCodecs filtered_codecs;
2135 // Add the codecs from current content if exists.
2136 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002137 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 14:10:50 -07002138 const AudioContentDescription* acd =
2139 static_cast<const AudioContentDescription*>(
2140 current_content->description);
2141 for (const AudioCodec& codec : acd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002142 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2143 nullptr)) {
zhihuang1c378ed2017-08-17 14:10:50 -07002144 filtered_codecs.push_back(codec);
2145 }
2146 }
2147 }
2148 // Add other supported audio codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002149 for (const AudioCodec& codec : supported_audio_codecs) {
2150 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Zhi Huang6f367472017-11-22 13:20:02 -08002151 codec, nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002152 !FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
2153 codec, nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002154 // We should use the local codec with local parameters and the codec id
2155 // would be correctly mapped in |NegotiateCodecs|.
2156 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002157 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002158 }
2159
zhihuang1c378ed2017-08-17 14:10:50 -07002160 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2161 session_options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08002162 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002163 new AudioContentDescription());
2164 // Do not require or create SDES cryptos if DTLS is used.
2165 cricket::SecurePolicy sdes_policy =
2166 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2167 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002168 offer_audio_description, media_description_options, session_options,
2169 filtered_codecs, sdes_policy, GetCryptos(current_content),
2170 audio_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2171 current_streams, bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002172 return false; // Fails the session setup.
2173 }
2174
deadbeefb7892532017-02-22 19:35:18 -08002175 bool secure = bundle_transport ? bundle_transport->description.secure()
2176 : audio_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002177 bool rejected = media_description_options.stopped ||
2178 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002179 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2180 audio_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002181 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002182 AddTransportAnswer(media_description_options.mid, *(audio_transport.get()),
2183 answer);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002184 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002185 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2186 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002187 }
2188
zhihuang1c378ed2017-08-17 14:10:50 -07002189 answer->AddContent(media_description_options.mid, offer_content->type,
2190 rejected, audio_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002191 return true;
2192}
2193
2194bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002195 const MediaDescriptionOptions& media_description_options,
2196 const MediaSessionOptions& session_options,
2197 const ContentInfo* offer_content,
2198 const SessionDescription* offer_description,
2199 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002200 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002201 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002202 const VideoCodecs& video_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002203 StreamParamsVec* current_streams,
2204 SessionDescription* answer) const {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002205 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002206 const VideoContentDescription* offer_video_description =
2207 static_cast<const VideoContentDescription*>(offer_content->description);
2208
deadbeefb7892532017-02-22 19:35:18 -08002209 std::unique_ptr<TransportDescription> video_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002210 CreateTransportAnswer(media_description_options.mid, offer_description,
2211 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002212 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002213 if (!video_transport) {
2214 return false;
2215 }
2216
zhihuang1c378ed2017-08-17 14:10:50 -07002217 VideoCodecs filtered_codecs;
2218 // Add the codecs from current content if exists.
2219 if (current_content) {
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002220 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 14:10:50 -07002221 const VideoContentDescription* vcd =
2222 static_cast<const VideoContentDescription*>(
2223 current_content->description);
2224 for (const VideoCodec& codec : vcd->codecs()) {
Taylor Brandstetter1c349742017-10-03 18:25:36 -07002225 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
zhihuang1c378ed2017-08-17 14:10:50 -07002226 nullptr)) {
2227 filtered_codecs.push_back(codec);
2228 }
2229 }
2230 }
2231 // Add other supported video codecs.
zhihuang1c378ed2017-08-17 14:10:50 -07002232 for (const VideoCodec& codec : video_codecs_) {
2233 if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
Zhi Huang6f367472017-11-22 13:20:02 -08002234 nullptr) &&
zhihuang1c378ed2017-08-17 14:10:50 -07002235 !FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
2236 nullptr)) {
Zhi Huang6f367472017-11-22 13:20:02 -08002237 // We should use the local codec with local parameters and the codec id
2238 // would be correctly mapped in |NegotiateCodecs|.
2239 filtered_codecs.push_back(codec);
zhihuang1c378ed2017-08-17 14:10:50 -07002240 }
2241 }
2242
2243 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2244 session_options.bundle_enabled;
2245
kwiberg31022942016-03-11 14:18:21 -08002246 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002247 new VideoContentDescription());
2248 // Do not require or create SDES cryptos if DTLS is used.
2249 cricket::SecurePolicy sdes_policy =
2250 video_transport->secure() ? cricket::SEC_DISABLED : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002251 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002252 offer_video_description, media_description_options, session_options,
2253 filtered_codecs, sdes_policy, GetCryptos(current_content),
2254 video_rtp_extensions_, enable_encrypted_rtp_header_extensions_,
2255 current_streams, bundle_enabled, video_answer.get())) {
2256 return false; // Failed the sessin setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002257 }
deadbeefb7892532017-02-22 19:35:18 -08002258 bool secure = bundle_transport ? bundle_transport->description.secure()
2259 : video_transport->secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002260 bool rejected = media_description_options.stopped ||
2261 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002262 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2263 video_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002264 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002265 if (!AddTransportAnswer(media_description_options.mid,
2266 *(video_transport.get()), answer)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002267 return false;
2268 }
zhihuang1c378ed2017-08-17 14:10:50 -07002269 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002270 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002271 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2272 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002273 }
zhihuang1c378ed2017-08-17 14:10:50 -07002274 answer->AddContent(media_description_options.mid, offer_content->type,
2275 rejected, video_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002276 return true;
2277}
2278
2279bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 14:10:50 -07002280 const MediaDescriptionOptions& media_description_options,
2281 const MediaSessionOptions& session_options,
2282 const ContentInfo* offer_content,
2283 const SessionDescription* offer_description,
2284 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002285 const SessionDescription* current_description,
deadbeefb7892532017-02-22 19:35:18 -08002286 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 14:10:50 -07002287 const DataCodecs& data_codecs,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002288 StreamParamsVec* current_streams,
2289 SessionDescription* answer) const {
deadbeefb7892532017-02-22 19:35:18 -08002290 std::unique_ptr<TransportDescription> data_transport(
zhihuang1c378ed2017-08-17 14:10:50 -07002291 CreateTransportAnswer(media_description_options.mid, offer_description,
2292 media_description_options.transport_options,
deadbeefb7892532017-02-22 19:35:18 -08002293 current_description, bundle_transport != nullptr));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002294 if (!data_transport) {
2295 return false;
2296 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002297
kwiberg31022942016-03-11 14:18:21 -08002298 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002299 new DataContentDescription());
2300 // Do not require or create SDES cryptos if DTLS is used.
2301 cricket::SecurePolicy sdes_policy =
2302 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 14:10:50 -07002303 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2304 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002305 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2306 const DataContentDescription* offer_data_description =
2307 static_cast<const DataContentDescription*>(offer_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002308 if (!CreateMediaContentAnswer(
Taylor Brandstetter80cfb522017-10-12 20:37:38 -07002309 offer_data_description, media_description_options, session_options,
2310 data_codecs, sdes_policy, GetCryptos(current_content),
2311 RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
2312 current_streams, bundle_enabled, data_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002313 return false; // Fails the session setup.
2314 }
2315
zstein4b2e0822017-02-17 19:48:38 -08002316 // Respond with sctpmap if the offer uses sctpmap.
zstein4b2e0822017-02-17 19:48:38 -08002317 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2318 data_answer->set_use_sctpmap(offer_uses_sctpmap);
2319
deadbeefb7892532017-02-22 19:35:18 -08002320 bool secure = bundle_transport ? bundle_transport->description.secure()
2321 : data_transport->secure();
2322
zhihuang1c378ed2017-08-17 14:10:50 -07002323 bool rejected = session_options.data_channel_type == DCT_NONE ||
2324 media_description_options.stopped ||
2325 offer_content->rejected ||
deadbeefb7892532017-02-22 19:35:18 -08002326 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2327 data_answer->protocol(), secure);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002328 if (!rejected) {
zhihuang1c378ed2017-08-17 14:10:50 -07002329 data_answer->set_bandwidth(kDataMaxBandwidth);
2330 if (!AddTransportAnswer(media_description_options.mid,
2331 *(data_transport.get()), answer)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002332 return false;
2333 }
2334 } else {
2335 // RFC 3264
2336 // The answer MUST contain the same number of m-lines as the offer.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002337 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002338 }
zhihuang1c378ed2017-08-17 14:10:50 -07002339 answer->AddContent(media_description_options.mid, offer_content->type,
2340 rejected, data_answer.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00002341 return true;
2342}
2343
zhihuang1c378ed2017-08-17 14:10:50 -07002344void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2345 audio_sendrecv_codecs_.clear();
2346 all_audio_codecs_.clear();
2347 // Compute the audio codecs union.
2348 for (const AudioCodec& send : audio_send_codecs_) {
2349 all_audio_codecs_.push_back(send);
2350 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2351 send, nullptr)) {
2352 // It doesn't make sense to have an RTX codec we support sending but not
2353 // receiving.
2354 RTC_DCHECK(!IsRtxCodec(send));
2355 }
2356 }
2357 for (const AudioCodec& recv : audio_recv_codecs_) {
2358 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2359 recv, nullptr)) {
2360 all_audio_codecs_.push_back(recv);
2361 }
2362 }
2363 // Use NegotiateCodecs to merge our codec lists, since the operation is
2364 // essentially the same. Put send_codecs as the offered_codecs, which is the
2365 // order we'd like to follow. The reasoning is that encoding is usually more
2366 // expensive than decoding, and prioritizing a codec in the send list probably
2367 // means it's a codec we can handle efficiently.
2368 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2369 &audio_sendrecv_codecs_);
2370}
2371
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002372bool IsMediaContent(const ContentInfo* content) {
2373 return (content &&
2374 (content->type == NS_JINGLE_RTP ||
2375 content->type == NS_JINGLE_DRAFT_SCTP));
2376}
2377
2378bool IsAudioContent(const ContentInfo* content) {
2379 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2380}
2381
2382bool IsVideoContent(const ContentInfo* content) {
2383 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2384}
2385
2386bool IsDataContent(const ContentInfo* content) {
2387 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2388}
2389
deadbeef0ed85b22016-02-23 17:24:52 -08002390const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2391 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002392 for (const ContentInfo& content : contents) {
2393 if (IsMediaContentOfType(&content, media_type)) {
2394 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002395 }
2396 }
deadbeef0ed85b22016-02-23 17:24:52 -08002397 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002398}
2399
2400const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2401 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2402}
2403
2404const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2405 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2406}
2407
2408const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2409 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2410}
2411
2412static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2413 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08002414 if (sdesc == nullptr) {
2415 return nullptr;
2416 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002417
2418 return GetFirstMediaContent(sdesc->contents(), media_type);
2419}
2420
2421const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2422 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2423}
2424
2425const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2426 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2427}
2428
2429const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2430 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2431}
2432
2433const MediaContentDescription* GetFirstMediaContentDescription(
2434 const SessionDescription* sdesc, MediaType media_type) {
2435 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2436 const ContentDescription* description = content ? content->description : NULL;
2437 return static_cast<const MediaContentDescription*>(description);
2438}
2439
2440const AudioContentDescription* GetFirstAudioContentDescription(
2441 const SessionDescription* sdesc) {
2442 return static_cast<const AudioContentDescription*>(
2443 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2444}
2445
2446const VideoContentDescription* GetFirstVideoContentDescription(
2447 const SessionDescription* sdesc) {
2448 return static_cast<const VideoContentDescription*>(
2449 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2450}
2451
2452const DataContentDescription* GetFirstDataContentDescription(
2453 const SessionDescription* sdesc) {
2454 return static_cast<const DataContentDescription*>(
2455 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2456}
2457
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002458//
2459// Non-const versions of the above functions.
2460//
2461
Steve Anton36b29d12017-10-30 09:57:42 -07002462ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002463 MediaType media_type) {
Steve Anton36b29d12017-10-30 09:57:42 -07002464 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002465 if (IsMediaContentOfType(&content, media_type)) {
2466 return &content;
2467 }
2468 }
2469 return nullptr;
2470}
2471
Steve Anton36b29d12017-10-30 09:57:42 -07002472ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002473 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2474}
2475
Steve Anton36b29d12017-10-30 09:57:42 -07002476ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002477 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2478}
2479
Steve Anton36b29d12017-10-30 09:57:42 -07002480ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002481 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2482}
2483
2484static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2485 MediaType media_type) {
2486 if (sdesc == nullptr) {
2487 return nullptr;
2488 }
2489
Steve Anton36b29d12017-10-30 09:57:42 -07002490 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07002491}
2492
2493ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2494 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2495}
2496
2497ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2498 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2499}
2500
2501ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2502 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2503}
2504
2505MediaContentDescription* GetFirstMediaContentDescription(
2506 SessionDescription* sdesc,
2507 MediaType media_type) {
2508 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2509 ContentDescription* description = content ? content->description : NULL;
2510 return static_cast<MediaContentDescription*>(description);
2511}
2512
2513AudioContentDescription* GetFirstAudioContentDescription(
2514 SessionDescription* sdesc) {
2515 return static_cast<AudioContentDescription*>(
2516 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2517}
2518
2519VideoContentDescription* GetFirstVideoContentDescription(
2520 SessionDescription* sdesc) {
2521 return static_cast<VideoContentDescription*>(
2522 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2523}
2524
2525DataContentDescription* GetFirstDataContentDescription(
2526 SessionDescription* sdesc) {
2527 return static_cast<DataContentDescription*>(
2528 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2529}
2530
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002531} // namespace cricket