blob: 937a2c11f241450e8bd64b7b79ad3888960f2283 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellander65c7f672016-02-12 08:05:012 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellander65c7f672016-02-12 08:05:014 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:369 */
10
kjellander@webrtc.org9b8df252016-02-12 05:47:5911#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
deadbeef67cf2c12016-04-13 17:07:1613#include <algorithm> // For std::find_if, std::sort.
henrike@webrtc.org28e20752013-07-10 00:45:3614#include <functional>
15#include <map>
kwiberg31022942016-03-11 22:18:2116#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:3617#include <set>
deadbeef67cf2c12016-04-13 17:07:1618#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:3619#include <utility>
20
buildbot@webrtc.orga09a9992014-08-13 17:26:0821#include "webrtc/base/helpers.h"
22#include "webrtc/base/logging.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:0823#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-05 07:52:2824#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 13:42:3025#include "webrtc/media/base/mediaconstants.h"
26#include "webrtc/p2p/base/p2pconstants.h"
kjellander@webrtc.org9b8df252016-02-12 05:47:5927#include "webrtc/pc/channelmanager.h"
28#include "webrtc/pc/srtpfilter.h"
henrike@webrtc.org28e20752013-07-10 00:45:3629
mallinath@webrtc.orga27be8e2013-09-27 23:04:1030#ifdef HAVE_SCTP
kjellandera96e2d72016-02-05 07:52:2831#include "webrtc/media/sctp/sctpdataengine.h"
mallinath@webrtc.orga27be8e2013-09-27 23:04:1032#else
Peter Boström0c4e06b2015-10-07 10:23:2133static const uint32_t kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:1034#endif
35
henrike@webrtc.org28e20752013-07-10 00:45:3636namespace {
37const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-19 03:41:5338
39void GetSupportedCryptoSuiteNames(void (*func)(std::vector<int>*),
40 std::vector<std::string>* names) {
41#ifdef HAVE_SRTP
42 std::vector<int> crypto_suites;
43 func(&crypto_suites);
44 for (const auto crypto : crypto_suites) {
45 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
46 }
47#endif
48}
terelius8c011e52016-04-26 12:28:1149} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:3650
51namespace cricket {
52
henrike@webrtc.org28e20752013-07-10 00:45:3653// RTP Profile names
54// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
55// RFC4585
56const char kMediaProtocolAvpf[] = "RTP/AVPF";
57// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:5958const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
59
deadbeeff3938292015-07-15 19:20:5360// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
61// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:3662const char kMediaProtocolSavpf[] = "RTP/SAVPF";
63
64const char kMediaProtocolRtpPrefix[] = "RTP/";
65
66const char kMediaProtocolSctp[] = "SCTP";
67const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:4868const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:2169const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:3670
ossu075af922016-06-14 10:29:3871RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
72 MediaContentDirection md) {
73 const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
74 const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
75 return RtpTransceiverDirection(send, recv);
76}
77
78MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
79 if (send && recv) {
80 return MD_SENDRECV;
81 } else if (send) {
82 return MD_SENDONLY;
83 } else if (recv) {
84 return MD_RECVONLY;
85 }
86
87 return MD_INACTIVE;
88}
89
90RtpTransceiverDirection
91NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
92 RtpTransceiverDirection wants) {
93 return RtpTransceiverDirection(offer.recv && wants.send,
94 offer.send && wants.recv);
95}
96
henrike@webrtc.org28e20752013-07-10 00:45:3697static bool IsMediaContentOfType(const ContentInfo* content,
98 MediaType media_type) {
99 if (!IsMediaContent(content)) {
100 return false;
101 }
102
103 const MediaContentDescription* mdesc =
104 static_cast<const MediaContentDescription*>(content->description);
105 return mdesc && mdesc->type() == media_type;
106}
107
108static bool CreateCryptoParams(int tag, const std::string& cipher,
109 CryptoParams *out) {
110 std::string key;
111 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
112
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52113 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
henrike@webrtc.org28e20752013-07-10 00:45:36114 return false;
115 }
116 out->tag = tag;
117 out->cipher_suite = cipher;
118 out->key_params = kInline;
119 out->key_params += key;
120 return true;
121}
122
123#ifdef HAVE_SRTP
124static bool AddCryptoParams(const std::string& cipher_suite,
125 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49126 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36127
128 out->resize(size + 1);
129 return CreateCryptoParams(size, cipher_suite, &out->at(size));
130}
131
132void AddMediaCryptos(const CryptoParamsVec& cryptos,
133 MediaContentDescription* media) {
134 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
135 crypto != cryptos.end(); ++crypto) {
136 media->AddCrypto(*crypto);
137 }
138}
139
140bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
141 MediaContentDescription* media) {
142 CryptoParamsVec cryptos;
143 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
144 it != crypto_suites.end(); ++it) {
145 if (!AddCryptoParams(*it, &cryptos)) {
146 return false;
147 }
148 }
149 AddMediaCryptos(cryptos, media);
150 return true;
151}
152#endif
153
154const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
155 if (!media) {
156 return NULL;
157 }
158 return &media->cryptos();
159}
160
161bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
162 const CryptoParams& crypto,
163 CryptoParams* out) {
164 for (CryptoParamsVec::const_iterator it = cryptos.begin();
165 it != cryptos.end(); ++it) {
166 if (crypto.Matches(*it)) {
167 *out = *it;
168 return true;
169 }
170 }
171 return false;
172}
173
174// For audio, HMAC 32 is prefered because of the low overhead.
Guo-wei Shieh521ed7b2015-11-19 03:41:53175void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36176#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-19 03:41:53177 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
178 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36179#endif
180}
181
Guo-wei Shieh521ed7b2015-11-19 03:41:53182void GetSupportedAudioCryptoSuiteNames(
183 std::vector<std::string>* crypto_suite_names) {
184 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
185 crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36186}
187
Guo-wei Shieh521ed7b2015-11-19 03:41:53188void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) {
189 GetDefaultSrtpCryptoSuites(crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36190}
191
Guo-wei Shieh521ed7b2015-11-19 03:41:53192void GetSupportedVideoCryptoSuiteNames(
193 std::vector<std::string>* crypto_suite_names) {
194 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
195 crypto_suite_names);
196}
197
198void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) {
199 GetDefaultSrtpCryptoSuites(crypto_suites);
200}
201
202void GetSupportedDataCryptoSuiteNames(
203 std::vector<std::string>* crypto_suite_names) {
204 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
205 crypto_suite_names);
206}
207
208void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36209#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-19 03:41:53210 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36211#endif
212}
213
Guo-wei Shieh521ed7b2015-11-19 03:41:53214void GetDefaultSrtpCryptoSuiteNames(
215 std::vector<std::string>* crypto_suite_names) {
216 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names);
217}
218
henrike@webrtc.org28e20752013-07-10 00:45:36219// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
220// tolerated unless bundle is enabled because it is low overhead. Pick the
221// crypto in the list that is supported.
222static bool SelectCrypto(const MediaContentDescription* offer,
223 bool bundle,
224 CryptoParams *crypto) {
225 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
226 const CryptoParamsVec& cryptos = offer->cryptos();
227
228 for (CryptoParamsVec::const_iterator i = cryptos.begin();
229 i != cryptos.end(); ++i) {
Guo-wei Shieh456696a2015-10-01 04:48:54230 if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
231 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
232 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36233 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
234 }
235 }
236 return false;
237}
238
henrike@webrtc.org28e20752013-07-10 00:45:36239// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12240// The generated values are added to |ssrcs|.
241// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36242static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12243 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 10:23:21244 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12245 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 10:23:21246 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36247 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52248 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41249 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36250 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
251 ssrcs->push_back(candidate);
252 }
253}
254
255// Returns false if we exhaust the range of SIDs.
Peter Boström0c4e06b2015-10-07 10:23:21256static bool GenerateSctpSid(const StreamParamsVec& params_vec, uint32_t* sid) {
henrike@webrtc.org28e20752013-07-10 00:45:36257 if (params_vec.size() > kMaxSctpSid) {
258 LOG(LS_WARNING) <<
259 "Could not generate an SCTP SID: too many SCTP streams.";
260 return false;
261 }
262 while (true) {
Peter Boström0c4e06b2015-10-07 10:23:21263 uint32_t candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41264 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36265 *sid = candidate;
266 return true;
267 }
268 }
269}
270
271static bool GenerateSctpSids(const StreamParamsVec& params_vec,
Peter Boström0c4e06b2015-10-07 10:23:21272 std::vector<uint32_t>* sids) {
273 uint32_t sid;
henrike@webrtc.org28e20752013-07-10 00:45:36274 if (!GenerateSctpSid(params_vec, &sid)) {
275 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
276 return false;
277 }
278 sids->push_back(sid);
279 return true;
280}
281
282// Finds all StreamParams of all media types and attach them to stream_params.
283static void GetCurrentStreamParams(const SessionDescription* sdesc,
284 StreamParamsVec* stream_params) {
285 if (!sdesc)
286 return;
287
288 const ContentInfos& contents = sdesc->contents();
289 for (ContentInfos::const_iterator content = contents.begin();
290 content != contents.end(); ++content) {
291 if (!IsMediaContent(&*content)) {
292 continue;
293 }
294 const MediaContentDescription* media =
295 static_cast<const MediaContentDescription*>(
296 content->description);
297 const StreamParamsVec& streams = media->streams();
298 for (StreamParamsVec::const_iterator it = streams.begin();
299 it != streams.end(); ++it) {
300 stream_params->push_back(*it);
301 }
302 }
303}
304
jiayl@webrtc.org9c16c392014-05-01 18:30:30305// Filters the data codecs for the data channel type.
306void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
307 // Filter RTP codec for SCTP and vice versa.
308 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
309 for (std::vector<DataCodec>::iterator iter = codecs->begin();
310 iter != codecs->end();) {
311 if (iter->id == codec_id) {
312 iter = codecs->erase(iter);
313 } else {
314 ++iter;
315 }
316 }
317}
318
henrike@webrtc.org28e20752013-07-10 00:45:36319template <typename IdStruct>
320class UsedIds {
321 public:
322 UsedIds(int min_allowed_id, int max_allowed_id)
323 : min_allowed_id_(min_allowed_id),
324 max_allowed_id_(max_allowed_id),
325 next_id_(max_allowed_id) {
326 }
327
328 // Loops through all Id in |ids| and changes its id if it is
329 // already in use by another IdStruct. Call this methods with all Id
330 // in a session description to make sure no duplicate ids exists.
331 // Note that typename Id must be a type of IdStruct.
332 template <typename Id>
333 void FindAndSetIdUsed(std::vector<Id>* ids) {
334 for (typename std::vector<Id>::iterator it = ids->begin();
335 it != ids->end(); ++it) {
336 FindAndSetIdUsed(&*it);
337 }
338 }
339
340 // Finds and sets an unused id if the |idstruct| id is already in use.
341 void FindAndSetIdUsed(IdStruct* idstruct) {
342 const int original_id = idstruct->id;
343 int new_id = idstruct->id;
344
345 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
346 // If the original id is not in range - this is an id that can't be
347 // dynamically changed.
348 return;
349 }
350
351 if (IsIdUsed(original_id)) {
352 new_id = FindUnusedId();
353 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
354 << " to " << new_id;
355 idstruct->id = new_id;
356 }
357 SetIdUsed(new_id);
358 }
359
360 private:
361 // Returns the first unused id in reverse order.
362 // This hopefully reduce the risk of more collisions. We want to change the
363 // default ids as little as possible.
364 int FindUnusedId() {
365 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
366 --next_id_;
367 }
368 ASSERT(next_id_ >= min_allowed_id_);
369 return next_id_;
370 }
371
372 bool IsIdUsed(int new_id) {
373 return id_set_.find(new_id) != id_set_.end();
374 }
375
376 void SetIdUsed(int new_id) {
377 id_set_.insert(new_id);
378 }
379
380 const int min_allowed_id_;
381 const int max_allowed_id_;
382 int next_id_;
383 std::set<int> id_set_;
384};
385
386// Helper class used for finding duplicate RTP payload types among audio, video
387// and data codecs. When bundle is used the payload types may not collide.
388class UsedPayloadTypes : public UsedIds<Codec> {
389 public:
390 UsedPayloadTypes()
391 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
392 }
393
394
395 private:
396 static const int kDynamicPayloadTypeMin = 96;
397 static const int kDynamicPayloadTypeMax = 127;
398};
399
400// Helper class used for finding duplicate RTP Header extension ids among
401// audio and video extensions.
isheriff6f8d6862016-05-26 18:24:55402class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36403 public:
404 UsedRtpHeaderExtensionIds()
isheriff6f8d6862016-05-26 18:24:55405 : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36406
407 private:
henrike@webrtc.org79047f92014-03-06 23:46:59408 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36409 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59410 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36411};
412
413static bool IsSctp(const MediaContentDescription* desc) {
414 return ((desc->protocol() == kMediaProtocolSctp) ||
415 (desc->protocol() == kMediaProtocolDtlsSctp));
416}
417
418// Adds a StreamParams for each Stream in Streams with media type
419// media_type to content_description.
420// |current_params| - All currently known StreamParams of any media type.
421template <class C>
zhihuang8f65cdf2016-05-07 01:40:30422static bool AddStreamParams(MediaType media_type,
423 const MediaSessionOptions& options,
424 StreamParamsVec* current_streams,
425 MediaContentDescriptionImpl<C>* content_description,
426 const bool add_legacy_stream) {
Noah Richards2e7a0982015-05-18 21:02:54427 const bool include_rtx_streams =
428 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36429
zhihuang8f65cdf2016-05-07 01:40:30430 const MediaSessionOptions::Streams& streams = options.streams;
henrike@webrtc.org28e20752013-07-10 00:45:36431 if (streams.empty() && add_legacy_stream) {
432 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 10:23:21433 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36434 if (IsSctp(content_description)) {
435 GenerateSctpSids(*current_streams, &ssrcs);
436 } else {
Noah Richards2e7a0982015-05-18 21:02:54437 int num_ssrcs = include_rtx_streams ? 2 : 1;
wu@webrtc.orgcecfd182013-10-30 05:18:12438 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36439 }
Noah Richards2e7a0982015-05-18 21:02:54440 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36441 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
442 content_description->set_multistream(true);
443 } else {
444 content_description->AddLegacyStream(ssrcs[0]);
445 }
446 return true;
447 }
448
449 MediaSessionOptions::Streams::const_iterator stream_it;
450 for (stream_it = streams.begin();
451 stream_it != streams.end(); ++stream_it) {
452 if (stream_it->type != media_type)
453 continue; // Wrong media type.
454
tommi@webrtc.org586f2ed2015-01-22 23:00:41455 const StreamParams* param =
456 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36457 // groupid is empty for StreamParams generated using
458 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41459 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36460 // This is a new stream.
Peter Boström0c4e06b2015-10-07 10:23:21461 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36462 if (IsSctp(content_description)) {
463 GenerateSctpSids(*current_streams, &ssrcs);
464 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12465 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36466 }
467 StreamParams stream_param;
468 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12469 // Add the generated ssrc.
470 for (size_t i = 0; i < ssrcs.size(); ++i) {
471 stream_param.ssrcs.push_back(ssrcs[i]);
472 }
473 if (stream_it->num_sim_layers > 1) {
474 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
475 stream_param.ssrc_groups.push_back(group);
476 }
Noah Richards2e7a0982015-05-18 21:02:54477 // Generate extra ssrcs for include_rtx_streams case.
478 if (include_rtx_streams) {
479 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 10:23:21480 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 21:02:54481 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
482 &rtx_ssrcs);
483 for (size_t i = 0; i < ssrcs.size(); ++i) {
484 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
485 }
henrike@webrtc.org28e20752013-07-10 00:45:36486 content_description->set_multistream(true);
487 }
zhihuang8f65cdf2016-05-07 01:40:30488 stream_param.cname = options.rtcp_cname;
henrike@webrtc.org28e20752013-07-10 00:45:36489 stream_param.sync_label = stream_it->sync_label;
490 content_description->AddStream(stream_param);
491
492 // Store the new StreamParams in current_streams.
493 // This is necessary so that we can use the CNAME for other media types.
494 current_streams->push_back(stream_param);
495 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41496 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36497 }
498 }
499 return true;
500}
501
502// Updates the transport infos of the |sdesc| according to the given
503// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d3652016-01-08 23:35:57504// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
505// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36506static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
507 SessionDescription* sdesc) {
508 // The bundle should not be empty.
509 if (!sdesc || !bundle_group.FirstContentName()) {
510 return false;
511 }
512
513 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 09:46:32514 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36515 const TransportInfo* selected_transport_info =
516 sdesc->GetTransportInfoByName(selected_content_name);
517 if (!selected_transport_info) {
518 return false;
519 }
520
521 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 09:46:32522 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36523 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 09:46:32524 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36525 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d3652016-01-08 23:35:57526 ConnectionRole selected_connection_role =
527 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36528 for (TransportInfos::iterator it =
529 sdesc->transport_infos().begin();
530 it != sdesc->transport_infos().end(); ++it) {
531 if (bundle_group.HasContentName(it->content_name) &&
532 it->content_name != selected_content_name) {
533 it->description.ice_ufrag = selected_ufrag;
534 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d3652016-01-08 23:35:57535 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36536 }
537 }
538 return true;
539}
540
541// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
542// sets it to |cryptos|.
543static bool GetCryptosByName(const SessionDescription* sdesc,
544 const std::string& content_name,
545 CryptoParamsVec* cryptos) {
546 if (!sdesc || !cryptos) {
547 return false;
548 }
549
550 const ContentInfo* content = sdesc->GetContentByName(content_name);
551 if (!IsMediaContent(content) || !content->description) {
552 return false;
553 }
554
555 const MediaContentDescription* media_desc =
556 static_cast<const MediaContentDescription*>(content->description);
557 *cryptos = media_desc->cryptos();
558 return true;
559}
560
561// Predicate function used by the remove_if.
562// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
563static bool CryptoNotFound(const CryptoParams crypto,
564 const CryptoParamsVec* filter) {
565 if (filter == NULL) {
566 return true;
567 }
568 for (CryptoParamsVec::const_iterator it = filter->begin();
569 it != filter->end(); ++it) {
570 if (it->cipher_suite == crypto.cipher_suite) {
571 return false;
572 }
573 }
574 return true;
575}
576
577// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
578// which are not available in |filter|.
579static void PruneCryptos(const CryptoParamsVec& filter,
580 CryptoParamsVec* target_cryptos) {
581 if (!target_cryptos) {
582 return;
583 }
584 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
585 target_cryptos->end(),
586 bind2nd(ptr_fun(CryptoNotFound),
587 &filter)),
588 target_cryptos->end());
589}
590
deadbeefb5cb19b2015-11-24 00:39:12591static bool IsRtpProtocol(const std::string& protocol) {
592 return protocol.empty() ||
593 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
594}
595
henrike@webrtc.org28e20752013-07-10 00:45:36596static bool IsRtpContent(SessionDescription* sdesc,
597 const std::string& content_name) {
598 bool is_rtp = false;
599 ContentInfo* content = sdesc->GetContentByName(content_name);
600 if (IsMediaContent(content)) {
601 MediaContentDescription* media_desc =
602 static_cast<MediaContentDescription*>(content->description);
603 if (!media_desc) {
604 return false;
605 }
deadbeefb5cb19b2015-11-24 00:39:12606 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36607 }
608 return is_rtp;
609}
610
611// Updates the crypto parameters of the |sdesc| according to the given
612// |bundle_group|. The crypto parameters of all the contents within the
613// |bundle_group| should be updated to use the common subset of the
614// available cryptos.
615static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
616 SessionDescription* sdesc) {
617 // The bundle should not be empty.
618 if (!sdesc || !bundle_group.FirstContentName()) {
619 return false;
620 }
621
wu@webrtc.org78187522013-10-07 23:32:02622 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36623 // Get the common cryptos.
624 const ContentNames& content_names = bundle_group.content_names();
625 CryptoParamsVec common_cryptos;
626 for (ContentNames::const_iterator it = content_names.begin();
627 it != content_names.end(); ++it) {
628 if (!IsRtpContent(sdesc, *it)) {
629 continue;
630 }
wu@webrtc.org78187522013-10-07 23:32:02631 // The common cryptos are needed if any of the content does not have DTLS
632 // enabled.
633 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
634 common_cryptos_needed = true;
635 }
henrike@webrtc.org28e20752013-07-10 00:45:36636 if (it == content_names.begin()) {
637 // Initial the common_cryptos with the first content in the bundle group.
638 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
639 return false;
640 }
641 if (common_cryptos.empty()) {
642 // If there's no crypto params, we should just return.
643 return true;
644 }
645 } else {
646 CryptoParamsVec cryptos;
647 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
648 return false;
649 }
650 PruneCryptos(cryptos, &common_cryptos);
651 }
652 }
653
wu@webrtc.org78187522013-10-07 23:32:02654 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36655 return false;
656 }
657
658 // Update to use the common cryptos.
659 for (ContentNames::const_iterator it = content_names.begin();
660 it != content_names.end(); ++it) {
661 if (!IsRtpContent(sdesc, *it)) {
662 continue;
663 }
664 ContentInfo* content = sdesc->GetContentByName(*it);
665 if (IsMediaContent(content)) {
666 MediaContentDescription* media_desc =
667 static_cast<MediaContentDescription*>(content->description);
668 if (!media_desc) {
669 return false;
670 }
671 media_desc->set_cryptos(common_cryptos);
672 }
673 }
674 return true;
675}
676
677template <class C>
678static bool ContainsRtxCodec(const std::vector<C>& codecs) {
679 typename std::vector<C>::const_iterator it;
680 for (it = codecs.begin(); it != codecs.end(); ++it) {
681 if (IsRtxCodec(*it)) {
682 return true;
683 }
684 }
685 return false;
686}
687
688template <class C>
689static bool IsRtxCodec(const C& codec) {
690 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
691}
692
deadbeef0ed85b22016-02-24 01:24:52693static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
694 const std::string& content_name) {
695 auto it = options.transport_options.find(content_name);
696 if (it == options.transport_options.end()) {
697 return TransportOptions();
698 }
699 return it->second;
700}
701
henrike@webrtc.org28e20752013-07-10 00:45:36702// Create a media content to be offered in a session-initiate,
703// according to the given options.rtcp_mux, options.is_muc,
704// options.streams, codecs, secure_transport, crypto, and streams. If we don't
705// currently have crypto (in current_cryptos) and it is enabled (in
706// secure_policy), crypto is created (according to crypto_suites). If
707// add_legacy_stream is true, and current_streams is empty, a legacy
708// stream is created. The created content is added to the offer.
709template <class C>
710static bool CreateMediaContentOffer(
711 const MediaSessionOptions& options,
712 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57713 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36714 const CryptoParamsVec* current_cryptos,
715 const std::vector<std::string>& crypto_suites,
716 const RtpHeaderExtensions& rtp_extensions,
717 bool add_legacy_stream,
718 StreamParamsVec* current_streams,
719 MediaContentDescriptionImpl<C>* offer) {
720 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36721
henrike@webrtc.orgb90991d2014-03-04 19:54:57722 if (secure_policy == SEC_REQUIRED) {
723 offer->set_crypto_required(CT_SDES);
724 }
henrike@webrtc.org28e20752013-07-10 00:45:36725 offer->set_rtcp_mux(options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 22:02:07726 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
727 offer->set_rtcp_reduced_size(true);
728 }
henrike@webrtc.org28e20752013-07-10 00:45:36729 offer->set_multistream(options.is_muc);
730 offer->set_rtp_header_extensions(rtp_extensions);
731
zhihuang8f65cdf2016-05-07 01:40:30732 if (!AddStreamParams(offer->type(), options, current_streams, offer,
733 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36734 return false;
735 }
736
737#ifdef HAVE_SRTP
738 if (secure_policy != SEC_DISABLED) {
739 if (current_cryptos) {
740 AddMediaCryptos(*current_cryptos, offer);
741 }
742 if (offer->cryptos().empty()) {
743 if (!CreateMediaCryptos(crypto_suites, offer)) {
744 return false;
745 }
746 }
747 }
748#endif
749
henrike@webrtc.orgb90991d2014-03-04 19:54:57750 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36751 return false;
752 }
753 return true;
754}
755
756template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34757static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
758 const std::string& codec1_id_str,
759 const std::vector<C>& codecs2,
760 const std::string& codec2_id_str) {
761 int codec1_id;
762 int codec2_id;
763 C codec1;
764 C codec2;
765 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
766 !rtc::FromString(codec2_id_str, &codec2_id) ||
767 !FindCodecById(codecs1, codec1_id, &codec1) ||
768 !FindCodecById(codecs2, codec2_id, &codec2)) {
769 return false;
770 }
771 return codec1.Matches(codec2);
772}
773
774template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36775static void NegotiateCodecs(const std::vector<C>& local_codecs,
776 const std::vector<C>& offered_codecs,
777 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56778 for (const C& ours : local_codecs) {
779 C theirs;
deadbeef67cf2c12016-04-13 17:07:16780 // Note that we intentionally only find one matching codec for each of our
781 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56782 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
783 C negotiated = ours;
784 negotiated.IntersectFeedbackParams(theirs);
785 if (IsRtxCodec(negotiated)) {
786 std::string offered_apt_value;
787 theirs.GetParam(kCodecParamAssociatedPayloadType, &offered_apt_value);
788 // FindMatchingCodec shouldn't return something with no apt value.
789 RTC_DCHECK(!offered_apt_value.empty());
790 negotiated.SetParam(kCodecParamAssociatedPayloadType,
791 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36792 }
Taylor Brandstetter6ec641b2016-03-05 00:47:56793 negotiated.id = theirs.id;
ossu075af922016-06-14 10:29:38794 negotiated.name = theirs.name;
Taylor Brandstetter6ec641b2016-03-05 00:47:56795 negotiated_codecs->push_back(negotiated);
henrike@webrtc.org28e20752013-07-10 00:45:36796 }
797 }
deadbeef67cf2c12016-04-13 17:07:16798 // RFC3264: Although the answerer MAY list the formats in their desired
799 // order of preference, it is RECOMMENDED that unless there is a
800 // specific reason, the answerer list formats in the same relative order
801 // they were present in the offer.
802 std::unordered_map<int, int> payload_type_preferences;
803 int preference = static_cast<int>(offered_codecs.size() + 1);
804 for (const C& codec : offered_codecs) {
805 payload_type_preferences[codec.id] = preference--;
806 }
807 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
808 [&payload_type_preferences](const C& a, const C& b) {
809 return payload_type_preferences[a.id] >
810 payload_type_preferences[b.id];
811 });
henrike@webrtc.org28e20752013-07-10 00:45:36812}
813
Taylor Brandstetter6ec641b2016-03-05 00:47:56814// Finds a codec in |codecs2| that matches |codec_to_match|, which is
815// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
816// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36817template <class C>
Taylor Brandstetter6ec641b2016-03-05 00:47:56818static bool FindMatchingCodec(const std::vector<C>& codecs1,
819 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36820 const C& codec_to_match,
821 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56822 for (const C& potential_match : codecs2) {
823 if (potential_match.Matches(codec_to_match)) {
824 if (IsRtxCodec(codec_to_match)) {
825 std::string apt_value_1;
826 std::string apt_value_2;
827 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
828 &apt_value_1) ||
829 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
830 &apt_value_2)) {
831 LOG(LS_WARNING) << "RTX missing associated payload type.";
832 continue;
833 }
834 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
835 apt_value_2)) {
836 continue;
837 }
838 }
839 if (found_codec) {
840 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36841 }
842 return true;
843 }
844 }
845 return false;
846}
847
848// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
849// already exist in |offered_codecs| and ensure the payload types don't
850// collide.
851template <class C>
852static void FindCodecsToOffer(
853 const std::vector<C>& reference_codecs,
854 std::vector<C>* offered_codecs,
855 UsedPayloadTypes* used_pltypes) {
856
henrike@webrtc.org28e20752013-07-10 00:45:36857 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56858 for (const C& reference_codec : reference_codecs) {
859 if (!IsRtxCodec(reference_codec) &&
860 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
861 reference_codec, nullptr)) {
862 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36863 used_pltypes->FindAndSetIdUsed(&codec);
864 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36865 }
866 }
867
868 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:56869 for (const C& reference_codec : reference_codecs) {
870 if (IsRtxCodec(reference_codec) &&
871 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
872 reference_codec, nullptr)) {
873 C rtx_codec = reference_codec;
874
875 std::string associated_pt_str;
876 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
877 &associated_pt_str)) {
878 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
879 << " is missing an associated payload type.";
880 continue;
881 }
882
883 int associated_pt;
884 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
885 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
886 << " of RTX codec " << rtx_codec.name
887 << " to an integer.";
888 continue;
889 }
890
891 // Find the associated reference codec for the reference RTX codec.
892 C associated_codec;
893 if (!FindCodecById(reference_codecs, associated_pt, &associated_codec)) {
894 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
895 << associated_pt << " for RTX codec " << rtx_codec.name
896 << ".";
897 continue;
898 }
899
900 // Find a codec in the offered list that matches the reference codec.
901 // Its payload type may be different than the reference codec.
902 C matching_codec;
903 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
904 associated_codec, &matching_codec)) {
905 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec.name
906 << " codec.";
907 continue;
908 }
909
910 rtx_codec.params[kCodecParamAssociatedPayloadType] =
911 rtc::ToString(matching_codec.id);
912 used_pltypes->FindAndSetIdUsed(&rtx_codec);
913 offered_codecs->push_back(rtx_codec);
914 }
henrike@webrtc.org28e20752013-07-10 00:45:36915 }
916}
917
henrike@webrtc.org28e20752013-07-10 00:45:36918static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 18:24:55919 const webrtc::RtpExtension& ext_to_match,
920 webrtc::RtpExtension* found_extension) {
henrike@webrtc.org28e20752013-07-10 00:45:36921 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
922 it != extensions.end(); ++it) {
923 // We assume that all URIs are given in a canonical format.
924 if (it->uri == ext_to_match.uri) {
925 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59926 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36927 }
928 return true;
929 }
930 }
931 return false;
932}
933
deadbeefa5b273a2015-08-21 00:30:13934// Iterates through |offered_extensions|, adding each one to |all_extensions|
935// and |used_ids|, and resolving ID conflicts. If an offered extension has the
936// same URI as one in |all_extensions|, it will re-use the same ID and won't be
937// treated as a conflict.
938static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
939 RtpHeaderExtensions* all_extensions,
940 UsedRtpHeaderExtensionIds* used_ids) {
941 for (auto& extension : *offered_extensions) {
isheriff6f8d6862016-05-26 18:24:55942 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-21 00:30:13943 if (FindByUri(*all_extensions, extension, &existing)) {
944 extension.id = existing.id;
945 } else {
946 used_ids->FindAndSetIdUsed(&extension);
947 all_extensions->push_back(extension);
948 }
949 }
950}
951
952// Adds |reference_extensions| to |offered_extensions|, while updating
953// |all_extensions| and |used_ids|.
954static void FindRtpHdrExtsToOffer(
955 const RtpHeaderExtensions& reference_extensions,
956 RtpHeaderExtensions* offered_extensions,
957 RtpHeaderExtensions* all_extensions,
958 UsedRtpHeaderExtensionIds* used_ids) {
959 for (auto reference_extension : reference_extensions) {
960 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
isheriff6f8d6862016-05-26 18:24:55961 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-21 00:30:13962 if (FindByUri(*all_extensions, reference_extension, &existing)) {
963 offered_extensions->push_back(existing);
964 } else {
965 used_ids->FindAndSetIdUsed(&reference_extension);
966 all_extensions->push_back(reference_extension);
967 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59968 }
henrike@webrtc.org28e20752013-07-10 00:45:36969 }
970 }
971}
972
973static void NegotiateRtpHeaderExtensions(
974 const RtpHeaderExtensions& local_extensions,
975 const RtpHeaderExtensions& offered_extensions,
976 RtpHeaderExtensions* negotiated_extenstions) {
977 RtpHeaderExtensions::const_iterator ours;
978 for (ours = local_extensions.begin();
979 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 18:24:55980 webrtc::RtpExtension theirs;
henrike@webrtc.org28e20752013-07-10 00:45:36981 if (FindByUri(offered_extensions, *ours, &theirs)) {
982 // We respond with their RTP header extension id.
983 negotiated_extenstions->push_back(theirs);
984 }
985 }
986}
987
988static void StripCNCodecs(AudioCodecs* audio_codecs) {
989 AudioCodecs::iterator iter = audio_codecs->begin();
990 while (iter != audio_codecs->end()) {
991 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
992 iter = audio_codecs->erase(iter);
993 } else {
994 ++iter;
995 }
996 }
997}
998
999// Create a media content to be answered in a session-accept,
1000// according to the given options.rtcp_mux, options.streams, codecs,
1001// crypto, and streams. If we don't currently have crypto (in
1002// current_cryptos) and it is enabled (in secure_policy), crypto is
1003// created (according to crypto_suites). If add_legacy_stream is
1004// true, and current_streams is empty, a legacy stream is created.
1005// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1006// from the incoming session-initiate. If the negotiation fails, this
1007// method returns false. The created content is added to the offer.
1008template <class C>
1009static bool CreateMediaContentAnswer(
1010 const MediaContentDescriptionImpl<C>* offer,
1011 const MediaSessionOptions& options,
1012 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:571013 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:361014 const CryptoParamsVec* current_cryptos,
1015 const RtpHeaderExtensions& local_rtp_extenstions,
1016 StreamParamsVec* current_streams,
1017 bool add_legacy_stream,
1018 bool bundle_enabled,
1019 MediaContentDescriptionImpl<C>* answer) {
1020 std::vector<C> negotiated_codecs;
1021 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1022 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:361023 answer->set_protocol(offer->protocol());
1024 RtpHeaderExtensions negotiated_rtp_extensions;
1025 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1026 offer->rtp_header_extensions(),
1027 &negotiated_rtp_extensions);
1028 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1029
1030 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 22:02:071031 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1032 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1033 }
henrike@webrtc.org28e20752013-07-10 00:45:361034
1035 if (sdes_policy != SEC_DISABLED) {
1036 CryptoParams crypto;
1037 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
1038 if (current_cryptos) {
1039 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1040 }
1041 answer->AddCrypto(crypto);
1042 }
1043 }
1044
1045 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:571046 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:361047 return false;
1048 }
1049
zhihuang8f65cdf2016-05-07 01:40:301050 if (!AddStreamParams(answer->type(), options, current_streams, answer,
1051 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:361052 return false; // Something went seriously wrong.
1053 }
1054
1055 // Make sure the answer media content direction is per default set as
1056 // described in RFC3264 section 6.1.
ossu075af922016-06-14 10:29:381057 const bool is_data = !IsRtpProtocol(answer->protocol());
1058 const bool has_send_streams = !answer->streams().empty();
1059 const bool wants_send = has_send_streams || is_data;
1060 const bool recv_audio =
1061 answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
1062 const bool recv_video =
1063 answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
1064 const bool recv_data =
1065 answer->type() == cricket::MEDIA_TYPE_DATA;
1066 const bool wants_receive = recv_audio || recv_video || recv_data;
henrike@webrtc.org28e20752013-07-10 00:45:361067
ossu075af922016-06-14 10:29:381068 auto offer_rtd =
1069 RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
1070 auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
1071 answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
1072 .ToMediaContentDirection());
henrike@webrtc.org28e20752013-07-10 00:45:361073 return true;
1074}
1075
zhihuangcf5b37c2016-05-05 18:44:351076static bool IsDtlsRtp(const std::string& protocol) {
1077 // Most-likely values first.
1078 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1079 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1080}
1081
1082static bool IsPlainRtp(const std::string& protocol) {
1083 // Most-likely values first.
1084 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1085 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1086}
1087
1088static bool IsDtlsSctp(const std::string& protocol) {
1089 return protocol == "DTLS/SCTP";
1090}
1091
1092static bool IsPlainSctp(const std::string& protocol) {
1093 return protocol == "SCTP";
1094}
1095
henrike@webrtc.org28e20752013-07-10 00:45:361096static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591097 const std::string& protocol,
1098 bool secure_transport) {
zhihuangcf5b37c2016-05-05 18:44:351099 // Since not all applications serialize and deserialize the media protocol,
1100 // we will have to accept |protocol| to be empty.
1101 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:361102 return true;
1103 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591104
zhihuangcf5b37c2016-05-05 18:44:351105 if (type == MEDIA_TYPE_DATA) {
1106 // Check for SCTP, but also for RTP for RTP-based data channels.
1107 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1108 if (secure_transport) {
1109 // Most likely scenarios first.
1110 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1111 IsPlainRtp(protocol);
1112 } else {
1113 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1114 }
1115 }
1116
1117 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1118 // JSEP specifies.
1119 if (secure_transport) {
1120 // Most likely scenarios first.
1121 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1122 } else {
1123 return IsPlainRtp(protocol);
1124 }
henrike@webrtc.org28e20752013-07-10 00:45:361125}
1126
1127static void SetMediaProtocol(bool secure_transport,
1128 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 19:20:531129 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:361130 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 19:20:531131 else if (secure_transport)
1132 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:361133 else
1134 desc->set_protocol(kMediaProtocolAvpf);
1135}
1136
mallinath@webrtc.org19f27e62013-10-13 17:18:271137// Gets the TransportInfo of the given |content_name| from the
1138// |current_description|. If doesn't exist, returns a new one.
1139static const TransportDescription* GetTransportDescription(
1140 const std::string& content_name,
1141 const SessionDescription* current_description) {
1142 const TransportDescription* desc = NULL;
1143 if (current_description) {
1144 const TransportInfo* info =
1145 current_description->GetTransportInfoByName(content_name);
1146 if (info) {
1147 desc = &info->description;
1148 }
1149 }
1150 return desc;
1151}
1152
1153// Gets the current DTLS state from the transport description.
1154static bool IsDtlsActive(
1155 const std::string& content_name,
1156 const SessionDescription* current_description) {
1157 if (!current_description)
1158 return false;
1159
1160 const ContentInfo* content =
1161 current_description->GetContentByName(content_name);
1162 if (!content)
1163 return false;
1164
1165 const TransportDescription* current_tdesc =
1166 GetTransportDescription(content_name, current_description);
1167 if (!current_tdesc)
1168 return false;
1169
1170 return current_tdesc->secure();
1171}
1172
sergeyu@chromium.org4b26e2e2014-01-15 23:15:541173std::string MediaTypeToString(MediaType type) {
1174 std::string type_str;
1175 switch (type) {
1176 case MEDIA_TYPE_AUDIO:
1177 type_str = "audio";
1178 break;
1179 case MEDIA_TYPE_VIDEO:
1180 type_str = "video";
1181 break;
1182 case MEDIA_TYPE_DATA:
1183 type_str = "data";
1184 break;
1185 default:
1186 ASSERT(false);
1187 break;
1188 }
1189 return type_str;
1190}
1191
ossu075af922016-06-14 10:29:381192std::string MediaContentDirectionToString(MediaContentDirection direction) {
1193 std::string dir_str;
1194 switch (direction) {
1195 case MD_INACTIVE:
1196 dir_str = "inactive";
1197 break;
1198 case MD_SENDONLY:
1199 dir_str = "sendonly";
1200 break;
1201 case MD_RECVONLY:
1202 dir_str = "recvonly";
1203 break;
1204 case MD_SENDRECV:
1205 dir_str = "sendrecv";
1206 break;
1207 default:
1208 ASSERT(false);
1209 break;
1210 }
1211
1212 return dir_str;
1213}
1214
jiayl@webrtc.org742922b2014-10-07 21:32:431215void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:361216 const std::string& id,
1217 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:431218 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:121219}
1220
jiayl@webrtc.org742922b2014-10-07 21:32:431221void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:121222 const std::string& id,
1223 const std::string& sync_label,
1224 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:431225 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:121226}
1227
jiayl@webrtc.org742922b2014-10-07 21:32:431228void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:121229 MediaType type,
1230 const std::string& id,
1231 const std::string& sync_label,
1232 int num_sim_layers) {
1233 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:361234
henrike@webrtc.org28e20752013-07-10 00:45:361235 // If we haven't already set the data_channel_type, and we add a
1236 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:431237 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:361238 data_channel_type = DCT_RTP;
1239}
1240
jiayl@webrtc.org742922b2014-10-07 21:32:431241void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:361242 const std::string& id) {
1243 Streams::iterator stream_it = streams.begin();
1244 for (; stream_it != streams.end(); ++stream_it) {
1245 if (stream_it->type == type && stream_it->id == id) {
1246 streams.erase(stream_it);
1247 return;
1248 }
1249 }
1250 ASSERT(false);
1251}
1252
jiayl@webrtc.org742922b2014-10-07 21:32:431253bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1254 Streams::const_iterator stream_it = streams.begin();
1255 for (; stream_it != streams.end(); ++stream_it) {
1256 if (stream_it->type == type) {
1257 return true;
1258 }
1259 }
1260 return false;
1261}
1262
henrike@webrtc.org28e20752013-07-10 00:45:361263MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1264 const TransportDescriptionFactory* transport_desc_factory)
1265 : secure_(SEC_DISABLED),
1266 add_legacy_(true),
1267 transport_desc_factory_(transport_desc_factory) {
1268}
1269
1270MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1271 ChannelManager* channel_manager,
1272 const TransportDescriptionFactory* transport_desc_factory)
1273 : secure_(SEC_DISABLED),
1274 add_legacy_(true),
1275 transport_desc_factory_(transport_desc_factory) {
ossudedfd282016-06-14 14:12:391276 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1277 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1278 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:361279 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1280 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1281 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1282 channel_manager->GetSupportedDataCodecs(&data_codecs_);
ossudedfd282016-06-14 14:12:391283 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
1284 &audio_sendrecv_codecs_);
ossu075af922016-06-14 10:29:381285}
1286
ossudedfd282016-06-14 14:12:391287const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1288 const {
ossu075af922016-06-14 10:29:381289 return audio_sendrecv_codecs_;
1290}
1291
1292const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1293 return audio_send_codecs_;
1294}
1295
1296const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1297 return audio_recv_codecs_;
1298}
1299
1300void MediaSessionDescriptionFactory::set_audio_codecs(
1301 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1302 audio_send_codecs_ = send_codecs;
1303 audio_recv_codecs_ = recv_codecs;
1304 audio_sendrecv_codecs_.clear();
1305 // Use NegotiateCodecs to merge our codec lists, since the operation is
1306 // essentially the same. Put send_codecs as the offered_codecs, which is the
1307 // order we'd like to follow. The reasoning is that encoding is usually more
1308 // expensive than decoding, and prioritizing a codec in the send list probably
1309 // means it's a codec we can handle efficiently.
1310 NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
henrike@webrtc.org28e20752013-07-10 00:45:361311}
1312
1313SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1314 const MediaSessionOptions& options,
1315 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 22:18:211316 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:361317
1318 StreamParamsVec current_streams;
1319 GetCurrentStreamParams(current_description, &current_streams);
1320
ossu075af922016-06-14 10:29:381321 const bool wants_send =
1322 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1323 const AudioCodecs& supported_audio_codecs =
1324 GetAudioCodecsForOffer({wants_send, options.recv_audio});
1325
henrike@webrtc.org28e20752013-07-10 00:45:361326 AudioCodecs audio_codecs;
1327 VideoCodecs video_codecs;
1328 DataCodecs data_codecs;
ossu075af922016-06-14 10:29:381329 GetCodecsToOffer(current_description, supported_audio_codecs,
1330 video_codecs_, data_codecs_,
1331 &audio_codecs, &video_codecs, &data_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:361332
1333 if (!options.vad_enabled) {
1334 // If application doesn't want CN codecs in offer.
1335 StripCNCodecs(&audio_codecs);
1336 }
1337
1338 RtpHeaderExtensions audio_rtp_extensions;
1339 RtpHeaderExtensions video_rtp_extensions;
1340 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1341 &video_rtp_extensions);
1342
jiayl@webrtc.orge7d47a12014-08-05 19:19:051343 bool audio_added = false;
1344 bool video_added = false;
1345 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:271346
jiayl@webrtc.orge7d47a12014-08-05 19:19:051347 // Iterate through the contents of |current_description| to maintain the order
1348 // of the m-lines in the new offer.
1349 if (current_description) {
1350 ContentInfos::const_iterator it = current_description->contents().begin();
1351 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:151352 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:051353 if (!AddAudioContentForOffer(options, current_description,
1354 audio_rtp_extensions, audio_codecs,
1355 &current_streams, offer.get())) {
1356 return NULL;
1357 }
1358 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:151359 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:051360 if (!AddVideoContentForOffer(options, current_description,
1361 video_rtp_extensions, video_codecs,
1362 &current_streams, offer.get())) {
1363 return NULL;
1364 }
1365 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:151366 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:041367 MediaSessionOptions options_copy(options);
1368 if (IsSctp(static_cast<const MediaContentDescription*>(
1369 it->description))) {
1370 options_copy.data_channel_type = DCT_SCTP;
1371 }
1372 if (!AddDataContentForOffer(options_copy, current_description,
1373 &data_codecs, &current_streams,
1374 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:051375 return NULL;
1376 }
1377 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:151378 } else {
1379 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051380 }
henrike@webrtc.org28e20752013-07-10 00:45:361381 }
1382 }
jiayl@webrtc.org742922b2014-10-07 21:32:431383
jiayl@webrtc.orge7d47a12014-08-05 19:19:051384 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:431385 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:051386 !AddAudioContentForOffer(options, current_description,
1387 audio_rtp_extensions, audio_codecs,
1388 &current_streams, offer.get())) {
1389 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:361390 }
jiayl@webrtc.org742922b2014-10-07 21:32:431391 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:051392 !AddVideoContentForOffer(options, current_description,
1393 video_rtp_extensions, video_codecs,
1394 &current_streams, offer.get())) {
1395 return NULL;
1396 }
1397 if (!data_added && options.has_data() &&
1398 !AddDataContentForOffer(options, current_description, &data_codecs,
1399 &current_streams, offer.get())) {
1400 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:361401 }
1402
1403 // Bundle the contents together, if we've been asked to do so, and update any
1404 // parameters that need to be tweaked for BUNDLE.
1405 if (options.bundle_enabled) {
1406 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1407 for (ContentInfos::const_iterator content = offer->contents().begin();
1408 content != offer->contents().end(); ++content) {
1409 offer_bundle.AddContentName(content->name);
1410 }
1411 offer->AddGroup(offer_bundle);
1412 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1413 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1414 return NULL;
1415 }
1416 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1417 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1418 return NULL;
1419 }
1420 }
1421
1422 return offer.release();
1423}
1424
1425SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1426 const SessionDescription* offer, const MediaSessionOptions& options,
1427 const SessionDescription* current_description) const {
1428 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 17:07:161429 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1430 // from the offer in the answer.
kwiberg31022942016-03-11 22:18:211431 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:361432
1433 StreamParamsVec current_streams;
1434 GetCurrentStreamParams(current_description, &current_streams);
1435
jiayl@webrtc.orge7d47a12014-08-05 19:19:051436 if (offer) {
1437 ContentInfos::const_iterator it = offer->contents().begin();
1438 for (; it != offer->contents().end(); ++it) {
1439 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1440 if (!AddAudioContentForAnswer(offer, options, current_description,
1441 &current_streams, answer.get())) {
1442 return NULL;
1443 }
1444 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1445 if (!AddVideoContentForAnswer(offer, options, current_description,
1446 &current_streams, answer.get())) {
1447 return NULL;
1448 }
1449 } else {
1450 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1451 if (!AddDataContentForAnswer(offer, options, current_description,
1452 &current_streams, answer.get())) {
1453 return NULL;
1454 }
henrike@webrtc.org28e20752013-07-10 00:45:361455 }
henrike@webrtc.org28e20752013-07-10 00:45:361456 }
henrike@webrtc.org28e20752013-07-10 00:45:361457 }
1458
1459 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1460 // group in the answer with the appropriate content names.
1461 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1462 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1463 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1464 for (ContentInfos::const_iterator content = answer->contents().begin();
1465 content != answer->contents().end(); ++content) {
1466 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1467 answer_bundle.AddContentName(content->name);
1468 }
1469 }
1470 if (answer_bundle.FirstContentName()) {
1471 answer->AddGroup(answer_bundle);
1472
1473 // Share the same ICE credentials and crypto params across all contents,
1474 // as BUNDLE requires.
1475 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1476 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1477 return NULL;
1478 }
1479
1480 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1481 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1482 return NULL;
1483 }
1484 }
1485 }
1486
1487 return answer.release();
1488}
1489
ossu075af922016-06-14 10:29:381490const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1491 const RtpTransceiverDirection& direction) const {
1492 // If stream is inactive - generate list as if sendrecv.
1493 if (direction.send == direction.recv) {
1494 return audio_sendrecv_codecs_;
1495 } else if (direction.send) {
1496 return audio_send_codecs_;
1497 } else {
1498 return audio_recv_codecs_;
1499 }
1500}
1501
1502const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1503 const RtpTransceiverDirection& offer,
1504 const RtpTransceiverDirection& answer) const {
1505 // For inactive and sendrecv answers, generate lists as if we were to accept
1506 // the offer's direction. See RFC 3264 Section 6.1.
1507 if (answer.send == answer.recv) {
1508 if (offer.send == offer.recv) {
1509 return audio_sendrecv_codecs_;
1510 } else if (offer.send) {
1511 return audio_recv_codecs_;
1512 } else {
1513 return audio_send_codecs_;
1514 }
1515 } else if (answer.send) {
1516 return audio_send_codecs_;
1517 } else {
1518 return audio_recv_codecs_;
1519 }
1520}
1521
henrike@webrtc.org28e20752013-07-10 00:45:361522void MediaSessionDescriptionFactory::GetCodecsToOffer(
1523 const SessionDescription* current_description,
ossu075af922016-06-14 10:29:381524 const AudioCodecs& supported_audio_codecs,
1525 const VideoCodecs& supported_video_codecs,
1526 const DataCodecs& supported_data_codecs,
henrike@webrtc.org28e20752013-07-10 00:45:361527 AudioCodecs* audio_codecs,
1528 VideoCodecs* video_codecs,
1529 DataCodecs* data_codecs) const {
1530 UsedPayloadTypes used_pltypes;
1531 audio_codecs->clear();
1532 video_codecs->clear();
1533 data_codecs->clear();
1534
1535
1536 // First - get all codecs from the current description if the media type
1537 // is used.
1538 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1539 // type is added.
1540 if (current_description) {
1541 const AudioContentDescription* audio =
1542 GetFirstAudioContentDescription(current_description);
1543 if (audio) {
1544 *audio_codecs = audio->codecs();
1545 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1546 }
1547 const VideoContentDescription* video =
1548 GetFirstVideoContentDescription(current_description);
1549 if (video) {
1550 *video_codecs = video->codecs();
1551 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1552 }
1553 const DataContentDescription* data =
1554 GetFirstDataContentDescription(current_description);
1555 if (data) {
1556 *data_codecs = data->codecs();
1557 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1558 }
1559 }
1560
1561 // Add our codecs that are not in |current_description|.
ossu075af922016-06-14 10:29:381562 FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
1563 &used_pltypes);
1564 FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
1565 &used_pltypes);
1566 FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
1567 &used_pltypes);
henrike@webrtc.org28e20752013-07-10 00:45:361568}
1569
1570void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1571 const SessionDescription* current_description,
1572 RtpHeaderExtensions* audio_extensions,
1573 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:591574 // All header extensions allocated from the same range to avoid potential
1575 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:361576 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-21 00:30:131577 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:361578 audio_extensions->clear();
1579 video_extensions->clear();
1580
1581 // First - get all extensions from the current description if the media type
1582 // is used.
1583 // Add them to |used_ids| so the local ids are not reused if a new media
1584 // type is added.
1585 if (current_description) {
1586 const AudioContentDescription* audio =
1587 GetFirstAudioContentDescription(current_description);
1588 if (audio) {
1589 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-21 00:30:131590 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:361591 }
1592 const VideoContentDescription* video =
1593 GetFirstVideoContentDescription(current_description);
1594 if (video) {
1595 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-21 00:30:131596 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:361597 }
1598 }
1599
1600 // Add our default RTP header extensions that are not in
1601 // |current_description|.
deadbeefa5b273a2015-08-21 00:30:131602 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1603 &all_extensions, &used_ids);
1604 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1605 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:361606}
1607
1608bool MediaSessionDescriptionFactory::AddTransportOffer(
1609 const std::string& content_name,
1610 const TransportOptions& transport_options,
1611 const SessionDescription* current_desc,
1612 SessionDescription* offer_desc) const {
1613 if (!transport_desc_factory_)
1614 return false;
1615 const TransportDescription* current_tdesc =
1616 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 22:18:211617 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:361618 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1619 bool ret = (new_tdesc.get() != NULL &&
1620 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1621 if (!ret) {
1622 LOG(LS_ERROR)
1623 << "Failed to AddTransportOffer, content name=" << content_name;
1624 }
1625 return ret;
1626}
1627
1628TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1629 const std::string& content_name,
1630 const SessionDescription* offer_desc,
1631 const TransportOptions& transport_options,
1632 const SessionDescription* current_desc) const {
1633 if (!transport_desc_factory_)
1634 return NULL;
1635 const TransportDescription* offer_tdesc =
1636 GetTransportDescription(content_name, offer_desc);
1637 const TransportDescription* current_tdesc =
1638 GetTransportDescription(content_name, current_desc);
1639 return
1640 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1641 current_tdesc);
1642}
1643
1644bool MediaSessionDescriptionFactory::AddTransportAnswer(
1645 const std::string& content_name,
1646 const TransportDescription& transport_desc,
1647 SessionDescription* answer_desc) const {
1648 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1649 transport_desc))) {
1650 LOG(LS_ERROR)
1651 << "Failed to AddTransportAnswer, content name=" << content_name;
1652 return false;
1653 }
1654 return true;
1655}
1656
jiayl@webrtc.orge7d47a12014-08-05 19:19:051657bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1658 const MediaSessionOptions& options,
1659 const SessionDescription* current_description,
1660 const RtpHeaderExtensions& audio_rtp_extensions,
1661 const AudioCodecs& audio_codecs,
1662 StreamParamsVec* current_streams,
1663 SessionDescription* desc) const {
deadbeef44f08192015-12-16 00:20:091664 const ContentInfo* current_audio_content =
1665 GetFirstAudioContent(current_description);
1666 std::string content_name =
1667 current_audio_content ? current_audio_content->name : CN_AUDIO;
1668
jiayl@webrtc.orge7d47a12014-08-05 19:19:051669 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-16 00:20:091670 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1671 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051672
kwiberg31022942016-03-11 22:18:211673 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051674 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-19 03:41:531675 GetSupportedAudioCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051676 if (!CreateMediaContentOffer(
1677 options,
1678 audio_codecs,
1679 sdes_policy,
1680 GetCryptos(GetFirstAudioContentDescription(current_description)),
1681 crypto_suites,
1682 audio_rtp_extensions,
1683 add_legacy_,
1684 current_streams,
1685 audio.get())) {
1686 return false;
1687 }
1688 audio->set_lang(lang_);
1689
1690 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1691 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:151692
ossu075af922016-06-14 10:29:381693 auto offer_rtd =
1694 RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
1695 audio->set_direction(offer_rtd.ToMediaContentDirection());
jiayl@webrtc.org742922b2014-10-07 21:32:431696
deadbeef44f08192015-12-16 00:20:091697 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-24 01:24:521698 if (!AddTransportOffer(content_name,
1699 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:051700 current_description, desc)) {
1701 return false;
1702 }
1703
1704 return true;
1705}
1706
1707bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1708 const MediaSessionOptions& options,
1709 const SessionDescription* current_description,
1710 const RtpHeaderExtensions& video_rtp_extensions,
1711 const VideoCodecs& video_codecs,
1712 StreamParamsVec* current_streams,
1713 SessionDescription* desc) const {
deadbeef44f08192015-12-16 00:20:091714 const ContentInfo* current_video_content =
1715 GetFirstVideoContent(current_description);
1716 std::string content_name =
1717 current_video_content ? current_video_content->name : CN_VIDEO;
1718
jiayl@webrtc.orge7d47a12014-08-05 19:19:051719 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-16 00:20:091720 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1721 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051722
kwiberg31022942016-03-11 22:18:211723 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051724 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-19 03:41:531725 GetSupportedVideoCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051726 if (!CreateMediaContentOffer(
1727 options,
1728 video_codecs,
1729 sdes_policy,
1730 GetCryptos(GetFirstVideoContentDescription(current_description)),
1731 crypto_suites,
1732 video_rtp_extensions,
1733 add_legacy_,
1734 current_streams,
1735 video.get())) {
1736 return false;
1737 }
1738
1739 video->set_bandwidth(options.video_bandwidth);
1740
1741 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1742 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:431743
deadbeefc80741f2015-10-22 20:14:451744 if (!video->streams().empty()) {
1745 if (options.recv_video) {
1746 video->set_direction(MD_SENDRECV);
1747 } else {
1748 video->set_direction(MD_SENDONLY);
1749 }
1750 } else {
1751 if (options.recv_video) {
1752 video->set_direction(MD_RECVONLY);
1753 } else {
1754 video->set_direction(MD_INACTIVE);
1755 }
jiayl@webrtc.org742922b2014-10-07 21:32:431756 }
1757
deadbeef44f08192015-12-16 00:20:091758 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-24 01:24:521759 if (!AddTransportOffer(content_name,
1760 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:051761 current_description, desc)) {
1762 return false;
1763 }
1764
1765 return true;
1766}
1767
1768bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1769 const MediaSessionOptions& options,
1770 const SessionDescription* current_description,
1771 DataCodecs* data_codecs,
1772 StreamParamsVec* current_streams,
1773 SessionDescription* desc) const {
1774 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1775
kwiberg31022942016-03-11 22:18:211776 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051777 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1778
1779 FilterDataCodecs(data_codecs, is_sctp);
1780
deadbeef44f08192015-12-16 00:20:091781 const ContentInfo* current_data_content =
1782 GetFirstDataContent(current_description);
1783 std::string content_name =
1784 current_data_content ? current_data_content->name : CN_DATA;
1785
jiayl@webrtc.orge7d47a12014-08-05 19:19:051786 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-16 00:20:091787 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1788 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:051789 std::vector<std::string> crypto_suites;
1790 if (is_sctp) {
1791 // SDES doesn't make sense for SCTP, so we disable it, and we only
1792 // get SDES crypto suites for RTP-based data channels.
1793 sdes_policy = cricket::SEC_DISABLED;
1794 // Unlike SetMediaProtocol below, we need to set the protocol
1795 // before we call CreateMediaContentOffer. Otherwise,
1796 // CreateMediaContentOffer won't know this is SCTP and will
1797 // generate SSRCs rather than SIDs.
1798 data->set_protocol(
1799 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1800 } else {
Guo-wei Shieh521ed7b2015-11-19 03:41:531801 GetSupportedDataCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051802 }
1803
1804 if (!CreateMediaContentOffer(
1805 options,
1806 *data_codecs,
1807 sdes_policy,
1808 GetCryptos(GetFirstDataContentDescription(current_description)),
1809 crypto_suites,
1810 RtpHeaderExtensions(),
1811 add_legacy_,
1812 current_streams,
1813 data.get())) {
1814 return false;
1815 }
1816
1817 if (is_sctp) {
deadbeef44f08192015-12-16 00:20:091818 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051819 } else {
1820 data->set_bandwidth(options.data_bandwidth);
1821 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-16 00:20:091822 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051823 }
deadbeef0ed85b22016-02-24 01:24:521824 if (!AddTransportOffer(content_name,
1825 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:051826 current_description, desc)) {
1827 return false;
1828 }
1829 return true;
1830}
1831
1832bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1833 const SessionDescription* offer,
1834 const MediaSessionOptions& options,
1835 const SessionDescription* current_description,
1836 StreamParamsVec* current_streams,
1837 SessionDescription* answer) const {
1838 const ContentInfo* audio_content = GetFirstAudioContent(offer);
ossu075af922016-06-14 10:29:381839 const AudioContentDescription* offer_audio =
1840 static_cast<const AudioContentDescription*>(audio_content->description);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051841
kwiberg31022942016-03-11 22:18:211842 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-24 01:24:521843 audio_content->name, offer,
1844 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:051845 if (!audio_transport) {
1846 return false;
1847 }
1848
ossu075af922016-06-14 10:29:381849 // Pick codecs based on the requested communications direction in the offer.
1850 const bool wants_send =
1851 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1852 auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
1853 auto offer_rtd =
1854 RtpTransceiverDirection::FromMediaContentDirection(
1855 offer_audio->direction());
1856 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
1857 AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051858 if (!options.vad_enabled) {
1859 StripCNCodecs(&audio_codecs);
1860 }
1861
1862 bool bundle_enabled =
1863 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 22:18:211864 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:051865 new AudioContentDescription());
1866 // Do not require or create SDES cryptos if DTLS is used.
1867 cricket::SecurePolicy sdes_policy =
1868 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1869 if (!CreateMediaContentAnswer(
ossu075af922016-06-14 10:29:381870 offer_audio,
jiayl@webrtc.orge7d47a12014-08-05 19:19:051871 options,
1872 audio_codecs,
1873 sdes_policy,
1874 GetCryptos(GetFirstAudioContentDescription(current_description)),
1875 audio_rtp_extensions_,
1876 current_streams,
1877 add_legacy_,
1878 bundle_enabled,
1879 audio_answer.get())) {
1880 return false; // Fails the session setup.
1881 }
1882
jiayl@webrtc.org742922b2014-10-07 21:32:431883 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:051884 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1885 audio_answer->protocol(),
1886 audio_transport->secure());
1887 if (!rejected) {
1888 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1889 } else {
1890 // RFC 3264
1891 // The answer MUST contain the same number of m-lines as the offer.
1892 LOG(LS_INFO) << "Audio is not supported in the answer.";
1893 }
1894
1895 answer->AddContent(audio_content->name, audio_content->type, rejected,
1896 audio_answer.release());
1897 return true;
1898}
1899
1900bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1901 const SessionDescription* offer,
1902 const MediaSessionOptions& options,
1903 const SessionDescription* current_description,
1904 StreamParamsVec* current_streams,
1905 SessionDescription* answer) const {
1906 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 22:18:211907 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-24 01:24:521908 video_content->name, offer,
1909 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:051910 if (!video_transport) {
1911 return false;
1912 }
1913
kwiberg31022942016-03-11 22:18:211914 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:051915 new VideoContentDescription());
1916 // Do not require or create SDES cryptos if DTLS is used.
1917 cricket::SecurePolicy sdes_policy =
1918 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1919 bool bundle_enabled =
1920 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1921 if (!CreateMediaContentAnswer(
1922 static_cast<const VideoContentDescription*>(
1923 video_content->description),
1924 options,
1925 video_codecs_,
1926 sdes_policy,
1927 GetCryptos(GetFirstVideoContentDescription(current_description)),
1928 video_rtp_extensions_,
1929 current_streams,
1930 add_legacy_,
1931 bundle_enabled,
1932 video_answer.get())) {
1933 return false;
1934 }
jiayl@webrtc.org742922b2014-10-07 21:32:431935 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:051936 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1937 video_answer->protocol(),
1938 video_transport->secure());
1939 if (!rejected) {
1940 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1941 answer)) {
1942 return false;
1943 }
1944 video_answer->set_bandwidth(options.video_bandwidth);
1945 } else {
1946 // RFC 3264
1947 // The answer MUST contain the same number of m-lines as the offer.
1948 LOG(LS_INFO) << "Video is not supported in the answer.";
1949 }
1950 answer->AddContent(video_content->name, video_content->type, rejected,
1951 video_answer.release());
1952 return true;
1953}
1954
1955bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1956 const SessionDescription* offer,
1957 const MediaSessionOptions& options,
1958 const SessionDescription* current_description,
1959 StreamParamsVec* current_streams,
1960 SessionDescription* answer) const {
1961 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 22:18:211962 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-24 01:24:521963 data_content->name, offer,
1964 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:051965 if (!data_transport) {
1966 return false;
1967 }
1968 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1969 std::vector<DataCodec> data_codecs(data_codecs_);
1970 FilterDataCodecs(&data_codecs, is_sctp);
1971
kwiberg31022942016-03-11 22:18:211972 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:051973 new DataContentDescription());
1974 // Do not require or create SDES cryptos if DTLS is used.
1975 cricket::SecurePolicy sdes_policy =
1976 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1977 bool bundle_enabled =
1978 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1979 if (!CreateMediaContentAnswer(
1980 static_cast<const DataContentDescription*>(
1981 data_content->description),
1982 options,
1983 data_codecs_,
1984 sdes_policy,
1985 GetCryptos(GetFirstDataContentDescription(current_description)),
1986 RtpHeaderExtensions(),
1987 current_streams,
1988 add_legacy_,
1989 bundle_enabled,
1990 data_answer.get())) {
1991 return false; // Fails the session setup.
1992 }
1993
1994 bool rejected = !options.has_data() || data_content->rejected ||
1995 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1996 data_answer->protocol(),
1997 data_transport->secure());
1998 if (!rejected) {
1999 data_answer->set_bandwidth(options.data_bandwidth);
2000 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
2001 answer)) {
2002 return false;
2003 }
2004 } else {
2005 // RFC 3264
2006 // The answer MUST contain the same number of m-lines as the offer.
2007 LOG(LS_INFO) << "Data is not supported in the answer.";
2008 }
2009 answer->AddContent(data_content->name, data_content->type, rejected,
2010 data_answer.release());
2011 return true;
2012}
2013
henrike@webrtc.org28e20752013-07-10 00:45:362014bool IsMediaContent(const ContentInfo* content) {
2015 return (content &&
2016 (content->type == NS_JINGLE_RTP ||
2017 content->type == NS_JINGLE_DRAFT_SCTP));
2018}
2019
2020bool IsAudioContent(const ContentInfo* content) {
2021 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2022}
2023
2024bool IsVideoContent(const ContentInfo* content) {
2025 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2026}
2027
2028bool IsDataContent(const ContentInfo* content) {
2029 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2030}
2031
deadbeef0ed85b22016-02-24 01:24:522032const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2033 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502034 for (const ContentInfo& content : contents) {
2035 if (IsMediaContentOfType(&content, media_type)) {
2036 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:362037 }
2038 }
deadbeef0ed85b22016-02-24 01:24:522039 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:362040}
2041
2042const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2043 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2044}
2045
2046const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2047 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2048}
2049
2050const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2051 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2052}
2053
2054static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2055 MediaType media_type) {
deadbeef0ed85b22016-02-24 01:24:522056 if (sdesc == nullptr) {
2057 return nullptr;
2058 }
henrike@webrtc.org28e20752013-07-10 00:45:362059
2060 return GetFirstMediaContent(sdesc->contents(), media_type);
2061}
2062
2063const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2064 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2065}
2066
2067const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2068 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2069}
2070
2071const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2072 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2073}
2074
2075const MediaContentDescription* GetFirstMediaContentDescription(
2076 const SessionDescription* sdesc, MediaType media_type) {
2077 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2078 const ContentDescription* description = content ? content->description : NULL;
2079 return static_cast<const MediaContentDescription*>(description);
2080}
2081
2082const AudioContentDescription* GetFirstAudioContentDescription(
2083 const SessionDescription* sdesc) {
2084 return static_cast<const AudioContentDescription*>(
2085 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2086}
2087
2088const VideoContentDescription* GetFirstVideoContentDescription(
2089 const SessionDescription* sdesc) {
2090 return static_cast<const VideoContentDescription*>(
2091 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2092}
2093
2094const DataContentDescription* GetFirstDataContentDescription(
2095 const SessionDescription* sdesc) {
2096 return static_cast<const DataContentDescription*>(
2097 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2098}
2099
Taylor Brandstetterdc4eb8c2016-05-12 15:14:502100//
2101// Non-const versions of the above functions.
2102//
2103
2104ContentInfo* GetFirstMediaContent(ContentInfos& contents,
2105 MediaType media_type) {
2106 for (ContentInfo& content : contents) {
2107 if (IsMediaContentOfType(&content, media_type)) {
2108 return &content;
2109 }
2110 }
2111 return nullptr;
2112}
2113
2114ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2115 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2116}
2117
2118ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2119 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2120}
2121
2122ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2123 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2124}
2125
2126static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2127 MediaType media_type) {
2128 if (sdesc == nullptr) {
2129 return nullptr;
2130 }
2131
2132 return GetFirstMediaContent(sdesc->contents(), media_type);
2133}
2134
2135ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2136 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2137}
2138
2139ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2140 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2141}
2142
2143ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2144 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2145}
2146
2147MediaContentDescription* GetFirstMediaContentDescription(
2148 SessionDescription* sdesc,
2149 MediaType media_type) {
2150 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2151 ContentDescription* description = content ? content->description : NULL;
2152 return static_cast<MediaContentDescription*>(description);
2153}
2154
2155AudioContentDescription* GetFirstAudioContentDescription(
2156 SessionDescription* sdesc) {
2157 return static_cast<AudioContentDescription*>(
2158 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2159}
2160
2161VideoContentDescription* GetFirstVideoContentDescription(
2162 SessionDescription* sdesc) {
2163 return static_cast<VideoContentDescription*>(
2164 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2165}
2166
2167DataContentDescription* GetFirstDataContentDescription(
2168 SessionDescription* sdesc) {
2169 return static_cast<DataContentDescription*>(
2170 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2171}
2172
henrike@webrtc.org28e20752013-07-10 00:45:362173} // namespace cricket