blob: 358f92d684e2290f8ac232ed6acdfca0fd7e7547 [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
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010011#include "webrtc/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
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000021#include "webrtc/base/helpers.h"
22#include "webrtc/base/logging.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000023#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080024#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080025#include "webrtc/media/base/mediaconstants.h"
26#include "webrtc/p2p/base/p2pconstants.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010027#include "webrtc/pc/channelmanager.h"
28#include "webrtc/pc/srtpfilter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000030#ifdef HAVE_SCTP
kjellandera96e2d72016-02-04 23:52:28 -080031#include "webrtc/media/sctp/sctpdataengine.h"
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000032#else
Peter Boström0c4e06b2015-10-07 12:23:21 +020033static const uint32_t kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000034#endif
35
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036namespace {
37const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080038
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 05:28:11 -070049} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000050
51namespace cricket {
52
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053
54// RTP Profile names
55// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
56// RFC4585
57const char kMediaProtocolAvpf[] = "RTP/AVPF";
58// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000059const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
60
deadbeeff3938292015-07-15 12:20:53 -070061// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
62// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063const char kMediaProtocolSavpf[] = "RTP/SAVPF";
64
65const char kMediaProtocolRtpPrefix[] = "RTP/";
66
67const char kMediaProtocolSctp[] = "SCTP";
68const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000069const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000070const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000071
72static bool IsMediaContentOfType(const ContentInfo* content,
73 MediaType media_type) {
74 if (!IsMediaContent(content)) {
75 return false;
76 }
77
78 const MediaContentDescription* mdesc =
79 static_cast<const MediaContentDescription*>(content->description);
80 return mdesc && mdesc->type() == media_type;
81}
82
83static bool CreateCryptoParams(int tag, const std::string& cipher,
84 CryptoParams *out) {
85 std::string key;
86 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
87
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000088 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000089 return false;
90 }
91 out->tag = tag;
92 out->cipher_suite = cipher;
93 out->key_params = kInline;
94 out->key_params += key;
95 return true;
96}
97
98#ifdef HAVE_SRTP
99static bool AddCryptoParams(const std::string& cipher_suite,
100 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000101 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102
103 out->resize(size + 1);
104 return CreateCryptoParams(size, cipher_suite, &out->at(size));
105}
106
107void AddMediaCryptos(const CryptoParamsVec& cryptos,
108 MediaContentDescription* media) {
109 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
110 crypto != cryptos.end(); ++crypto) {
111 media->AddCrypto(*crypto);
112 }
113}
114
115bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
116 MediaContentDescription* media) {
117 CryptoParamsVec cryptos;
118 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
119 it != crypto_suites.end(); ++it) {
120 if (!AddCryptoParams(*it, &cryptos)) {
121 return false;
122 }
123 }
124 AddMediaCryptos(cryptos, media);
125 return true;
126}
127#endif
128
129const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
130 if (!media) {
131 return NULL;
132 }
133 return &media->cryptos();
134}
135
136bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
137 const CryptoParams& crypto,
138 CryptoParams* out) {
139 for (CryptoParamsVec::const_iterator it = cryptos.begin();
140 it != cryptos.end(); ++it) {
141 if (crypto.Matches(*it)) {
142 *out = *it;
143 return true;
144 }
145 }
146 return false;
147}
148
149// For audio, HMAC 32 is prefered because of the low overhead.
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800150void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800152 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
153 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154#endif
155}
156
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800157void GetSupportedAudioCryptoSuiteNames(
158 std::vector<std::string>* crypto_suite_names) {
159 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
160 crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000161}
162
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800163void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) {
164 GetDefaultSrtpCryptoSuites(crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165}
166
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800167void GetSupportedVideoCryptoSuiteNames(
168 std::vector<std::string>* crypto_suite_names) {
169 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
170 crypto_suite_names);
171}
172
173void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) {
174 GetDefaultSrtpCryptoSuites(crypto_suites);
175}
176
177void GetSupportedDataCryptoSuiteNames(
178 std::vector<std::string>* crypto_suite_names) {
179 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
180 crypto_suite_names);
181}
182
183void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000184#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800185 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000186#endif
187}
188
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800189void GetDefaultSrtpCryptoSuiteNames(
190 std::vector<std::string>* crypto_suite_names) {
191 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names);
192}
193
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
195// tolerated unless bundle is enabled because it is low overhead. Pick the
196// crypto in the list that is supported.
197static bool SelectCrypto(const MediaContentDescription* offer,
198 bool bundle,
199 CryptoParams *crypto) {
200 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
201 const CryptoParamsVec& cryptos = offer->cryptos();
202
203 for (CryptoParamsVec::const_iterator i = cryptos.begin();
204 i != cryptos.end(); ++i) {
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700205 if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
206 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
207 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
209 }
210 }
211 return false;
212}
213
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000215// The generated values are added to |ssrcs|.
216// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000218 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200219 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000220 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200221 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000223 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000224 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
226 ssrcs->push_back(candidate);
227 }
228}
229
230// Returns false if we exhaust the range of SIDs.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200231static bool GenerateSctpSid(const StreamParamsVec& params_vec, uint32_t* sid) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232 if (params_vec.size() > kMaxSctpSid) {
233 LOG(LS_WARNING) <<
234 "Could not generate an SCTP SID: too many SCTP streams.";
235 return false;
236 }
237 while (true) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200238 uint32_t candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000239 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240 *sid = candidate;
241 return true;
242 }
243 }
244}
245
246static bool GenerateSctpSids(const StreamParamsVec& params_vec,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200247 std::vector<uint32_t>* sids) {
248 uint32_t sid;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 if (!GenerateSctpSid(params_vec, &sid)) {
250 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
251 return false;
252 }
253 sids->push_back(sid);
254 return true;
255}
256
257// Finds all StreamParams of all media types and attach them to stream_params.
258static void GetCurrentStreamParams(const SessionDescription* sdesc,
259 StreamParamsVec* stream_params) {
260 if (!sdesc)
261 return;
262
263 const ContentInfos& contents = sdesc->contents();
264 for (ContentInfos::const_iterator content = contents.begin();
265 content != contents.end(); ++content) {
266 if (!IsMediaContent(&*content)) {
267 continue;
268 }
269 const MediaContentDescription* media =
270 static_cast<const MediaContentDescription*>(
271 content->description);
272 const StreamParamsVec& streams = media->streams();
273 for (StreamParamsVec::const_iterator it = streams.begin();
274 it != streams.end(); ++it) {
275 stream_params->push_back(*it);
276 }
277 }
278}
279
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000280// Filters the data codecs for the data channel type.
281void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
282 // Filter RTP codec for SCTP and vice versa.
283 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
284 for (std::vector<DataCodec>::iterator iter = codecs->begin();
285 iter != codecs->end();) {
286 if (iter->id == codec_id) {
287 iter = codecs->erase(iter);
288 } else {
289 ++iter;
290 }
291 }
292}
293
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294template <typename IdStruct>
295class UsedIds {
296 public:
297 UsedIds(int min_allowed_id, int max_allowed_id)
298 : min_allowed_id_(min_allowed_id),
299 max_allowed_id_(max_allowed_id),
300 next_id_(max_allowed_id) {
301 }
302
303 // Loops through all Id in |ids| and changes its id if it is
304 // already in use by another IdStruct. Call this methods with all Id
305 // in a session description to make sure no duplicate ids exists.
306 // Note that typename Id must be a type of IdStruct.
307 template <typename Id>
308 void FindAndSetIdUsed(std::vector<Id>* ids) {
309 for (typename std::vector<Id>::iterator it = ids->begin();
310 it != ids->end(); ++it) {
311 FindAndSetIdUsed(&*it);
312 }
313 }
314
315 // Finds and sets an unused id if the |idstruct| id is already in use.
316 void FindAndSetIdUsed(IdStruct* idstruct) {
317 const int original_id = idstruct->id;
318 int new_id = idstruct->id;
319
320 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
321 // If the original id is not in range - this is an id that can't be
322 // dynamically changed.
323 return;
324 }
325
326 if (IsIdUsed(original_id)) {
327 new_id = FindUnusedId();
328 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
329 << " to " << new_id;
330 idstruct->id = new_id;
331 }
332 SetIdUsed(new_id);
333 }
334
335 private:
336 // Returns the first unused id in reverse order.
337 // This hopefully reduce the risk of more collisions. We want to change the
338 // default ids as little as possible.
339 int FindUnusedId() {
340 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
341 --next_id_;
342 }
343 ASSERT(next_id_ >= min_allowed_id_);
344 return next_id_;
345 }
346
347 bool IsIdUsed(int new_id) {
348 return id_set_.find(new_id) != id_set_.end();
349 }
350
351 void SetIdUsed(int new_id) {
352 id_set_.insert(new_id);
353 }
354
355 const int min_allowed_id_;
356 const int max_allowed_id_;
357 int next_id_;
358 std::set<int> id_set_;
359};
360
361// Helper class used for finding duplicate RTP payload types among audio, video
362// and data codecs. When bundle is used the payload types may not collide.
363class UsedPayloadTypes : public UsedIds<Codec> {
364 public:
365 UsedPayloadTypes()
366 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
367 }
368
369
370 private:
371 static const int kDynamicPayloadTypeMin = 96;
372 static const int kDynamicPayloadTypeMax = 127;
373};
374
375// Helper class used for finding duplicate RTP Header extension ids among
376// audio and video extensions.
isheriff6f8d6862016-05-26 11:24:55 -0700377class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 public:
379 UsedRtpHeaderExtensionIds()
isheriff6f8d6862016-05-26 11:24:55 -0700380 : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000381
382 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000383 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000384 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000385 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000386};
387
388static bool IsSctp(const MediaContentDescription* desc) {
389 return ((desc->protocol() == kMediaProtocolSctp) ||
390 (desc->protocol() == kMediaProtocolDtlsSctp));
391}
392
393// Adds a StreamParams for each Stream in Streams with media type
394// media_type to content_description.
395// |current_params| - All currently known StreamParams of any media type.
396template <class C>
zhihuang8f65cdf2016-05-06 18:40:30 -0700397static bool AddStreamParams(MediaType media_type,
398 const MediaSessionOptions& options,
399 StreamParamsVec* current_streams,
400 MediaContentDescriptionImpl<C>* content_description,
401 const bool add_legacy_stream) {
Noah Richards2e7a0982015-05-18 14:02:54 -0700402 const bool include_rtx_streams =
403 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404
zhihuang8f65cdf2016-05-06 18:40:30 -0700405 const MediaSessionOptions::Streams& streams = options.streams;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000406 if (streams.empty() && add_legacy_stream) {
407 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200408 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000409 if (IsSctp(content_description)) {
410 GenerateSctpSids(*current_streams, &ssrcs);
411 } else {
Noah Richards2e7a0982015-05-18 14:02:54 -0700412 int num_ssrcs = include_rtx_streams ? 2 : 1;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000413 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000414 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700415 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000416 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
417 content_description->set_multistream(true);
418 } else {
419 content_description->AddLegacyStream(ssrcs[0]);
420 }
421 return true;
422 }
423
424 MediaSessionOptions::Streams::const_iterator stream_it;
425 for (stream_it = streams.begin();
426 stream_it != streams.end(); ++stream_it) {
427 if (stream_it->type != media_type)
428 continue; // Wrong media type.
429
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000430 const StreamParams* param =
431 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 // groupid is empty for StreamParams generated using
433 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000434 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 // This is a new stream.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200436 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 if (IsSctp(content_description)) {
438 GenerateSctpSids(*current_streams, &ssrcs);
439 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000440 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 }
442 StreamParams stream_param;
443 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000444 // Add the generated ssrc.
445 for (size_t i = 0; i < ssrcs.size(); ++i) {
446 stream_param.ssrcs.push_back(ssrcs[i]);
447 }
448 if (stream_it->num_sim_layers > 1) {
449 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
450 stream_param.ssrc_groups.push_back(group);
451 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700452 // Generate extra ssrcs for include_rtx_streams case.
453 if (include_rtx_streams) {
454 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200455 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700456 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
457 &rtx_ssrcs);
458 for (size_t i = 0; i < ssrcs.size(); ++i) {
459 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
460 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000461 content_description->set_multistream(true);
462 }
zhihuang8f65cdf2016-05-06 18:40:30 -0700463 stream_param.cname = options.rtcp_cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464 stream_param.sync_label = stream_it->sync_label;
465 content_description->AddStream(stream_param);
466
467 // Store the new StreamParams in current_streams.
468 // This is necessary so that we can use the CNAME for other media types.
469 current_streams->push_back(stream_param);
470 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000471 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000472 }
473 }
474 return true;
475}
476
477// Updates the transport infos of the |sdesc| according to the given
478// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800479// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
480// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000481static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
482 SessionDescription* sdesc) {
483 // The bundle should not be empty.
484 if (!sdesc || !bundle_group.FirstContentName()) {
485 return false;
486 }
487
488 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700489 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000490 const TransportInfo* selected_transport_info =
491 sdesc->GetTransportInfoByName(selected_content_name);
492 if (!selected_transport_info) {
493 return false;
494 }
495
496 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700497 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700499 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000500 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800501 ConnectionRole selected_connection_role =
502 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 for (TransportInfos::iterator it =
504 sdesc->transport_infos().begin();
505 it != sdesc->transport_infos().end(); ++it) {
506 if (bundle_group.HasContentName(it->content_name) &&
507 it->content_name != selected_content_name) {
508 it->description.ice_ufrag = selected_ufrag;
509 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800510 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 }
512 }
513 return true;
514}
515
516// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
517// sets it to |cryptos|.
518static bool GetCryptosByName(const SessionDescription* sdesc,
519 const std::string& content_name,
520 CryptoParamsVec* cryptos) {
521 if (!sdesc || !cryptos) {
522 return false;
523 }
524
525 const ContentInfo* content = sdesc->GetContentByName(content_name);
526 if (!IsMediaContent(content) || !content->description) {
527 return false;
528 }
529
530 const MediaContentDescription* media_desc =
531 static_cast<const MediaContentDescription*>(content->description);
532 *cryptos = media_desc->cryptos();
533 return true;
534}
535
536// Predicate function used by the remove_if.
537// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
538static bool CryptoNotFound(const CryptoParams crypto,
539 const CryptoParamsVec* filter) {
540 if (filter == NULL) {
541 return true;
542 }
543 for (CryptoParamsVec::const_iterator it = filter->begin();
544 it != filter->end(); ++it) {
545 if (it->cipher_suite == crypto.cipher_suite) {
546 return false;
547 }
548 }
549 return true;
550}
551
552// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
553// which are not available in |filter|.
554static void PruneCryptos(const CryptoParamsVec& filter,
555 CryptoParamsVec* target_cryptos) {
556 if (!target_cryptos) {
557 return;
558 }
559 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
560 target_cryptos->end(),
561 bind2nd(ptr_fun(CryptoNotFound),
562 &filter)),
563 target_cryptos->end());
564}
565
deadbeefb5cb19b2015-11-23 16:39:12 -0800566static bool IsRtpProtocol(const std::string& protocol) {
567 return protocol.empty() ||
568 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
569}
570
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000571static bool IsRtpContent(SessionDescription* sdesc,
572 const std::string& content_name) {
573 bool is_rtp = false;
574 ContentInfo* content = sdesc->GetContentByName(content_name);
575 if (IsMediaContent(content)) {
576 MediaContentDescription* media_desc =
577 static_cast<MediaContentDescription*>(content->description);
578 if (!media_desc) {
579 return false;
580 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800581 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000582 }
583 return is_rtp;
584}
585
586// Updates the crypto parameters of the |sdesc| according to the given
587// |bundle_group|. The crypto parameters of all the contents within the
588// |bundle_group| should be updated to use the common subset of the
589// available cryptos.
590static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
591 SessionDescription* sdesc) {
592 // The bundle should not be empty.
593 if (!sdesc || !bundle_group.FirstContentName()) {
594 return false;
595 }
596
wu@webrtc.org78187522013-10-07 23:32:02 +0000597 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 // Get the common cryptos.
599 const ContentNames& content_names = bundle_group.content_names();
600 CryptoParamsVec common_cryptos;
601 for (ContentNames::const_iterator it = content_names.begin();
602 it != content_names.end(); ++it) {
603 if (!IsRtpContent(sdesc, *it)) {
604 continue;
605 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000606 // The common cryptos are needed if any of the content does not have DTLS
607 // enabled.
608 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
609 common_cryptos_needed = true;
610 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000611 if (it == content_names.begin()) {
612 // Initial the common_cryptos with the first content in the bundle group.
613 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
614 return false;
615 }
616 if (common_cryptos.empty()) {
617 // If there's no crypto params, we should just return.
618 return true;
619 }
620 } else {
621 CryptoParamsVec cryptos;
622 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
623 return false;
624 }
625 PruneCryptos(cryptos, &common_cryptos);
626 }
627 }
628
wu@webrtc.org78187522013-10-07 23:32:02 +0000629 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000630 return false;
631 }
632
633 // Update to use the common cryptos.
634 for (ContentNames::const_iterator it = content_names.begin();
635 it != content_names.end(); ++it) {
636 if (!IsRtpContent(sdesc, *it)) {
637 continue;
638 }
639 ContentInfo* content = sdesc->GetContentByName(*it);
640 if (IsMediaContent(content)) {
641 MediaContentDescription* media_desc =
642 static_cast<MediaContentDescription*>(content->description);
643 if (!media_desc) {
644 return false;
645 }
646 media_desc->set_cryptos(common_cryptos);
647 }
648 }
649 return true;
650}
651
652template <class C>
653static bool ContainsRtxCodec(const std::vector<C>& codecs) {
654 typename std::vector<C>::const_iterator it;
655 for (it = codecs.begin(); it != codecs.end(); ++it) {
656 if (IsRtxCodec(*it)) {
657 return true;
658 }
659 }
660 return false;
661}
662
663template <class C>
664static bool IsRtxCodec(const C& codec) {
665 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
666}
667
deadbeef0ed85b22016-02-23 17:24:52 -0800668static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
669 const std::string& content_name) {
670 auto it = options.transport_options.find(content_name);
671 if (it == options.transport_options.end()) {
672 return TransportOptions();
673 }
674 return it->second;
675}
676
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677// Create a media content to be offered in a session-initiate,
678// according to the given options.rtcp_mux, options.is_muc,
679// options.streams, codecs, secure_transport, crypto, and streams. If we don't
680// currently have crypto (in current_cryptos) and it is enabled (in
681// secure_policy), crypto is created (according to crypto_suites). If
682// add_legacy_stream is true, and current_streams is empty, a legacy
683// stream is created. The created content is added to the offer.
684template <class C>
685static bool CreateMediaContentOffer(
686 const MediaSessionOptions& options,
687 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000688 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000689 const CryptoParamsVec* current_cryptos,
690 const std::vector<std::string>& crypto_suites,
691 const RtpHeaderExtensions& rtp_extensions,
692 bool add_legacy_stream,
693 StreamParamsVec* current_streams,
694 MediaContentDescriptionImpl<C>* offer) {
695 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000696
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000697 if (secure_policy == SEC_REQUIRED) {
698 offer->set_crypto_required(CT_SDES);
699 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000700 offer->set_rtcp_mux(options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700701 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
702 offer->set_rtcp_reduced_size(true);
703 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704 offer->set_multistream(options.is_muc);
705 offer->set_rtp_header_extensions(rtp_extensions);
706
zhihuang8f65cdf2016-05-06 18:40:30 -0700707 if (!AddStreamParams(offer->type(), options, current_streams, offer,
708 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709 return false;
710 }
711
712#ifdef HAVE_SRTP
713 if (secure_policy != SEC_DISABLED) {
714 if (current_cryptos) {
715 AddMediaCryptos(*current_cryptos, offer);
716 }
717 if (offer->cryptos().empty()) {
718 if (!CreateMediaCryptos(crypto_suites, offer)) {
719 return false;
720 }
721 }
722 }
723#endif
724
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000725 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 return false;
727 }
728 return true;
729}
730
731template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000732static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
733 const std::string& codec1_id_str,
734 const std::vector<C>& codecs2,
735 const std::string& codec2_id_str) {
736 int codec1_id;
737 int codec2_id;
738 C codec1;
739 C codec2;
740 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
741 !rtc::FromString(codec2_id_str, &codec2_id) ||
742 !FindCodecById(codecs1, codec1_id, &codec1) ||
743 !FindCodecById(codecs2, codec2_id, &codec2)) {
744 return false;
745 }
746 return codec1.Matches(codec2);
747}
748
749template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750static void NegotiateCodecs(const std::vector<C>& local_codecs,
751 const std::vector<C>& offered_codecs,
752 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800753 for (const C& ours : local_codecs) {
754 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700755 // Note that we intentionally only find one matching codec for each of our
756 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800757 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
758 C negotiated = ours;
759 negotiated.IntersectFeedbackParams(theirs);
760 if (IsRtxCodec(negotiated)) {
761 std::string offered_apt_value;
762 theirs.GetParam(kCodecParamAssociatedPayloadType, &offered_apt_value);
763 // FindMatchingCodec shouldn't return something with no apt value.
764 RTC_DCHECK(!offered_apt_value.empty());
765 negotiated.SetParam(kCodecParamAssociatedPayloadType,
766 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000767 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800768 negotiated.id = theirs.id;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800769 negotiated_codecs->push_back(negotiated);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000770 }
771 }
deadbeef67cf2c12016-04-13 10:07:16 -0700772 // RFC3264: Although the answerer MAY list the formats in their desired
773 // order of preference, it is RECOMMENDED that unless there is a
774 // specific reason, the answerer list formats in the same relative order
775 // they were present in the offer.
776 std::unordered_map<int, int> payload_type_preferences;
777 int preference = static_cast<int>(offered_codecs.size() + 1);
778 for (const C& codec : offered_codecs) {
779 payload_type_preferences[codec.id] = preference--;
780 }
781 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
782 [&payload_type_preferences](const C& a, const C& b) {
783 return payload_type_preferences[a.id] >
784 payload_type_preferences[b.id];
785 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000786}
787
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800788// Finds a codec in |codecs2| that matches |codec_to_match|, which is
789// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
790// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000791template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800792static bool FindMatchingCodec(const std::vector<C>& codecs1,
793 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000794 const C& codec_to_match,
795 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800796 for (const C& potential_match : codecs2) {
797 if (potential_match.Matches(codec_to_match)) {
798 if (IsRtxCodec(codec_to_match)) {
799 std::string apt_value_1;
800 std::string apt_value_2;
801 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
802 &apt_value_1) ||
803 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
804 &apt_value_2)) {
805 LOG(LS_WARNING) << "RTX missing associated payload type.";
806 continue;
807 }
808 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
809 apt_value_2)) {
810 continue;
811 }
812 }
813 if (found_codec) {
814 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000815 }
816 return true;
817 }
818 }
819 return false;
820}
821
822// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
823// already exist in |offered_codecs| and ensure the payload types don't
824// collide.
825template <class C>
826static void FindCodecsToOffer(
827 const std::vector<C>& reference_codecs,
828 std::vector<C>* offered_codecs,
829 UsedPayloadTypes* used_pltypes) {
830
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000831 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800832 for (const C& reference_codec : reference_codecs) {
833 if (!IsRtxCodec(reference_codec) &&
834 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
835 reference_codec, nullptr)) {
836 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000837 used_pltypes->FindAndSetIdUsed(&codec);
838 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000839 }
840 }
841
842 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800843 for (const C& reference_codec : reference_codecs) {
844 if (IsRtxCodec(reference_codec) &&
845 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
846 reference_codec, nullptr)) {
847 C rtx_codec = reference_codec;
848
849 std::string associated_pt_str;
850 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
851 &associated_pt_str)) {
852 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
853 << " is missing an associated payload type.";
854 continue;
855 }
856
857 int associated_pt;
858 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
859 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
860 << " of RTX codec " << rtx_codec.name
861 << " to an integer.";
862 continue;
863 }
864
865 // Find the associated reference codec for the reference RTX codec.
866 C associated_codec;
867 if (!FindCodecById(reference_codecs, associated_pt, &associated_codec)) {
868 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
869 << associated_pt << " for RTX codec " << rtx_codec.name
870 << ".";
871 continue;
872 }
873
874 // Find a codec in the offered list that matches the reference codec.
875 // Its payload type may be different than the reference codec.
876 C matching_codec;
877 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
878 associated_codec, &matching_codec)) {
879 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec.name
880 << " codec.";
881 continue;
882 }
883
884 rtx_codec.params[kCodecParamAssociatedPayloadType] =
885 rtc::ToString(matching_codec.id);
886 used_pltypes->FindAndSetIdUsed(&rtx_codec);
887 offered_codecs->push_back(rtx_codec);
888 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 }
890}
891
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000892static bool FindByUri(const RtpHeaderExtensions& extensions,
isheriff6f8d6862016-05-26 11:24:55 -0700893 const webrtc::RtpExtension& ext_to_match,
894 webrtc::RtpExtension* found_extension) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000895 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
896 it != extensions.end(); ++it) {
897 // We assume that all URIs are given in a canonical format.
898 if (it->uri == ext_to_match.uri) {
899 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000900 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000901 }
902 return true;
903 }
904 }
905 return false;
906}
907
deadbeefa5b273a2015-08-20 17:30:13 -0700908// Iterates through |offered_extensions|, adding each one to |all_extensions|
909// and |used_ids|, and resolving ID conflicts. If an offered extension has the
910// same URI as one in |all_extensions|, it will re-use the same ID and won't be
911// treated as a conflict.
912static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
913 RtpHeaderExtensions* all_extensions,
914 UsedRtpHeaderExtensionIds* used_ids) {
915 for (auto& extension : *offered_extensions) {
isheriff6f8d6862016-05-26 11:24:55 -0700916 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700917 if (FindByUri(*all_extensions, extension, &existing)) {
918 extension.id = existing.id;
919 } else {
920 used_ids->FindAndSetIdUsed(&extension);
921 all_extensions->push_back(extension);
922 }
923 }
924}
925
926// Adds |reference_extensions| to |offered_extensions|, while updating
927// |all_extensions| and |used_ids|.
928static void FindRtpHdrExtsToOffer(
929 const RtpHeaderExtensions& reference_extensions,
930 RtpHeaderExtensions* offered_extensions,
931 RtpHeaderExtensions* all_extensions,
932 UsedRtpHeaderExtensionIds* used_ids) {
933 for (auto reference_extension : reference_extensions) {
934 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
isheriff6f8d6862016-05-26 11:24:55 -0700935 webrtc::RtpExtension existing;
deadbeefa5b273a2015-08-20 17:30:13 -0700936 if (FindByUri(*all_extensions, reference_extension, &existing)) {
937 offered_extensions->push_back(existing);
938 } else {
939 used_ids->FindAndSetIdUsed(&reference_extension);
940 all_extensions->push_back(reference_extension);
941 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000942 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000943 }
944 }
945}
946
947static void NegotiateRtpHeaderExtensions(
948 const RtpHeaderExtensions& local_extensions,
949 const RtpHeaderExtensions& offered_extensions,
950 RtpHeaderExtensions* negotiated_extenstions) {
951 RtpHeaderExtensions::const_iterator ours;
952 for (ours = local_extensions.begin();
953 ours != local_extensions.end(); ++ours) {
isheriff6f8d6862016-05-26 11:24:55 -0700954 webrtc::RtpExtension theirs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000955 if (FindByUri(offered_extensions, *ours, &theirs)) {
956 // We respond with their RTP header extension id.
957 negotiated_extenstions->push_back(theirs);
958 }
959 }
960}
961
962static void StripCNCodecs(AudioCodecs* audio_codecs) {
963 AudioCodecs::iterator iter = audio_codecs->begin();
964 while (iter != audio_codecs->end()) {
965 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
966 iter = audio_codecs->erase(iter);
967 } else {
968 ++iter;
969 }
970 }
971}
972
973// Create a media content to be answered in a session-accept,
974// according to the given options.rtcp_mux, options.streams, codecs,
975// crypto, and streams. If we don't currently have crypto (in
976// current_cryptos) and it is enabled (in secure_policy), crypto is
977// created (according to crypto_suites). If add_legacy_stream is
978// true, and current_streams is empty, a legacy stream is created.
979// The codecs, rtcp_mux, and crypto are all negotiated with the offer
980// from the incoming session-initiate. If the negotiation fails, this
981// method returns false. The created content is added to the offer.
982template <class C>
983static bool CreateMediaContentAnswer(
984 const MediaContentDescriptionImpl<C>* offer,
985 const MediaSessionOptions& options,
986 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000987 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000988 const CryptoParamsVec* current_cryptos,
989 const RtpHeaderExtensions& local_rtp_extenstions,
990 StreamParamsVec* current_streams,
991 bool add_legacy_stream,
992 bool bundle_enabled,
993 MediaContentDescriptionImpl<C>* answer) {
994 std::vector<C> negotiated_codecs;
995 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
996 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000997 answer->set_protocol(offer->protocol());
998 RtpHeaderExtensions negotiated_rtp_extensions;
999 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1000 offer->rtp_header_extensions(),
1001 &negotiated_rtp_extensions);
1002 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1003
1004 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001005 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1006 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1007 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001008
1009 if (sdes_policy != SEC_DISABLED) {
1010 CryptoParams crypto;
1011 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
1012 if (current_cryptos) {
1013 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1014 }
1015 answer->AddCrypto(crypto);
1016 }
1017 }
1018
1019 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001020 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001021 return false;
1022 }
1023
zhihuang8f65cdf2016-05-06 18:40:30 -07001024 if (!AddStreamParams(answer->type(), options, current_streams, answer,
1025 add_legacy_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001026 return false; // Something went seriously wrong.
1027 }
1028
1029 // Make sure the answer media content direction is per default set as
1030 // described in RFC3264 section 6.1.
1031 switch (offer->direction()) {
1032 case MD_INACTIVE:
1033 answer->set_direction(MD_INACTIVE);
1034 break;
1035 case MD_SENDONLY:
1036 answer->set_direction(MD_RECVONLY);
1037 break;
1038 case MD_RECVONLY:
deadbeefb5cb19b2015-11-23 16:39:12 -08001039 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1040 answer->streams().empty()
1041 ? MD_INACTIVE
1042 : MD_SENDONLY);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001043 break;
1044 case MD_SENDRECV:
deadbeefb5cb19b2015-11-23 16:39:12 -08001045 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1046 answer->streams().empty()
1047 ? MD_RECVONLY
1048 : MD_SENDRECV);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001049 break;
1050 default:
deadbeefc80741f2015-10-22 13:14:45 -07001051 RTC_DCHECK(false && "MediaContentDescription has unexpected direction.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001052 break;
1053 }
1054
1055 return true;
1056}
1057
zhihuangcf5b37c2016-05-05 11:44:35 -07001058static bool IsDtlsRtp(const std::string& protocol) {
1059 // Most-likely values first.
1060 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1061 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1062}
1063
1064static bool IsPlainRtp(const std::string& protocol) {
1065 // Most-likely values first.
1066 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1067 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1068}
1069
1070static bool IsDtlsSctp(const std::string& protocol) {
1071 return protocol == "DTLS/SCTP";
1072}
1073
1074static bool IsPlainSctp(const std::string& protocol) {
1075 return protocol == "SCTP";
1076}
1077
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001078static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001079 const std::string& protocol,
1080 bool secure_transport) {
zhihuangcf5b37c2016-05-05 11:44:35 -07001081 // Since not all applications serialize and deserialize the media protocol,
1082 // we will have to accept |protocol| to be empty.
1083 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001084 return true;
1085 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001086
zhihuangcf5b37c2016-05-05 11:44:35 -07001087 if (type == MEDIA_TYPE_DATA) {
1088 // Check for SCTP, but also for RTP for RTP-based data channels.
1089 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1090 if (secure_transport) {
1091 // Most likely scenarios first.
1092 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1093 IsPlainRtp(protocol);
1094 } else {
1095 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1096 }
1097 }
1098
1099 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1100 // JSEP specifies.
1101 if (secure_transport) {
1102 // Most likely scenarios first.
1103 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1104 } else {
1105 return IsPlainRtp(protocol);
1106 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001107}
1108
1109static void SetMediaProtocol(bool secure_transport,
1110 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001111 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001112 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001113 else if (secure_transport)
1114 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001115 else
1116 desc->set_protocol(kMediaProtocolAvpf);
1117}
1118
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001119// Gets the TransportInfo of the given |content_name| from the
1120// |current_description|. If doesn't exist, returns a new one.
1121static const TransportDescription* GetTransportDescription(
1122 const std::string& content_name,
1123 const SessionDescription* current_description) {
1124 const TransportDescription* desc = NULL;
1125 if (current_description) {
1126 const TransportInfo* info =
1127 current_description->GetTransportInfoByName(content_name);
1128 if (info) {
1129 desc = &info->description;
1130 }
1131 }
1132 return desc;
1133}
1134
1135// Gets the current DTLS state from the transport description.
1136static bool IsDtlsActive(
1137 const std::string& content_name,
1138 const SessionDescription* current_description) {
1139 if (!current_description)
1140 return false;
1141
1142 const ContentInfo* content =
1143 current_description->GetContentByName(content_name);
1144 if (!content)
1145 return false;
1146
1147 const TransportDescription* current_tdesc =
1148 GetTransportDescription(content_name, current_description);
1149 if (!current_tdesc)
1150 return false;
1151
1152 return current_tdesc->secure();
1153}
1154
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001155std::string MediaTypeToString(MediaType type) {
1156 std::string type_str;
1157 switch (type) {
1158 case MEDIA_TYPE_AUDIO:
1159 type_str = "audio";
1160 break;
1161 case MEDIA_TYPE_VIDEO:
1162 type_str = "video";
1163 break;
1164 case MEDIA_TYPE_DATA:
1165 type_str = "data";
1166 break;
1167 default:
1168 ASSERT(false);
1169 break;
1170 }
1171 return type_str;
1172}
1173
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001174void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001175 const std::string& id,
1176 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001177 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001178}
1179
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001180void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001181 const std::string& id,
1182 const std::string& sync_label,
1183 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001184 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001185}
1186
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001187void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001188 MediaType type,
1189 const std::string& id,
1190 const std::string& sync_label,
1191 int num_sim_layers) {
1192 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001193
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001194 // If we haven't already set the data_channel_type, and we add a
1195 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001196 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001197 data_channel_type = DCT_RTP;
1198}
1199
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001200void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201 const std::string& id) {
1202 Streams::iterator stream_it = streams.begin();
1203 for (; stream_it != streams.end(); ++stream_it) {
1204 if (stream_it->type == type && stream_it->id == id) {
1205 streams.erase(stream_it);
1206 return;
1207 }
1208 }
1209 ASSERT(false);
1210}
1211
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001212bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1213 Streams::const_iterator stream_it = streams.begin();
1214 for (; stream_it != streams.end(); ++stream_it) {
1215 if (stream_it->type == type) {
1216 return true;
1217 }
1218 }
1219 return false;
1220}
1221
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001222MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1223 const TransportDescriptionFactory* transport_desc_factory)
1224 : secure_(SEC_DISABLED),
1225 add_legacy_(true),
1226 transport_desc_factory_(transport_desc_factory) {
1227}
1228
1229MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1230 ChannelManager* channel_manager,
1231 const TransportDescriptionFactory* transport_desc_factory)
1232 : secure_(SEC_DISABLED),
1233 add_legacy_(true),
1234 transport_desc_factory_(transport_desc_factory) {
1235 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1236 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1237 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1238 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1239 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1240}
1241
1242SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1243 const MediaSessionOptions& options,
1244 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001245 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001246
1247 StreamParamsVec current_streams;
1248 GetCurrentStreamParams(current_description, &current_streams);
1249
1250 AudioCodecs audio_codecs;
1251 VideoCodecs video_codecs;
1252 DataCodecs data_codecs;
1253 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1254 &data_codecs);
1255
1256 if (!options.vad_enabled) {
1257 // If application doesn't want CN codecs in offer.
1258 StripCNCodecs(&audio_codecs);
1259 }
1260
1261 RtpHeaderExtensions audio_rtp_extensions;
1262 RtpHeaderExtensions video_rtp_extensions;
1263 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1264 &video_rtp_extensions);
1265
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001266 bool audio_added = false;
1267 bool video_added = false;
1268 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001269
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001270 // Iterate through the contents of |current_description| to maintain the order
1271 // of the m-lines in the new offer.
1272 if (current_description) {
1273 ContentInfos::const_iterator it = current_description->contents().begin();
1274 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001275 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001276 if (!AddAudioContentForOffer(options, current_description,
1277 audio_rtp_extensions, audio_codecs,
1278 &current_streams, offer.get())) {
1279 return NULL;
1280 }
1281 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001282 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001283 if (!AddVideoContentForOffer(options, current_description,
1284 video_rtp_extensions, video_codecs,
1285 &current_streams, offer.get())) {
1286 return NULL;
1287 }
1288 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001289 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001290 MediaSessionOptions options_copy(options);
1291 if (IsSctp(static_cast<const MediaContentDescription*>(
1292 it->description))) {
1293 options_copy.data_channel_type = DCT_SCTP;
1294 }
1295 if (!AddDataContentForOffer(options_copy, current_description,
1296 &data_codecs, &current_streams,
1297 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001298 return NULL;
1299 }
1300 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001301 } else {
1302 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001303 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001304 }
1305 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001306
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001307 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001308 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001309 !AddAudioContentForOffer(options, current_description,
1310 audio_rtp_extensions, audio_codecs,
1311 &current_streams, offer.get())) {
1312 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001313 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001314 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001315 !AddVideoContentForOffer(options, current_description,
1316 video_rtp_extensions, video_codecs,
1317 &current_streams, offer.get())) {
1318 return NULL;
1319 }
1320 if (!data_added && options.has_data() &&
1321 !AddDataContentForOffer(options, current_description, &data_codecs,
1322 &current_streams, offer.get())) {
1323 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001324 }
1325
1326 // Bundle the contents together, if we've been asked to do so, and update any
1327 // parameters that need to be tweaked for BUNDLE.
1328 if (options.bundle_enabled) {
1329 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1330 for (ContentInfos::const_iterator content = offer->contents().begin();
1331 content != offer->contents().end(); ++content) {
1332 offer_bundle.AddContentName(content->name);
1333 }
1334 offer->AddGroup(offer_bundle);
1335 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1336 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1337 return NULL;
1338 }
1339 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1340 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1341 return NULL;
1342 }
1343 }
1344
1345 return offer.release();
1346}
1347
1348SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1349 const SessionDescription* offer, const MediaSessionOptions& options,
1350 const SessionDescription* current_description) const {
1351 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001352 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1353 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001354 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001355
1356 StreamParamsVec current_streams;
1357 GetCurrentStreamParams(current_description, &current_streams);
1358
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001359 if (offer) {
1360 ContentInfos::const_iterator it = offer->contents().begin();
1361 for (; it != offer->contents().end(); ++it) {
1362 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1363 if (!AddAudioContentForAnswer(offer, options, current_description,
1364 &current_streams, answer.get())) {
1365 return NULL;
1366 }
1367 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1368 if (!AddVideoContentForAnswer(offer, options, current_description,
1369 &current_streams, answer.get())) {
1370 return NULL;
1371 }
1372 } else {
1373 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1374 if (!AddDataContentForAnswer(offer, options, current_description,
1375 &current_streams, answer.get())) {
1376 return NULL;
1377 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001378 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001379 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 }
1381
1382 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1383 // group in the answer with the appropriate content names.
1384 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1385 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1386 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1387 for (ContentInfos::const_iterator content = answer->contents().begin();
1388 content != answer->contents().end(); ++content) {
1389 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1390 answer_bundle.AddContentName(content->name);
1391 }
1392 }
1393 if (answer_bundle.FirstContentName()) {
1394 answer->AddGroup(answer_bundle);
1395
1396 // Share the same ICE credentials and crypto params across all contents,
1397 // as BUNDLE requires.
1398 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1399 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1400 return NULL;
1401 }
1402
1403 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1404 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1405 return NULL;
1406 }
1407 }
1408 }
1409
1410 return answer.release();
1411}
1412
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001413void MediaSessionDescriptionFactory::GetCodecsToOffer(
1414 const SessionDescription* current_description,
1415 AudioCodecs* audio_codecs,
1416 VideoCodecs* video_codecs,
1417 DataCodecs* data_codecs) const {
1418 UsedPayloadTypes used_pltypes;
1419 audio_codecs->clear();
1420 video_codecs->clear();
1421 data_codecs->clear();
1422
1423
1424 // First - get all codecs from the current description if the media type
1425 // is used.
1426 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1427 // type is added.
1428 if (current_description) {
1429 const AudioContentDescription* audio =
1430 GetFirstAudioContentDescription(current_description);
1431 if (audio) {
1432 *audio_codecs = audio->codecs();
1433 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1434 }
1435 const VideoContentDescription* video =
1436 GetFirstVideoContentDescription(current_description);
1437 if (video) {
1438 *video_codecs = video->codecs();
1439 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1440 }
1441 const DataContentDescription* data =
1442 GetFirstDataContentDescription(current_description);
1443 if (data) {
1444 *data_codecs = data->codecs();
1445 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1446 }
1447 }
1448
1449 // Add our codecs that are not in |current_description|.
1450 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1451 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1452 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1453}
1454
1455void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1456 const SessionDescription* current_description,
1457 RtpHeaderExtensions* audio_extensions,
1458 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001459 // All header extensions allocated from the same range to avoid potential
1460 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001461 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001462 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001463 audio_extensions->clear();
1464 video_extensions->clear();
1465
1466 // First - get all extensions from the current description if the media type
1467 // is used.
1468 // Add them to |used_ids| so the local ids are not reused if a new media
1469 // type is added.
1470 if (current_description) {
1471 const AudioContentDescription* audio =
1472 GetFirstAudioContentDescription(current_description);
1473 if (audio) {
1474 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001475 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001476 }
1477 const VideoContentDescription* video =
1478 GetFirstVideoContentDescription(current_description);
1479 if (video) {
1480 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001481 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001482 }
1483 }
1484
1485 // Add our default RTP header extensions that are not in
1486 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001487 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1488 &all_extensions, &used_ids);
1489 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1490 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001491}
1492
1493bool MediaSessionDescriptionFactory::AddTransportOffer(
1494 const std::string& content_name,
1495 const TransportOptions& transport_options,
1496 const SessionDescription* current_desc,
1497 SessionDescription* offer_desc) const {
1498 if (!transport_desc_factory_)
1499 return false;
1500 const TransportDescription* current_tdesc =
1501 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001502 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001503 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1504 bool ret = (new_tdesc.get() != NULL &&
1505 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1506 if (!ret) {
1507 LOG(LS_ERROR)
1508 << "Failed to AddTransportOffer, content name=" << content_name;
1509 }
1510 return ret;
1511}
1512
1513TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1514 const std::string& content_name,
1515 const SessionDescription* offer_desc,
1516 const TransportOptions& transport_options,
1517 const SessionDescription* current_desc) const {
1518 if (!transport_desc_factory_)
1519 return NULL;
1520 const TransportDescription* offer_tdesc =
1521 GetTransportDescription(content_name, offer_desc);
1522 const TransportDescription* current_tdesc =
1523 GetTransportDescription(content_name, current_desc);
1524 return
1525 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1526 current_tdesc);
1527}
1528
1529bool MediaSessionDescriptionFactory::AddTransportAnswer(
1530 const std::string& content_name,
1531 const TransportDescription& transport_desc,
1532 SessionDescription* answer_desc) const {
1533 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1534 transport_desc))) {
1535 LOG(LS_ERROR)
1536 << "Failed to AddTransportAnswer, content name=" << content_name;
1537 return false;
1538 }
1539 return true;
1540}
1541
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001542bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1543 const MediaSessionOptions& options,
1544 const SessionDescription* current_description,
1545 const RtpHeaderExtensions& audio_rtp_extensions,
1546 const AudioCodecs& audio_codecs,
1547 StreamParamsVec* current_streams,
1548 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001549 const ContentInfo* current_audio_content =
1550 GetFirstAudioContent(current_description);
1551 std::string content_name =
1552 current_audio_content ? current_audio_content->name : CN_AUDIO;
1553
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001554 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001555 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1556 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001557
kwiberg31022942016-03-11 14:18:21 -08001558 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001559 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001560 GetSupportedAudioCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001561 if (!CreateMediaContentOffer(
1562 options,
1563 audio_codecs,
1564 sdes_policy,
1565 GetCryptos(GetFirstAudioContentDescription(current_description)),
1566 crypto_suites,
1567 audio_rtp_extensions,
1568 add_legacy_,
1569 current_streams,
1570 audio.get())) {
1571 return false;
1572 }
1573 audio->set_lang(lang_);
1574
1575 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1576 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001577
deadbeefc80741f2015-10-22 13:14:45 -07001578 if (!audio->streams().empty()) {
1579 if (options.recv_audio) {
1580 audio->set_direction(MD_SENDRECV);
1581 } else {
1582 audio->set_direction(MD_SENDONLY);
1583 }
1584 } else {
1585 if (options.recv_audio) {
1586 audio->set_direction(MD_RECVONLY);
1587 } else {
1588 audio->set_direction(MD_INACTIVE);
1589 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001590 }
1591
deadbeef44f08192015-12-15 16:20:09 -08001592 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001593 if (!AddTransportOffer(content_name,
1594 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001595 current_description, desc)) {
1596 return false;
1597 }
1598
1599 return true;
1600}
1601
1602bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1603 const MediaSessionOptions& options,
1604 const SessionDescription* current_description,
1605 const RtpHeaderExtensions& video_rtp_extensions,
1606 const VideoCodecs& video_codecs,
1607 StreamParamsVec* current_streams,
1608 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001609 const ContentInfo* current_video_content =
1610 GetFirstVideoContent(current_description);
1611 std::string content_name =
1612 current_video_content ? current_video_content->name : CN_VIDEO;
1613
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001614 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001615 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1616 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001617
kwiberg31022942016-03-11 14:18:21 -08001618 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001619 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001620 GetSupportedVideoCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001621 if (!CreateMediaContentOffer(
1622 options,
1623 video_codecs,
1624 sdes_policy,
1625 GetCryptos(GetFirstVideoContentDescription(current_description)),
1626 crypto_suites,
1627 video_rtp_extensions,
1628 add_legacy_,
1629 current_streams,
1630 video.get())) {
1631 return false;
1632 }
1633
1634 video->set_bandwidth(options.video_bandwidth);
1635
1636 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1637 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001638
deadbeefc80741f2015-10-22 13:14:45 -07001639 if (!video->streams().empty()) {
1640 if (options.recv_video) {
1641 video->set_direction(MD_SENDRECV);
1642 } else {
1643 video->set_direction(MD_SENDONLY);
1644 }
1645 } else {
1646 if (options.recv_video) {
1647 video->set_direction(MD_RECVONLY);
1648 } else {
1649 video->set_direction(MD_INACTIVE);
1650 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001651 }
1652
deadbeef44f08192015-12-15 16:20:09 -08001653 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001654 if (!AddTransportOffer(content_name,
1655 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001656 current_description, desc)) {
1657 return false;
1658 }
1659
1660 return true;
1661}
1662
1663bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1664 const MediaSessionOptions& options,
1665 const SessionDescription* current_description,
1666 DataCodecs* data_codecs,
1667 StreamParamsVec* current_streams,
1668 SessionDescription* desc) const {
1669 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1670
kwiberg31022942016-03-11 14:18:21 -08001671 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001672 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1673
1674 FilterDataCodecs(data_codecs, is_sctp);
1675
deadbeef44f08192015-12-15 16:20:09 -08001676 const ContentInfo* current_data_content =
1677 GetFirstDataContent(current_description);
1678 std::string content_name =
1679 current_data_content ? current_data_content->name : CN_DATA;
1680
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001681 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001682 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1683 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001684 std::vector<std::string> crypto_suites;
1685 if (is_sctp) {
1686 // SDES doesn't make sense for SCTP, so we disable it, and we only
1687 // get SDES crypto suites for RTP-based data channels.
1688 sdes_policy = cricket::SEC_DISABLED;
1689 // Unlike SetMediaProtocol below, we need to set the protocol
1690 // before we call CreateMediaContentOffer. Otherwise,
1691 // CreateMediaContentOffer won't know this is SCTP and will
1692 // generate SSRCs rather than SIDs.
1693 data->set_protocol(
1694 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1695 } else {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001696 GetSupportedDataCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001697 }
1698
1699 if (!CreateMediaContentOffer(
1700 options,
1701 *data_codecs,
1702 sdes_policy,
1703 GetCryptos(GetFirstDataContentDescription(current_description)),
1704 crypto_suites,
1705 RtpHeaderExtensions(),
1706 add_legacy_,
1707 current_streams,
1708 data.get())) {
1709 return false;
1710 }
1711
1712 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001713 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001714 } else {
1715 data->set_bandwidth(options.data_bandwidth);
1716 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001717 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001718 }
deadbeef0ed85b22016-02-23 17:24:52 -08001719 if (!AddTransportOffer(content_name,
1720 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001721 current_description, desc)) {
1722 return false;
1723 }
1724 return true;
1725}
1726
1727bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1728 const SessionDescription* offer,
1729 const MediaSessionOptions& options,
1730 const SessionDescription* current_description,
1731 StreamParamsVec* current_streams,
1732 SessionDescription* answer) const {
1733 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1734
kwiberg31022942016-03-11 14:18:21 -08001735 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001736 audio_content->name, offer,
1737 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001738 if (!audio_transport) {
1739 return false;
1740 }
1741
1742 AudioCodecs audio_codecs = audio_codecs_;
1743 if (!options.vad_enabled) {
1744 StripCNCodecs(&audio_codecs);
1745 }
1746
1747 bool bundle_enabled =
1748 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08001749 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001750 new AudioContentDescription());
1751 // Do not require or create SDES cryptos if DTLS is used.
1752 cricket::SecurePolicy sdes_policy =
1753 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1754 if (!CreateMediaContentAnswer(
1755 static_cast<const AudioContentDescription*>(
1756 audio_content->description),
1757 options,
1758 audio_codecs,
1759 sdes_policy,
1760 GetCryptos(GetFirstAudioContentDescription(current_description)),
1761 audio_rtp_extensions_,
1762 current_streams,
1763 add_legacy_,
1764 bundle_enabled,
1765 audio_answer.get())) {
1766 return false; // Fails the session setup.
1767 }
1768
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001769 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001770 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1771 audio_answer->protocol(),
1772 audio_transport->secure());
1773 if (!rejected) {
1774 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1775 } else {
1776 // RFC 3264
1777 // The answer MUST contain the same number of m-lines as the offer.
1778 LOG(LS_INFO) << "Audio is not supported in the answer.";
1779 }
1780
1781 answer->AddContent(audio_content->name, audio_content->type, rejected,
1782 audio_answer.release());
1783 return true;
1784}
1785
1786bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1787 const SessionDescription* offer,
1788 const MediaSessionOptions& options,
1789 const SessionDescription* current_description,
1790 StreamParamsVec* current_streams,
1791 SessionDescription* answer) const {
1792 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001793 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001794 video_content->name, offer,
1795 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001796 if (!video_transport) {
1797 return false;
1798 }
1799
kwiberg31022942016-03-11 14:18:21 -08001800 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001801 new VideoContentDescription());
1802 // Do not require or create SDES cryptos if DTLS is used.
1803 cricket::SecurePolicy sdes_policy =
1804 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1805 bool bundle_enabled =
1806 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1807 if (!CreateMediaContentAnswer(
1808 static_cast<const VideoContentDescription*>(
1809 video_content->description),
1810 options,
1811 video_codecs_,
1812 sdes_policy,
1813 GetCryptos(GetFirstVideoContentDescription(current_description)),
1814 video_rtp_extensions_,
1815 current_streams,
1816 add_legacy_,
1817 bundle_enabled,
1818 video_answer.get())) {
1819 return false;
1820 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001821 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001822 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1823 video_answer->protocol(),
1824 video_transport->secure());
1825 if (!rejected) {
1826 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1827 answer)) {
1828 return false;
1829 }
1830 video_answer->set_bandwidth(options.video_bandwidth);
1831 } else {
1832 // RFC 3264
1833 // The answer MUST contain the same number of m-lines as the offer.
1834 LOG(LS_INFO) << "Video is not supported in the answer.";
1835 }
1836 answer->AddContent(video_content->name, video_content->type, rejected,
1837 video_answer.release());
1838 return true;
1839}
1840
1841bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1842 const SessionDescription* offer,
1843 const MediaSessionOptions& options,
1844 const SessionDescription* current_description,
1845 StreamParamsVec* current_streams,
1846 SessionDescription* answer) const {
1847 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001848 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001849 data_content->name, offer,
1850 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001851 if (!data_transport) {
1852 return false;
1853 }
1854 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1855 std::vector<DataCodec> data_codecs(data_codecs_);
1856 FilterDataCodecs(&data_codecs, is_sctp);
1857
kwiberg31022942016-03-11 14:18:21 -08001858 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001859 new DataContentDescription());
1860 // Do not require or create SDES cryptos if DTLS is used.
1861 cricket::SecurePolicy sdes_policy =
1862 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1863 bool bundle_enabled =
1864 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1865 if (!CreateMediaContentAnswer(
1866 static_cast<const DataContentDescription*>(
1867 data_content->description),
1868 options,
1869 data_codecs_,
1870 sdes_policy,
1871 GetCryptos(GetFirstDataContentDescription(current_description)),
1872 RtpHeaderExtensions(),
1873 current_streams,
1874 add_legacy_,
1875 bundle_enabled,
1876 data_answer.get())) {
1877 return false; // Fails the session setup.
1878 }
1879
1880 bool rejected = !options.has_data() || data_content->rejected ||
1881 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1882 data_answer->protocol(),
1883 data_transport->secure());
1884 if (!rejected) {
1885 data_answer->set_bandwidth(options.data_bandwidth);
1886 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1887 answer)) {
1888 return false;
1889 }
1890 } else {
1891 // RFC 3264
1892 // The answer MUST contain the same number of m-lines as the offer.
1893 LOG(LS_INFO) << "Data is not supported in the answer.";
1894 }
1895 answer->AddContent(data_content->name, data_content->type, rejected,
1896 data_answer.release());
1897 return true;
1898}
1899
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001900bool IsMediaContent(const ContentInfo* content) {
1901 return (content &&
1902 (content->type == NS_JINGLE_RTP ||
1903 content->type == NS_JINGLE_DRAFT_SCTP));
1904}
1905
1906bool IsAudioContent(const ContentInfo* content) {
1907 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1908}
1909
1910bool IsVideoContent(const ContentInfo* content) {
1911 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1912}
1913
1914bool IsDataContent(const ContentInfo* content) {
1915 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1916}
1917
deadbeef0ed85b22016-02-23 17:24:52 -08001918const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1919 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07001920 for (const ContentInfo& content : contents) {
1921 if (IsMediaContentOfType(&content, media_type)) {
1922 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001923 }
1924 }
deadbeef0ed85b22016-02-23 17:24:52 -08001925 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001926}
1927
1928const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1929 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1930}
1931
1932const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1933 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1934}
1935
1936const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1937 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1938}
1939
1940static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1941 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08001942 if (sdesc == nullptr) {
1943 return nullptr;
1944 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001945
1946 return GetFirstMediaContent(sdesc->contents(), media_type);
1947}
1948
1949const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1950 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1951}
1952
1953const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1954 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1955}
1956
1957const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1958 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1959}
1960
1961const MediaContentDescription* GetFirstMediaContentDescription(
1962 const SessionDescription* sdesc, MediaType media_type) {
1963 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1964 const ContentDescription* description = content ? content->description : NULL;
1965 return static_cast<const MediaContentDescription*>(description);
1966}
1967
1968const AudioContentDescription* GetFirstAudioContentDescription(
1969 const SessionDescription* sdesc) {
1970 return static_cast<const AudioContentDescription*>(
1971 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1972}
1973
1974const VideoContentDescription* GetFirstVideoContentDescription(
1975 const SessionDescription* sdesc) {
1976 return static_cast<const VideoContentDescription*>(
1977 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1978}
1979
1980const DataContentDescription* GetFirstDataContentDescription(
1981 const SessionDescription* sdesc) {
1982 return static_cast<const DataContentDescription*>(
1983 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1984}
1985
Taylor Brandstetterdc4eb8c2016-05-12 08:14:50 -07001986//
1987// Non-const versions of the above functions.
1988//
1989
1990ContentInfo* GetFirstMediaContent(ContentInfos& contents,
1991 MediaType media_type) {
1992 for (ContentInfo& content : contents) {
1993 if (IsMediaContentOfType(&content, media_type)) {
1994 return &content;
1995 }
1996 }
1997 return nullptr;
1998}
1999
2000ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2001 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2002}
2003
2004ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2005 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2006}
2007
2008ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2009 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2010}
2011
2012static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2013 MediaType media_type) {
2014 if (sdesc == nullptr) {
2015 return nullptr;
2016 }
2017
2018 return GetFirstMediaContent(sdesc->contents(), media_type);
2019}
2020
2021ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2022 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2023}
2024
2025ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2026 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2027}
2028
2029ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2030 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2031}
2032
2033MediaContentDescription* GetFirstMediaContentDescription(
2034 SessionDescription* sdesc,
2035 MediaType media_type) {
2036 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2037 ContentDescription* description = content ? content->description : NULL;
2038 return static_cast<MediaContentDescription*>(description);
2039}
2040
2041AudioContentDescription* GetFirstAudioContentDescription(
2042 SessionDescription* sdesc) {
2043 return static_cast<AudioContentDescription*>(
2044 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2045}
2046
2047VideoContentDescription* GetFirstVideoContentDescription(
2048 SessionDescription* sdesc) {
2049 return static_cast<VideoContentDescription*>(
2050 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2051}
2052
2053DataContentDescription* GetFirstDataContentDescription(
2054 SessionDescription* sdesc) {
2055 return static_cast<DataContentDescription*>(
2056 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2057}
2058
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002059} // namespace cricket