blob: b6f52c2b62155e1a8917e7073eabe1df1e810d9b [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2004 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/session/media/mediasession.h"
29
30#include <functional>
31#include <map>
32#include <set>
33#include <utility>
34
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035#include "talk/session/media/channelmanager.h"
36#include "talk/session/media/srtpfilter.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000037#include "webrtc/base/helpers.h"
38#include "webrtc/base/logging.h"
39#include "webrtc/base/scoped_ptr.h"
40#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080041#include "webrtc/media/base/constants.h"
42#include "webrtc/media/base/cryptoparams.h"
pthatcher@webrtc.org5ad41782014-12-23 22:14:15 +000043#include "webrtc/p2p/base/constants.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000045#ifdef HAVE_SCTP
kjellandera96e2d72016-02-04 23:52:28 -080046#include "webrtc/media/sctp/sctpdataengine.h"
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000047#else
Peter Boström0c4e06b2015-10-07 12:23:21 +020048static const uint32_t kMaxSctpSid = 1023;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000049#endif
50
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051namespace {
52const char kInline[] = "inline:";
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -080053
54void GetSupportedCryptoSuiteNames(void (*func)(std::vector<int>*),
55 std::vector<std::string>* names) {
56#ifdef HAVE_SRTP
57 std::vector<int> crypto_suites;
58 func(&crypto_suites);
59 for (const auto crypto : crypto_suites) {
60 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
61 }
62#endif
63}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000064}
65
66namespace cricket {
67
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000068using rtc::scoped_ptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000069
70// RTP Profile names
71// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
72// RFC4585
73const char kMediaProtocolAvpf[] = "RTP/AVPF";
74// RFC5124
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +000075const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
76
deadbeeff3938292015-07-15 12:20:53 -070077// We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
78// but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079const char kMediaProtocolSavpf[] = "RTP/SAVPF";
80
81const char kMediaProtocolRtpPrefix[] = "RTP/";
82
83const char kMediaProtocolSctp[] = "SCTP";
84const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
lally@webrtc.orgec97c652015-02-24 20:18:48 +000085const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
lally@webrtc.orga7470932015-02-24 20:19:21 +000086const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000087
88static bool IsMediaContentOfType(const ContentInfo* content,
89 MediaType media_type) {
90 if (!IsMediaContent(content)) {
91 return false;
92 }
93
94 const MediaContentDescription* mdesc =
95 static_cast<const MediaContentDescription*>(content->description);
96 return mdesc && mdesc->type() == media_type;
97}
98
99static bool CreateCryptoParams(int tag, const std::string& cipher,
100 CryptoParams *out) {
101 std::string key;
102 key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
103
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000104 if (!rtc::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000105 return false;
106 }
107 out->tag = tag;
108 out->cipher_suite = cipher;
109 out->key_params = kInline;
110 out->key_params += key;
111 return true;
112}
113
114#ifdef HAVE_SRTP
115static bool AddCryptoParams(const std::string& cipher_suite,
116 CryptoParamsVec *out) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000117 int size = static_cast<int>(out->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118
119 out->resize(size + 1);
120 return CreateCryptoParams(size, cipher_suite, &out->at(size));
121}
122
123void AddMediaCryptos(const CryptoParamsVec& cryptos,
124 MediaContentDescription* media) {
125 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
126 crypto != cryptos.end(); ++crypto) {
127 media->AddCrypto(*crypto);
128 }
129}
130
131bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
132 MediaContentDescription* media) {
133 CryptoParamsVec cryptos;
134 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
135 it != crypto_suites.end(); ++it) {
136 if (!AddCryptoParams(*it, &cryptos)) {
137 return false;
138 }
139 }
140 AddMediaCryptos(cryptos, media);
141 return true;
142}
143#endif
144
145const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
146 if (!media) {
147 return NULL;
148 }
149 return &media->cryptos();
150}
151
152bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
153 const CryptoParams& crypto,
154 CryptoParams* out) {
155 for (CryptoParamsVec::const_iterator it = cryptos.begin();
156 it != cryptos.end(); ++it) {
157 if (crypto.Matches(*it)) {
158 *out = *it;
159 return true;
160 }
161 }
162 return false;
163}
164
165// For audio, HMAC 32 is prefered because of the low overhead.
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800166void GetSupportedAudioCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000167#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800168 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
169 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170#endif
171}
172
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800173void GetSupportedAudioCryptoSuiteNames(
174 std::vector<std::string>* crypto_suite_names) {
175 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
176 crypto_suite_names);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000177}
178
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800179void GetSupportedVideoCryptoSuites(std::vector<int>* crypto_suites) {
180 GetDefaultSrtpCryptoSuites(crypto_suites);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181}
182
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800183void GetSupportedVideoCryptoSuiteNames(
184 std::vector<std::string>* crypto_suite_names) {
185 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
186 crypto_suite_names);
187}
188
189void GetSupportedDataCryptoSuites(std::vector<int>* crypto_suites) {
190 GetDefaultSrtpCryptoSuites(crypto_suites);
191}
192
193void GetSupportedDataCryptoSuiteNames(
194 std::vector<std::string>* crypto_suite_names) {
195 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
196 crypto_suite_names);
197}
198
199void GetDefaultSrtpCryptoSuites(std::vector<int>* crypto_suites) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000200#ifdef HAVE_SRTP
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800201 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202#endif
203}
204
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -0800205void GetDefaultSrtpCryptoSuiteNames(
206 std::vector<std::string>* crypto_suite_names) {
207 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites, crypto_suite_names);
208}
209
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000210// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
211// tolerated unless bundle is enabled because it is low overhead. Pick the
212// crypto in the list that is supported.
213static bool SelectCrypto(const MediaContentDescription* offer,
214 bool bundle,
215 CryptoParams *crypto) {
216 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
217 const CryptoParamsVec& cryptos = offer->cryptos();
218
219 for (CryptoParamsVec::const_iterator i = cryptos.begin();
220 i != cryptos.end(); ++i) {
Guo-wei Shieh456696a2015-09-30 21:48:54 -0700221 if (rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
222 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
223 !bundle)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
225 }
226 }
227 return false;
228}
229
230static const StreamParams* FindFirstStreamParamsByCname(
231 const StreamParamsVec& params_vec,
232 const std::string& cname) {
233 for (StreamParamsVec::const_iterator it = params_vec.begin();
234 it != params_vec.end(); ++it) {
235 if (cname == it->cname)
236 return &*it;
237 }
238 return NULL;
239}
240
241// Generates a new CNAME or the CNAME of an already existing StreamParams
242// if a StreamParams exist for another Stream in streams with sync_label
243// sync_label.
244static bool GenerateCname(const StreamParamsVec& params_vec,
245 const MediaSessionOptions::Streams& streams,
246 const std::string& synch_label,
247 std::string* cname) {
248 ASSERT(cname != NULL);
249 if (!cname)
250 return false;
251
252 // Check if a CNAME exist for any of the other synched streams.
253 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
254 stream_it != streams.end() ; ++stream_it) {
255 if (synch_label != stream_it->sync_label)
256 continue;
257
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 // groupid is empty for StreamParams generated using
259 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000260 const StreamParams* param = GetStreamByIds(params_vec, "", stream_it->id);
261 if (param) {
262 *cname = param->cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 return true;
264 }
265 }
266 // No other stream seems to exist that we should sync with.
267 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
268 // This string is only used for synchronization, and therefore is opaque.
269 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000270 if (!rtc::CreateRandomString(16, cname)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000271 ASSERT(false);
272 return false;
273 }
274 } while (FindFirstStreamParamsByCname(params_vec, *cname));
275
276 return true;
277}
278
279// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000280// The generated values are added to |ssrcs|.
281// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000283 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200284 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000285 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200286 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000288 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000289 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
291 ssrcs->push_back(candidate);
292 }
293}
294
295// Returns false if we exhaust the range of SIDs.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200296static bool GenerateSctpSid(const StreamParamsVec& params_vec, uint32_t* sid) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 if (params_vec.size() > kMaxSctpSid) {
298 LOG(LS_WARNING) <<
299 "Could not generate an SCTP SID: too many SCTP streams.";
300 return false;
301 }
302 while (true) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200303 uint32_t candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000304 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000305 *sid = candidate;
306 return true;
307 }
308 }
309}
310
311static bool GenerateSctpSids(const StreamParamsVec& params_vec,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200312 std::vector<uint32_t>* sids) {
313 uint32_t sid;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 if (!GenerateSctpSid(params_vec, &sid)) {
315 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
316 return false;
317 }
318 sids->push_back(sid);
319 return true;
320}
321
322// Finds all StreamParams of all media types and attach them to stream_params.
323static void GetCurrentStreamParams(const SessionDescription* sdesc,
324 StreamParamsVec* stream_params) {
325 if (!sdesc)
326 return;
327
328 const ContentInfos& contents = sdesc->contents();
329 for (ContentInfos::const_iterator content = contents.begin();
330 content != contents.end(); ++content) {
331 if (!IsMediaContent(&*content)) {
332 continue;
333 }
334 const MediaContentDescription* media =
335 static_cast<const MediaContentDescription*>(
336 content->description);
337 const StreamParamsVec& streams = media->streams();
338 for (StreamParamsVec::const_iterator it = streams.begin();
339 it != streams.end(); ++it) {
340 stream_params->push_back(*it);
341 }
342 }
343}
344
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000345// Filters the data codecs for the data channel type.
346void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
347 // Filter RTP codec for SCTP and vice versa.
348 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
349 for (std::vector<DataCodec>::iterator iter = codecs->begin();
350 iter != codecs->end();) {
351 if (iter->id == codec_id) {
352 iter = codecs->erase(iter);
353 } else {
354 ++iter;
355 }
356 }
357}
358
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000359template <typename IdStruct>
360class UsedIds {
361 public:
362 UsedIds(int min_allowed_id, int max_allowed_id)
363 : min_allowed_id_(min_allowed_id),
364 max_allowed_id_(max_allowed_id),
365 next_id_(max_allowed_id) {
366 }
367
368 // Loops through all Id in |ids| and changes its id if it is
369 // already in use by another IdStruct. Call this methods with all Id
370 // in a session description to make sure no duplicate ids exists.
371 // Note that typename Id must be a type of IdStruct.
372 template <typename Id>
373 void FindAndSetIdUsed(std::vector<Id>* ids) {
374 for (typename std::vector<Id>::iterator it = ids->begin();
375 it != ids->end(); ++it) {
376 FindAndSetIdUsed(&*it);
377 }
378 }
379
380 // Finds and sets an unused id if the |idstruct| id is already in use.
381 void FindAndSetIdUsed(IdStruct* idstruct) {
382 const int original_id = idstruct->id;
383 int new_id = idstruct->id;
384
385 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
386 // If the original id is not in range - this is an id that can't be
387 // dynamically changed.
388 return;
389 }
390
391 if (IsIdUsed(original_id)) {
392 new_id = FindUnusedId();
393 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
394 << " to " << new_id;
395 idstruct->id = new_id;
396 }
397 SetIdUsed(new_id);
398 }
399
400 private:
401 // Returns the first unused id in reverse order.
402 // This hopefully reduce the risk of more collisions. We want to change the
403 // default ids as little as possible.
404 int FindUnusedId() {
405 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
406 --next_id_;
407 }
408 ASSERT(next_id_ >= min_allowed_id_);
409 return next_id_;
410 }
411
412 bool IsIdUsed(int new_id) {
413 return id_set_.find(new_id) != id_set_.end();
414 }
415
416 void SetIdUsed(int new_id) {
417 id_set_.insert(new_id);
418 }
419
420 const int min_allowed_id_;
421 const int max_allowed_id_;
422 int next_id_;
423 std::set<int> id_set_;
424};
425
426// Helper class used for finding duplicate RTP payload types among audio, video
427// and data codecs. When bundle is used the payload types may not collide.
428class UsedPayloadTypes : public UsedIds<Codec> {
429 public:
430 UsedPayloadTypes()
431 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
432 }
433
434
435 private:
436 static const int kDynamicPayloadTypeMin = 96;
437 static const int kDynamicPayloadTypeMax = 127;
438};
439
440// Helper class used for finding duplicate RTP Header extension ids among
441// audio and video extensions.
442class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
443 public:
444 UsedRtpHeaderExtensionIds()
445 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
446 }
447
448 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000449 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000451 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000452};
453
454static bool IsSctp(const MediaContentDescription* desc) {
455 return ((desc->protocol() == kMediaProtocolSctp) ||
456 (desc->protocol() == kMediaProtocolDtlsSctp));
457}
458
459// Adds a StreamParams for each Stream in Streams with media type
460// media_type to content_description.
461// |current_params| - All currently known StreamParams of any media type.
462template <class C>
463static bool AddStreamParams(
464 MediaType media_type,
465 const MediaSessionOptions::Streams& streams,
466 StreamParamsVec* current_streams,
467 MediaContentDescriptionImpl<C>* content_description,
468 const bool add_legacy_stream) {
Noah Richards2e7a0982015-05-18 14:02:54 -0700469 const bool include_rtx_streams =
470 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471
472 if (streams.empty() && add_legacy_stream) {
473 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200474 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000475 if (IsSctp(content_description)) {
476 GenerateSctpSids(*current_streams, &ssrcs);
477 } else {
Noah Richards2e7a0982015-05-18 14:02:54 -0700478 int num_ssrcs = include_rtx_streams ? 2 : 1;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000479 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000480 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700481 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
483 content_description->set_multistream(true);
484 } else {
485 content_description->AddLegacyStream(ssrcs[0]);
486 }
487 return true;
488 }
489
490 MediaSessionOptions::Streams::const_iterator stream_it;
491 for (stream_it = streams.begin();
492 stream_it != streams.end(); ++stream_it) {
493 if (stream_it->type != media_type)
494 continue; // Wrong media type.
495
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000496 const StreamParams* param =
497 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 // groupid is empty for StreamParams generated using
499 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000500 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000501 // This is a new stream.
502 // Get a CNAME. Either new or same as one of the other synched streams.
503 std::string cname;
504 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
505 &cname)) {
506 return false;
507 }
508
Peter Boström0c4e06b2015-10-07 12:23:21 +0200509 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000510 if (IsSctp(content_description)) {
511 GenerateSctpSids(*current_streams, &ssrcs);
512 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000513 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000514 }
515 StreamParams stream_param;
516 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000517 // Add the generated ssrc.
518 for (size_t i = 0; i < ssrcs.size(); ++i) {
519 stream_param.ssrcs.push_back(ssrcs[i]);
520 }
521 if (stream_it->num_sim_layers > 1) {
522 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
523 stream_param.ssrc_groups.push_back(group);
524 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700525 // Generate extra ssrcs for include_rtx_streams case.
526 if (include_rtx_streams) {
527 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200528 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700529 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
530 &rtx_ssrcs);
531 for (size_t i = 0; i < ssrcs.size(); ++i) {
532 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
533 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000534 content_description->set_multistream(true);
535 }
536 stream_param.cname = cname;
537 stream_param.sync_label = stream_it->sync_label;
538 content_description->AddStream(stream_param);
539
540 // Store the new StreamParams in current_streams.
541 // This is necessary so that we can use the CNAME for other media types.
542 current_streams->push_back(stream_param);
543 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000544 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000545 }
546 }
547 return true;
548}
549
550// Updates the transport infos of the |sdesc| according to the given
551// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800552// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
553// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000554static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
555 SessionDescription* sdesc) {
556 // The bundle should not be empty.
557 if (!sdesc || !bundle_group.FirstContentName()) {
558 return false;
559 }
560
561 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700562 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563 const TransportInfo* selected_transport_info =
564 sdesc->GetTransportInfoByName(selected_content_name);
565 if (!selected_transport_info) {
566 return false;
567 }
568
569 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700570 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000571 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700572 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000573 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800574 ConnectionRole selected_connection_role =
575 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000576 for (TransportInfos::iterator it =
577 sdesc->transport_infos().begin();
578 it != sdesc->transport_infos().end(); ++it) {
579 if (bundle_group.HasContentName(it->content_name) &&
580 it->content_name != selected_content_name) {
581 it->description.ice_ufrag = selected_ufrag;
582 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800583 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000584 }
585 }
586 return true;
587}
588
589// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
590// sets it to |cryptos|.
591static bool GetCryptosByName(const SessionDescription* sdesc,
592 const std::string& content_name,
593 CryptoParamsVec* cryptos) {
594 if (!sdesc || !cryptos) {
595 return false;
596 }
597
598 const ContentInfo* content = sdesc->GetContentByName(content_name);
599 if (!IsMediaContent(content) || !content->description) {
600 return false;
601 }
602
603 const MediaContentDescription* media_desc =
604 static_cast<const MediaContentDescription*>(content->description);
605 *cryptos = media_desc->cryptos();
606 return true;
607}
608
609// Predicate function used by the remove_if.
610// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
611static bool CryptoNotFound(const CryptoParams crypto,
612 const CryptoParamsVec* filter) {
613 if (filter == NULL) {
614 return true;
615 }
616 for (CryptoParamsVec::const_iterator it = filter->begin();
617 it != filter->end(); ++it) {
618 if (it->cipher_suite == crypto.cipher_suite) {
619 return false;
620 }
621 }
622 return true;
623}
624
625// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
626// which are not available in |filter|.
627static void PruneCryptos(const CryptoParamsVec& filter,
628 CryptoParamsVec* target_cryptos) {
629 if (!target_cryptos) {
630 return;
631 }
632 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
633 target_cryptos->end(),
634 bind2nd(ptr_fun(CryptoNotFound),
635 &filter)),
636 target_cryptos->end());
637}
638
deadbeefb5cb19b2015-11-23 16:39:12 -0800639static bool IsRtpProtocol(const std::string& protocol) {
640 return protocol.empty() ||
641 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
642}
643
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000644static bool IsRtpContent(SessionDescription* sdesc,
645 const std::string& content_name) {
646 bool is_rtp = false;
647 ContentInfo* content = sdesc->GetContentByName(content_name);
648 if (IsMediaContent(content)) {
649 MediaContentDescription* media_desc =
650 static_cast<MediaContentDescription*>(content->description);
651 if (!media_desc) {
652 return false;
653 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800654 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 }
656 return is_rtp;
657}
658
659// Updates the crypto parameters of the |sdesc| according to the given
660// |bundle_group|. The crypto parameters of all the contents within the
661// |bundle_group| should be updated to use the common subset of the
662// available cryptos.
663static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
664 SessionDescription* sdesc) {
665 // The bundle should not be empty.
666 if (!sdesc || !bundle_group.FirstContentName()) {
667 return false;
668 }
669
wu@webrtc.org78187522013-10-07 23:32:02 +0000670 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 // Get the common cryptos.
672 const ContentNames& content_names = bundle_group.content_names();
673 CryptoParamsVec common_cryptos;
674 for (ContentNames::const_iterator it = content_names.begin();
675 it != content_names.end(); ++it) {
676 if (!IsRtpContent(sdesc, *it)) {
677 continue;
678 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000679 // The common cryptos are needed if any of the content does not have DTLS
680 // enabled.
681 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
682 common_cryptos_needed = true;
683 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 if (it == content_names.begin()) {
685 // Initial the common_cryptos with the first content in the bundle group.
686 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
687 return false;
688 }
689 if (common_cryptos.empty()) {
690 // If there's no crypto params, we should just return.
691 return true;
692 }
693 } else {
694 CryptoParamsVec cryptos;
695 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
696 return false;
697 }
698 PruneCryptos(cryptos, &common_cryptos);
699 }
700 }
701
wu@webrtc.org78187522013-10-07 23:32:02 +0000702 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000703 return false;
704 }
705
706 // Update to use the common cryptos.
707 for (ContentNames::const_iterator it = content_names.begin();
708 it != content_names.end(); ++it) {
709 if (!IsRtpContent(sdesc, *it)) {
710 continue;
711 }
712 ContentInfo* content = sdesc->GetContentByName(*it);
713 if (IsMediaContent(content)) {
714 MediaContentDescription* media_desc =
715 static_cast<MediaContentDescription*>(content->description);
716 if (!media_desc) {
717 return false;
718 }
719 media_desc->set_cryptos(common_cryptos);
720 }
721 }
722 return true;
723}
724
725template <class C>
726static bool ContainsRtxCodec(const std::vector<C>& codecs) {
727 typename std::vector<C>::const_iterator it;
728 for (it = codecs.begin(); it != codecs.end(); ++it) {
729 if (IsRtxCodec(*it)) {
730 return true;
731 }
732 }
733 return false;
734}
735
736template <class C>
737static bool IsRtxCodec(const C& codec) {
738 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
739}
740
741// Create a media content to be offered in a session-initiate,
742// according to the given options.rtcp_mux, options.is_muc,
743// options.streams, codecs, secure_transport, crypto, and streams. If we don't
744// currently have crypto (in current_cryptos) and it is enabled (in
745// secure_policy), crypto is created (according to crypto_suites). If
746// add_legacy_stream is true, and current_streams is empty, a legacy
747// stream is created. The created content is added to the offer.
748template <class C>
749static bool CreateMediaContentOffer(
750 const MediaSessionOptions& options,
751 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000752 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000753 const CryptoParamsVec* current_cryptos,
754 const std::vector<std::string>& crypto_suites,
755 const RtpHeaderExtensions& rtp_extensions,
756 bool add_legacy_stream,
757 StreamParamsVec* current_streams,
758 MediaContentDescriptionImpl<C>* offer) {
759 offer->AddCodecs(codecs);
760 offer->SortCodecs();
761
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000762 if (secure_policy == SEC_REQUIRED) {
763 offer->set_crypto_required(CT_SDES);
764 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765 offer->set_rtcp_mux(options.rtcp_mux_enabled);
deadbeef13871492015-12-09 12:37:51 -0800766 // TODO(deadbeef): Once we're sure this works correctly, enable it in
767 // CreateOffer.
768 // if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
769 // offer->set_rtcp_reduced_size(true);
770 // }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000771 offer->set_multistream(options.is_muc);
772 offer->set_rtp_header_extensions(rtp_extensions);
773
774 if (!AddStreamParams(
775 offer->type(), options.streams, current_streams,
776 offer, add_legacy_stream)) {
777 return false;
778 }
779
780#ifdef HAVE_SRTP
781 if (secure_policy != SEC_DISABLED) {
782 if (current_cryptos) {
783 AddMediaCryptos(*current_cryptos, offer);
784 }
785 if (offer->cryptos().empty()) {
786 if (!CreateMediaCryptos(crypto_suites, offer)) {
787 return false;
788 }
789 }
790 }
791#endif
792
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000793 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000794 return false;
795 }
796 return true;
797}
798
799template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000800static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
801 const std::string& codec1_id_str,
802 const std::vector<C>& codecs2,
803 const std::string& codec2_id_str) {
804 int codec1_id;
805 int codec2_id;
806 C codec1;
807 C codec2;
808 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
809 !rtc::FromString(codec2_id_str, &codec2_id) ||
810 !FindCodecById(codecs1, codec1_id, &codec1) ||
811 !FindCodecById(codecs2, codec2_id, &codec2)) {
812 return false;
813 }
814 return codec1.Matches(codec2);
815}
816
817template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000818static void NegotiateCodecs(const std::vector<C>& local_codecs,
819 const std::vector<C>& offered_codecs,
820 std::vector<C>* negotiated_codecs) {
821 typename std::vector<C>::const_iterator ours;
822 for (ours = local_codecs.begin();
823 ours != local_codecs.end(); ++ours) {
824 typename std::vector<C>::const_iterator theirs;
825 for (theirs = offered_codecs.begin();
826 theirs != offered_codecs.end(); ++theirs) {
827 if (ours->Matches(*theirs)) {
828 C negotiated = *ours;
829 negotiated.IntersectFeedbackParams(*theirs);
830 if (IsRtxCodec(negotiated)) {
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000831 std::string offered_apt_value;
832 std::string local_apt_value;
833 if (!ours->GetParam(kCodecParamAssociatedPayloadType,
834 &local_apt_value) ||
835 !theirs->GetParam(kCodecParamAssociatedPayloadType,
836 &offered_apt_value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000837 LOG(LS_WARNING) << "RTX missing associated payload type.";
838 continue;
839 }
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000840 // Only negotiate RTX if kCodecParamAssociatedPayloadType has been
841 // set in local and remote codecs, and they match.
842 if (!ReferencedCodecsMatch(local_codecs, local_apt_value,
843 offered_codecs, offered_apt_value)) {
844 LOG(LS_WARNING) << "RTX associated codecs don't match.";
845 continue;
846 }
847 negotiated.SetParam(kCodecParamAssociatedPayloadType,
848 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000849 }
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000850
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000851 negotiated.id = theirs->id;
wu@webrtc.orgff1b1bf2014-06-20 20:57:42 +0000852 // RFC3264: Although the answerer MAY list the formats in their desired
853 // order of preference, it is RECOMMENDED that unless there is a
854 // specific reason, the answerer list formats in the same relative order
855 // they were present in the offer.
856 negotiated.preference = theirs->preference;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000857 negotiated_codecs->push_back(negotiated);
858 }
859 }
860 }
861}
862
863template <class C>
864static bool FindMatchingCodec(const std::vector<C>& codecs,
865 const C& codec_to_match,
866 C* found_codec) {
867 for (typename std::vector<C>::const_iterator it = codecs.begin();
868 it != codecs.end(); ++it) {
869 if (it->Matches(codec_to_match)) {
870 if (found_codec != NULL) {
871 *found_codec= *it;
872 }
873 return true;
874 }
875 }
876 return false;
877}
878
879// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
880// already exist in |offered_codecs| and ensure the payload types don't
881// collide.
882template <class C>
883static void FindCodecsToOffer(
884 const std::vector<C>& reference_codecs,
885 std::vector<C>* offered_codecs,
886 UsedPayloadTypes* used_pltypes) {
887
888 typedef std::map<int, C> RtxCodecReferences;
889 RtxCodecReferences new_rtx_codecs;
890
891 // Find all new RTX codecs.
892 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
893 it != reference_codecs.end(); ++it) {
894 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) {
895 C rtx_codec = *it;
896 int referenced_pl_type =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000897 rtc::FromString<int>(0,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000898 rtx_codec.params[kCodecParamAssociatedPayloadType]);
899 new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type,
900 rtx_codec));
901 }
902 }
903
904 // Add all new codecs that are not RTX codecs.
905 for (typename std::vector<C>::const_iterator it = reference_codecs.begin();
906 it != reference_codecs.end(); ++it) {
907 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) {
908 C codec = *it;
909 int original_payload_id = codec.id;
910 used_pltypes->FindAndSetIdUsed(&codec);
911 offered_codecs->push_back(codec);
912
913 // If this codec is referenced by a new RTX codec, update the reference
914 // in the RTX codec with the new payload type.
915 typename RtxCodecReferences::iterator rtx_it =
916 new_rtx_codecs.find(original_payload_id);
917 if (rtx_it != new_rtx_codecs.end()) {
918 C& rtx_codec = rtx_it->second;
919 rtx_codec.params[kCodecParamAssociatedPayloadType] =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000920 rtc::ToString(codec.id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000921 }
922 }
923 }
924
925 // Add all new RTX codecs.
926 for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin();
927 it != new_rtx_codecs.end(); ++it) {
928 C& rtx_codec = it->second;
929 used_pltypes->FindAndSetIdUsed(&rtx_codec);
930 offered_codecs->push_back(rtx_codec);
931 }
932}
933
934
935static bool FindByUri(const RtpHeaderExtensions& extensions,
936 const RtpHeaderExtension& ext_to_match,
937 RtpHeaderExtension* found_extension) {
938 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
939 it != extensions.end(); ++it) {
940 // We assume that all URIs are given in a canonical format.
941 if (it->uri == ext_to_match.uri) {
942 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000943 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000944 }
945 return true;
946 }
947 }
948 return false;
949}
950
deadbeefa5b273a2015-08-20 17:30:13 -0700951// Iterates through |offered_extensions|, adding each one to |all_extensions|
952// and |used_ids|, and resolving ID conflicts. If an offered extension has the
953// same URI as one in |all_extensions|, it will re-use the same ID and won't be
954// treated as a conflict.
955static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
956 RtpHeaderExtensions* all_extensions,
957 UsedRtpHeaderExtensionIds* used_ids) {
958 for (auto& extension : *offered_extensions) {
959 RtpHeaderExtension existing;
960 if (FindByUri(*all_extensions, extension, &existing)) {
961 extension.id = existing.id;
962 } else {
963 used_ids->FindAndSetIdUsed(&extension);
964 all_extensions->push_back(extension);
965 }
966 }
967}
968
969// Adds |reference_extensions| to |offered_extensions|, while updating
970// |all_extensions| and |used_ids|.
971static void FindRtpHdrExtsToOffer(
972 const RtpHeaderExtensions& reference_extensions,
973 RtpHeaderExtensions* offered_extensions,
974 RtpHeaderExtensions* all_extensions,
975 UsedRtpHeaderExtensionIds* used_ids) {
976 for (auto reference_extension : reference_extensions) {
977 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
978 RtpHeaderExtension existing;
979 if (FindByUri(*all_extensions, reference_extension, &existing)) {
980 offered_extensions->push_back(existing);
981 } else {
982 used_ids->FindAndSetIdUsed(&reference_extension);
983 all_extensions->push_back(reference_extension);
984 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000985 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000986 }
987 }
988}
989
990static void NegotiateRtpHeaderExtensions(
991 const RtpHeaderExtensions& local_extensions,
992 const RtpHeaderExtensions& offered_extensions,
993 RtpHeaderExtensions* negotiated_extenstions) {
994 RtpHeaderExtensions::const_iterator ours;
995 for (ours = local_extensions.begin();
996 ours != local_extensions.end(); ++ours) {
997 RtpHeaderExtension theirs;
998 if (FindByUri(offered_extensions, *ours, &theirs)) {
999 // We respond with their RTP header extension id.
1000 negotiated_extenstions->push_back(theirs);
1001 }
1002 }
1003}
1004
1005static void StripCNCodecs(AudioCodecs* audio_codecs) {
1006 AudioCodecs::iterator iter = audio_codecs->begin();
1007 while (iter != audio_codecs->end()) {
1008 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
1009 iter = audio_codecs->erase(iter);
1010 } else {
1011 ++iter;
1012 }
1013 }
1014}
1015
1016// Create a media content to be answered in a session-accept,
1017// according to the given options.rtcp_mux, options.streams, codecs,
1018// crypto, and streams. If we don't currently have crypto (in
1019// current_cryptos) and it is enabled (in secure_policy), crypto is
1020// created (according to crypto_suites). If add_legacy_stream is
1021// true, and current_streams is empty, a legacy stream is created.
1022// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1023// from the incoming session-initiate. If the negotiation fails, this
1024// method returns false. The created content is added to the offer.
1025template <class C>
1026static bool CreateMediaContentAnswer(
1027 const MediaContentDescriptionImpl<C>* offer,
1028 const MediaSessionOptions& options,
1029 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001030 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001031 const CryptoParamsVec* current_cryptos,
1032 const RtpHeaderExtensions& local_rtp_extenstions,
1033 StreamParamsVec* current_streams,
1034 bool add_legacy_stream,
1035 bool bundle_enabled,
1036 MediaContentDescriptionImpl<C>* answer) {
1037 std::vector<C> negotiated_codecs;
1038 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1039 answer->AddCodecs(negotiated_codecs);
1040 answer->SortCodecs();
1041 answer->set_protocol(offer->protocol());
1042 RtpHeaderExtensions negotiated_rtp_extensions;
1043 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1044 offer->rtp_header_extensions(),
1045 &negotiated_rtp_extensions);
1046 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1047
1048 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
deadbeef13871492015-12-09 12:37:51 -08001049 // TODO(deadbeef): Once we're sure this works correctly, enable it in
1050 // CreateAnswer.
1051 // if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1052 // answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1053 // }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001054
1055 if (sdes_policy != SEC_DISABLED) {
1056 CryptoParams crypto;
1057 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
1058 if (current_cryptos) {
1059 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1060 }
1061 answer->AddCrypto(crypto);
1062 }
1063 }
1064
1065 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001066 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001067 return false;
1068 }
1069
1070 if (!AddStreamParams(
1071 answer->type(), options.streams, current_streams,
1072 answer, add_legacy_stream)) {
1073 return false; // Something went seriously wrong.
1074 }
1075
1076 // Make sure the answer media content direction is per default set as
1077 // described in RFC3264 section 6.1.
1078 switch (offer->direction()) {
1079 case MD_INACTIVE:
1080 answer->set_direction(MD_INACTIVE);
1081 break;
1082 case MD_SENDONLY:
1083 answer->set_direction(MD_RECVONLY);
1084 break;
1085 case MD_RECVONLY:
deadbeefb5cb19b2015-11-23 16:39:12 -08001086 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1087 answer->streams().empty()
1088 ? MD_INACTIVE
1089 : MD_SENDONLY);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001090 break;
1091 case MD_SENDRECV:
deadbeefb5cb19b2015-11-23 16:39:12 -08001092 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1093 answer->streams().empty()
1094 ? MD_RECVONLY
1095 : MD_SENDRECV);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001096 break;
1097 default:
deadbeefc80741f2015-10-22 13:14:45 -07001098 RTC_DCHECK(false && "MediaContentDescription has unexpected direction.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 break;
1100 }
1101
1102 return true;
1103}
1104
1105static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001106 const std::string& protocol,
1107 bool secure_transport) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 // Data channels can have a protocol of SCTP or SCTP/DTLS.
1109 if (type == MEDIA_TYPE_DATA &&
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001110 ((protocol == kMediaProtocolSctp && !secure_transport)||
1111 (protocol == kMediaProtocolDtlsSctp && secure_transport))) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001112 return true;
1113 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001114
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001115 // Since not all applications serialize and deserialize the media protocol,
1116 // we will have to accept |protocol| to be empty.
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001117 return protocol == kMediaProtocolAvpf || protocol.empty() ||
1118 protocol == kMediaProtocolSavpf ||
1119 (protocol == kMediaProtocolDtlsSavpf && secure_transport);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001120}
1121
1122static void SetMediaProtocol(bool secure_transport,
1123 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001124 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001125 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001126 else if (secure_transport)
1127 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001128 else
1129 desc->set_protocol(kMediaProtocolAvpf);
1130}
1131
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001132// Gets the TransportInfo of the given |content_name| from the
1133// |current_description|. If doesn't exist, returns a new one.
1134static const TransportDescription* GetTransportDescription(
1135 const std::string& content_name,
1136 const SessionDescription* current_description) {
1137 const TransportDescription* desc = NULL;
1138 if (current_description) {
1139 const TransportInfo* info =
1140 current_description->GetTransportInfoByName(content_name);
1141 if (info) {
1142 desc = &info->description;
1143 }
1144 }
1145 return desc;
1146}
1147
1148// Gets the current DTLS state from the transport description.
1149static bool IsDtlsActive(
1150 const std::string& content_name,
1151 const SessionDescription* current_description) {
1152 if (!current_description)
1153 return false;
1154
1155 const ContentInfo* content =
1156 current_description->GetContentByName(content_name);
1157 if (!content)
1158 return false;
1159
1160 const TransportDescription* current_tdesc =
1161 GetTransportDescription(content_name, current_description);
1162 if (!current_tdesc)
1163 return false;
1164
1165 return current_tdesc->secure();
1166}
1167
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001168std::string MediaTypeToString(MediaType type) {
1169 std::string type_str;
1170 switch (type) {
1171 case MEDIA_TYPE_AUDIO:
1172 type_str = "audio";
1173 break;
1174 case MEDIA_TYPE_VIDEO:
1175 type_str = "video";
1176 break;
1177 case MEDIA_TYPE_DATA:
1178 type_str = "data";
1179 break;
1180 default:
1181 ASSERT(false);
1182 break;
1183 }
1184 return type_str;
1185}
1186
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001187void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001188 const std::string& id,
1189 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001190 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001191}
1192
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001193void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001194 const std::string& id,
1195 const std::string& sync_label,
1196 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001197 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001198}
1199
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001200void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001201 MediaType type,
1202 const std::string& id,
1203 const std::string& sync_label,
1204 int num_sim_layers) {
1205 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001206
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207 // If we haven't already set the data_channel_type, and we add a
1208 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001209 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001210 data_channel_type = DCT_RTP;
1211}
1212
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001213void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001214 const std::string& id) {
1215 Streams::iterator stream_it = streams.begin();
1216 for (; stream_it != streams.end(); ++stream_it) {
1217 if (stream_it->type == type && stream_it->id == id) {
1218 streams.erase(stream_it);
1219 return;
1220 }
1221 }
1222 ASSERT(false);
1223}
1224
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001225bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1226 Streams::const_iterator stream_it = streams.begin();
1227 for (; stream_it != streams.end(); ++stream_it) {
1228 if (stream_it->type == type) {
1229 return true;
1230 }
1231 }
1232 return false;
1233}
1234
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001235MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1236 const TransportDescriptionFactory* transport_desc_factory)
1237 : secure_(SEC_DISABLED),
1238 add_legacy_(true),
1239 transport_desc_factory_(transport_desc_factory) {
1240}
1241
1242MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1243 ChannelManager* channel_manager,
1244 const TransportDescriptionFactory* transport_desc_factory)
1245 : secure_(SEC_DISABLED),
1246 add_legacy_(true),
1247 transport_desc_factory_(transport_desc_factory) {
1248 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1249 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1250 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1251 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1252 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1253}
1254
1255SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1256 const MediaSessionOptions& options,
1257 const SessionDescription* current_description) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001258 scoped_ptr<SessionDescription> offer(new SessionDescription());
1259
1260 StreamParamsVec current_streams;
1261 GetCurrentStreamParams(current_description, &current_streams);
1262
1263 AudioCodecs audio_codecs;
1264 VideoCodecs video_codecs;
1265 DataCodecs data_codecs;
1266 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1267 &data_codecs);
1268
1269 if (!options.vad_enabled) {
1270 // If application doesn't want CN codecs in offer.
1271 StripCNCodecs(&audio_codecs);
1272 }
1273
1274 RtpHeaderExtensions audio_rtp_extensions;
1275 RtpHeaderExtensions video_rtp_extensions;
1276 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1277 &video_rtp_extensions);
1278
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001279 bool audio_added = false;
1280 bool video_added = false;
1281 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001282
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001283 // Iterate through the contents of |current_description| to maintain the order
1284 // of the m-lines in the new offer.
1285 if (current_description) {
1286 ContentInfos::const_iterator it = current_description->contents().begin();
1287 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001288 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001289 if (!AddAudioContentForOffer(options, current_description,
1290 audio_rtp_extensions, audio_codecs,
1291 &current_streams, offer.get())) {
1292 return NULL;
1293 }
1294 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001295 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001296 if (!AddVideoContentForOffer(options, current_description,
1297 video_rtp_extensions, video_codecs,
1298 &current_streams, offer.get())) {
1299 return NULL;
1300 }
1301 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001302 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001303 MediaSessionOptions options_copy(options);
1304 if (IsSctp(static_cast<const MediaContentDescription*>(
1305 it->description))) {
1306 options_copy.data_channel_type = DCT_SCTP;
1307 }
1308 if (!AddDataContentForOffer(options_copy, current_description,
1309 &data_codecs, &current_streams,
1310 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001311 return NULL;
1312 }
1313 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001314 } else {
1315 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001316 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001317 }
1318 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001319
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001320 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001321 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001322 !AddAudioContentForOffer(options, current_description,
1323 audio_rtp_extensions, audio_codecs,
1324 &current_streams, offer.get())) {
1325 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001326 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001327 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001328 !AddVideoContentForOffer(options, current_description,
1329 video_rtp_extensions, video_codecs,
1330 &current_streams, offer.get())) {
1331 return NULL;
1332 }
1333 if (!data_added && options.has_data() &&
1334 !AddDataContentForOffer(options, current_description, &data_codecs,
1335 &current_streams, offer.get())) {
1336 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001337 }
1338
1339 // Bundle the contents together, if we've been asked to do so, and update any
1340 // parameters that need to be tweaked for BUNDLE.
1341 if (options.bundle_enabled) {
1342 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1343 for (ContentInfos::const_iterator content = offer->contents().begin();
1344 content != offer->contents().end(); ++content) {
1345 offer_bundle.AddContentName(content->name);
1346 }
1347 offer->AddGroup(offer_bundle);
1348 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1349 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1350 return NULL;
1351 }
1352 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1353 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1354 return NULL;
1355 }
1356 }
1357
1358 return offer.release();
1359}
1360
1361SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1362 const SessionDescription* offer, const MediaSessionOptions& options,
1363 const SessionDescription* current_description) const {
1364 // The answer contains the intersection of the codecs in the offer with the
1365 // codecs we support, ordered by our local preference. As indicated by
1366 // XEP-0167, we retain the same payload ids from the offer in the answer.
1367 scoped_ptr<SessionDescription> answer(new SessionDescription());
1368
1369 StreamParamsVec current_streams;
1370 GetCurrentStreamParams(current_description, &current_streams);
1371
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001372 if (offer) {
1373 ContentInfos::const_iterator it = offer->contents().begin();
1374 for (; it != offer->contents().end(); ++it) {
1375 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1376 if (!AddAudioContentForAnswer(offer, options, current_description,
1377 &current_streams, answer.get())) {
1378 return NULL;
1379 }
1380 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1381 if (!AddVideoContentForAnswer(offer, options, current_description,
1382 &current_streams, answer.get())) {
1383 return NULL;
1384 }
1385 } else {
1386 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1387 if (!AddDataContentForAnswer(offer, options, current_description,
1388 &current_streams, answer.get())) {
1389 return NULL;
1390 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001391 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001392 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001393 }
1394
1395 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1396 // group in the answer with the appropriate content names.
1397 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1398 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1399 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1400 for (ContentInfos::const_iterator content = answer->contents().begin();
1401 content != answer->contents().end(); ++content) {
1402 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1403 answer_bundle.AddContentName(content->name);
1404 }
1405 }
1406 if (answer_bundle.FirstContentName()) {
1407 answer->AddGroup(answer_bundle);
1408
1409 // Share the same ICE credentials and crypto params across all contents,
1410 // as BUNDLE requires.
1411 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1412 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1413 return NULL;
1414 }
1415
1416 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1417 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1418 return NULL;
1419 }
1420 }
1421 }
1422
1423 return answer.release();
1424}
1425
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001426void MediaSessionDescriptionFactory::GetCodecsToOffer(
1427 const SessionDescription* current_description,
1428 AudioCodecs* audio_codecs,
1429 VideoCodecs* video_codecs,
1430 DataCodecs* data_codecs) const {
1431 UsedPayloadTypes used_pltypes;
1432 audio_codecs->clear();
1433 video_codecs->clear();
1434 data_codecs->clear();
1435
1436
1437 // First - get all codecs from the current description if the media type
1438 // is used.
1439 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1440 // type is added.
1441 if (current_description) {
1442 const AudioContentDescription* audio =
1443 GetFirstAudioContentDescription(current_description);
1444 if (audio) {
1445 *audio_codecs = audio->codecs();
1446 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1447 }
1448 const VideoContentDescription* video =
1449 GetFirstVideoContentDescription(current_description);
1450 if (video) {
1451 *video_codecs = video->codecs();
1452 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1453 }
1454 const DataContentDescription* data =
1455 GetFirstDataContentDescription(current_description);
1456 if (data) {
1457 *data_codecs = data->codecs();
1458 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1459 }
1460 }
1461
1462 // Add our codecs that are not in |current_description|.
1463 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1464 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1465 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1466}
1467
1468void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1469 const SessionDescription* current_description,
1470 RtpHeaderExtensions* audio_extensions,
1471 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001472 // All header extensions allocated from the same range to avoid potential
1473 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001474 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001475 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001476 audio_extensions->clear();
1477 video_extensions->clear();
1478
1479 // First - get all extensions from the current description if the media type
1480 // is used.
1481 // Add them to |used_ids| so the local ids are not reused if a new media
1482 // type is added.
1483 if (current_description) {
1484 const AudioContentDescription* audio =
1485 GetFirstAudioContentDescription(current_description);
1486 if (audio) {
1487 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001488 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001489 }
1490 const VideoContentDescription* video =
1491 GetFirstVideoContentDescription(current_description);
1492 if (video) {
1493 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001494 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001495 }
1496 }
1497
1498 // Add our default RTP header extensions that are not in
1499 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001500 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1501 &all_extensions, &used_ids);
1502 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1503 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001504}
1505
1506bool MediaSessionDescriptionFactory::AddTransportOffer(
1507 const std::string& content_name,
1508 const TransportOptions& transport_options,
1509 const SessionDescription* current_desc,
1510 SessionDescription* offer_desc) const {
1511 if (!transport_desc_factory_)
1512 return false;
1513 const TransportDescription* current_tdesc =
1514 GetTransportDescription(content_name, current_desc);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001515 rtc::scoped_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001516 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1517 bool ret = (new_tdesc.get() != NULL &&
1518 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1519 if (!ret) {
1520 LOG(LS_ERROR)
1521 << "Failed to AddTransportOffer, content name=" << content_name;
1522 }
1523 return ret;
1524}
1525
1526TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1527 const std::string& content_name,
1528 const SessionDescription* offer_desc,
1529 const TransportOptions& transport_options,
1530 const SessionDescription* current_desc) const {
1531 if (!transport_desc_factory_)
1532 return NULL;
1533 const TransportDescription* offer_tdesc =
1534 GetTransportDescription(content_name, offer_desc);
1535 const TransportDescription* current_tdesc =
1536 GetTransportDescription(content_name, current_desc);
1537 return
1538 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1539 current_tdesc);
1540}
1541
1542bool MediaSessionDescriptionFactory::AddTransportAnswer(
1543 const std::string& content_name,
1544 const TransportDescription& transport_desc,
1545 SessionDescription* answer_desc) const {
1546 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1547 transport_desc))) {
1548 LOG(LS_ERROR)
1549 << "Failed to AddTransportAnswer, content name=" << content_name;
1550 return false;
1551 }
1552 return true;
1553}
1554
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001555bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1556 const MediaSessionOptions& options,
1557 const SessionDescription* current_description,
1558 const RtpHeaderExtensions& audio_rtp_extensions,
1559 const AudioCodecs& audio_codecs,
1560 StreamParamsVec* current_streams,
1561 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001562 const ContentInfo* current_audio_content =
1563 GetFirstAudioContent(current_description);
1564 std::string content_name =
1565 current_audio_content ? current_audio_content->name : CN_AUDIO;
1566
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001567 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001568 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1569 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001570
1571 scoped_ptr<AudioContentDescription> audio(new AudioContentDescription());
1572 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001573 GetSupportedAudioCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001574 if (!CreateMediaContentOffer(
1575 options,
1576 audio_codecs,
1577 sdes_policy,
1578 GetCryptos(GetFirstAudioContentDescription(current_description)),
1579 crypto_suites,
1580 audio_rtp_extensions,
1581 add_legacy_,
1582 current_streams,
1583 audio.get())) {
1584 return false;
1585 }
1586 audio->set_lang(lang_);
1587
1588 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1589 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001590
deadbeefc80741f2015-10-22 13:14:45 -07001591 if (!audio->streams().empty()) {
1592 if (options.recv_audio) {
1593 audio->set_direction(MD_SENDRECV);
1594 } else {
1595 audio->set_direction(MD_SENDONLY);
1596 }
1597 } else {
1598 if (options.recv_audio) {
1599 audio->set_direction(MD_RECVONLY);
1600 } else {
1601 audio->set_direction(MD_INACTIVE);
1602 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001603 }
1604
deadbeef44f08192015-12-15 16:20:09 -08001605 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001606 if (!AddTransportOffer(content_name, options.audio_transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001607 current_description, desc)) {
1608 return false;
1609 }
1610
1611 return true;
1612}
1613
1614bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1615 const MediaSessionOptions& options,
1616 const SessionDescription* current_description,
1617 const RtpHeaderExtensions& video_rtp_extensions,
1618 const VideoCodecs& video_codecs,
1619 StreamParamsVec* current_streams,
1620 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001621 const ContentInfo* current_video_content =
1622 GetFirstVideoContent(current_description);
1623 std::string content_name =
1624 current_video_content ? current_video_content->name : CN_VIDEO;
1625
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001626 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001627 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1628 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001629
1630 scoped_ptr<VideoContentDescription> video(new VideoContentDescription());
1631 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001632 GetSupportedVideoCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001633 if (!CreateMediaContentOffer(
1634 options,
1635 video_codecs,
1636 sdes_policy,
1637 GetCryptos(GetFirstVideoContentDescription(current_description)),
1638 crypto_suites,
1639 video_rtp_extensions,
1640 add_legacy_,
1641 current_streams,
1642 video.get())) {
1643 return false;
1644 }
1645
1646 video->set_bandwidth(options.video_bandwidth);
1647
1648 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1649 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001650
deadbeefc80741f2015-10-22 13:14:45 -07001651 if (!video->streams().empty()) {
1652 if (options.recv_video) {
1653 video->set_direction(MD_SENDRECV);
1654 } else {
1655 video->set_direction(MD_SENDONLY);
1656 }
1657 } else {
1658 if (options.recv_video) {
1659 video->set_direction(MD_RECVONLY);
1660 } else {
1661 video->set_direction(MD_INACTIVE);
1662 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001663 }
1664
deadbeef44f08192015-12-15 16:20:09 -08001665 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001666 if (!AddTransportOffer(content_name, options.video_transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001667 current_description, desc)) {
1668 return false;
1669 }
1670
1671 return true;
1672}
1673
1674bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1675 const MediaSessionOptions& options,
1676 const SessionDescription* current_description,
1677 DataCodecs* data_codecs,
1678 StreamParamsVec* current_streams,
1679 SessionDescription* desc) const {
1680 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1681
1682 scoped_ptr<DataContentDescription> data(new DataContentDescription());
1683 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1684
1685 FilterDataCodecs(data_codecs, is_sctp);
1686
deadbeef44f08192015-12-15 16:20:09 -08001687 const ContentInfo* current_data_content =
1688 GetFirstDataContent(current_description);
1689 std::string content_name =
1690 current_data_content ? current_data_content->name : CN_DATA;
1691
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001692 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001693 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1694 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001695 std::vector<std::string> crypto_suites;
1696 if (is_sctp) {
1697 // SDES doesn't make sense for SCTP, so we disable it, and we only
1698 // get SDES crypto suites for RTP-based data channels.
1699 sdes_policy = cricket::SEC_DISABLED;
1700 // Unlike SetMediaProtocol below, we need to set the protocol
1701 // before we call CreateMediaContentOffer. Otherwise,
1702 // CreateMediaContentOffer won't know this is SCTP and will
1703 // generate SSRCs rather than SIDs.
1704 data->set_protocol(
1705 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1706 } else {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001707 GetSupportedDataCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001708 }
1709
1710 if (!CreateMediaContentOffer(
1711 options,
1712 *data_codecs,
1713 sdes_policy,
1714 GetCryptos(GetFirstDataContentDescription(current_description)),
1715 crypto_suites,
1716 RtpHeaderExtensions(),
1717 add_legacy_,
1718 current_streams,
1719 data.get())) {
1720 return false;
1721 }
1722
1723 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001724 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001725 } else {
1726 data->set_bandwidth(options.data_bandwidth);
1727 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001728 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001729 }
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001730 if (!AddTransportOffer(content_name, options.data_transport_options,
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001731 current_description, desc)) {
1732 return false;
1733 }
1734 return true;
1735}
1736
1737bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1738 const SessionDescription* offer,
1739 const MediaSessionOptions& options,
1740 const SessionDescription* current_description,
1741 StreamParamsVec* current_streams,
1742 SessionDescription* answer) const {
1743 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1744
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001745 scoped_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
1746 audio_content->name, offer, options.audio_transport_options,
1747 current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001748 if (!audio_transport) {
1749 return false;
1750 }
1751
1752 AudioCodecs audio_codecs = audio_codecs_;
1753 if (!options.vad_enabled) {
1754 StripCNCodecs(&audio_codecs);
1755 }
1756
1757 bool bundle_enabled =
1758 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1759 scoped_ptr<AudioContentDescription> audio_answer(
1760 new AudioContentDescription());
1761 // Do not require or create SDES cryptos if DTLS is used.
1762 cricket::SecurePolicy sdes_policy =
1763 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1764 if (!CreateMediaContentAnswer(
1765 static_cast<const AudioContentDescription*>(
1766 audio_content->description),
1767 options,
1768 audio_codecs,
1769 sdes_policy,
1770 GetCryptos(GetFirstAudioContentDescription(current_description)),
1771 audio_rtp_extensions_,
1772 current_streams,
1773 add_legacy_,
1774 bundle_enabled,
1775 audio_answer.get())) {
1776 return false; // Fails the session setup.
1777 }
1778
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001779 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001780 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1781 audio_answer->protocol(),
1782 audio_transport->secure());
1783 if (!rejected) {
1784 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1785 } else {
1786 // RFC 3264
1787 // The answer MUST contain the same number of m-lines as the offer.
1788 LOG(LS_INFO) << "Audio is not supported in the answer.";
1789 }
1790
1791 answer->AddContent(audio_content->name, audio_content->type, rejected,
1792 audio_answer.release());
1793 return true;
1794}
1795
1796bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1797 const SessionDescription* offer,
1798 const MediaSessionOptions& options,
1799 const SessionDescription* current_description,
1800 StreamParamsVec* current_streams,
1801 SessionDescription* answer) const {
1802 const ContentInfo* video_content = GetFirstVideoContent(offer);
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001803 scoped_ptr<TransportDescription> video_transport(CreateTransportAnswer(
1804 video_content->name, offer, options.video_transport_options,
1805 current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001806 if (!video_transport) {
1807 return false;
1808 }
1809
1810 scoped_ptr<VideoContentDescription> video_answer(
1811 new VideoContentDescription());
1812 // Do not require or create SDES cryptos if DTLS is used.
1813 cricket::SecurePolicy sdes_policy =
1814 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1815 bool bundle_enabled =
1816 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1817 if (!CreateMediaContentAnswer(
1818 static_cast<const VideoContentDescription*>(
1819 video_content->description),
1820 options,
1821 video_codecs_,
1822 sdes_policy,
1823 GetCryptos(GetFirstVideoContentDescription(current_description)),
1824 video_rtp_extensions_,
1825 current_streams,
1826 add_legacy_,
1827 bundle_enabled,
1828 video_answer.get())) {
1829 return false;
1830 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001831 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001832 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1833 video_answer->protocol(),
1834 video_transport->secure());
1835 if (!rejected) {
1836 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1837 answer)) {
1838 return false;
1839 }
1840 video_answer->set_bandwidth(options.video_bandwidth);
1841 } else {
1842 // RFC 3264
1843 // The answer MUST contain the same number of m-lines as the offer.
1844 LOG(LS_INFO) << "Video is not supported in the answer.";
1845 }
1846 answer->AddContent(video_content->name, video_content->type, rejected,
1847 video_answer.release());
1848 return true;
1849}
1850
1851bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1852 const SessionDescription* offer,
1853 const MediaSessionOptions& options,
1854 const SessionDescription* current_description,
1855 StreamParamsVec* current_streams,
1856 SessionDescription* answer) const {
1857 const ContentInfo* data_content = GetFirstDataContent(offer);
Taylor Brandstetterf475d362016-01-08 15:35:57 -08001858 scoped_ptr<TransportDescription> data_transport(CreateTransportAnswer(
1859 data_content->name, offer, options.data_transport_options,
1860 current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001861 if (!data_transport) {
1862 return false;
1863 }
1864 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1865 std::vector<DataCodec> data_codecs(data_codecs_);
1866 FilterDataCodecs(&data_codecs, is_sctp);
1867
1868 scoped_ptr<DataContentDescription> data_answer(
1869 new DataContentDescription());
1870 // Do not require or create SDES cryptos if DTLS is used.
1871 cricket::SecurePolicy sdes_policy =
1872 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1873 bool bundle_enabled =
1874 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1875 if (!CreateMediaContentAnswer(
1876 static_cast<const DataContentDescription*>(
1877 data_content->description),
1878 options,
1879 data_codecs_,
1880 sdes_policy,
1881 GetCryptos(GetFirstDataContentDescription(current_description)),
1882 RtpHeaderExtensions(),
1883 current_streams,
1884 add_legacy_,
1885 bundle_enabled,
1886 data_answer.get())) {
1887 return false; // Fails the session setup.
1888 }
1889
1890 bool rejected = !options.has_data() || data_content->rejected ||
1891 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1892 data_answer->protocol(),
1893 data_transport->secure());
1894 if (!rejected) {
1895 data_answer->set_bandwidth(options.data_bandwidth);
1896 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1897 answer)) {
1898 return false;
1899 }
1900 } else {
1901 // RFC 3264
1902 // The answer MUST contain the same number of m-lines as the offer.
1903 LOG(LS_INFO) << "Data is not supported in the answer.";
1904 }
1905 answer->AddContent(data_content->name, data_content->type, rejected,
1906 data_answer.release());
1907 return true;
1908}
1909
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001910bool IsMediaContent(const ContentInfo* content) {
1911 return (content &&
1912 (content->type == NS_JINGLE_RTP ||
1913 content->type == NS_JINGLE_DRAFT_SCTP));
1914}
1915
1916bool IsAudioContent(const ContentInfo* content) {
1917 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1918}
1919
1920bool IsVideoContent(const ContentInfo* content) {
1921 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1922}
1923
1924bool IsDataContent(const ContentInfo* content) {
1925 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1926}
1927
1928static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1929 MediaType media_type) {
1930 for (ContentInfos::const_iterator content = contents.begin();
1931 content != contents.end(); content++) {
1932 if (IsMediaContentOfType(&*content, media_type)) {
1933 return &*content;
1934 }
1935 }
1936 return NULL;
1937}
1938
1939const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1940 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1941}
1942
1943const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1944 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1945}
1946
1947const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1948 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1949}
1950
1951static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1952 MediaType media_type) {
1953 if (sdesc == NULL)
1954 return NULL;
1955
1956 return GetFirstMediaContent(sdesc->contents(), media_type);
1957}
1958
1959const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1960 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1961}
1962
1963const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1964 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1965}
1966
1967const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1968 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1969}
1970
1971const MediaContentDescription* GetFirstMediaContentDescription(
1972 const SessionDescription* sdesc, MediaType media_type) {
1973 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1974 const ContentDescription* description = content ? content->description : NULL;
1975 return static_cast<const MediaContentDescription*>(description);
1976}
1977
1978const AudioContentDescription* GetFirstAudioContentDescription(
1979 const SessionDescription* sdesc) {
1980 return static_cast<const AudioContentDescription*>(
1981 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1982}
1983
1984const VideoContentDescription* GetFirstVideoContentDescription(
1985 const SessionDescription* sdesc) {
1986 return static_cast<const VideoContentDescription*>(
1987 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
1988}
1989
1990const DataContentDescription* GetFirstDataContentDescription(
1991 const SessionDescription* sdesc) {
1992 return static_cast<const DataContentDescription*>(
1993 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
1994}
1995
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001996} // namespace cricket