blob: ea0eaa26836055c1a7c88f94898f2654894bfca1 [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
214static const StreamParams* FindFirstStreamParamsByCname(
215 const StreamParamsVec& params_vec,
216 const std::string& cname) {
217 for (StreamParamsVec::const_iterator it = params_vec.begin();
218 it != params_vec.end(); ++it) {
219 if (cname == it->cname)
220 return &*it;
221 }
222 return NULL;
223}
224
225// Generates a new CNAME or the CNAME of an already existing StreamParams
226// if a StreamParams exist for another Stream in streams with sync_label
227// sync_label.
228static bool GenerateCname(const StreamParamsVec& params_vec,
229 const MediaSessionOptions::Streams& streams,
230 const std::string& synch_label,
231 std::string* cname) {
232 ASSERT(cname != NULL);
233 if (!cname)
234 return false;
235
236 // Check if a CNAME exist for any of the other synched streams.
237 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin();
238 stream_it != streams.end() ; ++stream_it) {
239 if (synch_label != stream_it->sync_label)
240 continue;
241
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242 // groupid is empty for StreamParams generated using
243 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000244 const StreamParams* param = GetStreamByIds(params_vec, "", stream_it->id);
245 if (param) {
246 *cname = param->cname;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247 return true;
248 }
249 }
250 // No other stream seems to exist that we should sync with.
251 // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
252 // This string is only used for synchronization, and therefore is opaque.
253 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000254 if (!rtc::CreateRandomString(16, cname)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255 ASSERT(false);
256 return false;
257 }
258 } while (FindFirstStreamParamsByCname(params_vec, *cname));
259
260 return true;
261}
262
263// Generate random SSRC values that are not already present in |params_vec|.
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000264// The generated values are added to |ssrcs|.
265// |num_ssrcs| is the number of the SSRC will be generated.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266static void GenerateSsrcs(const StreamParamsVec& params_vec,
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000267 int num_ssrcs,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200268 std::vector<uint32_t>* ssrcs) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000269 for (int i = 0; i < num_ssrcs; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200270 uint32_t candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000271 do {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000272 candidate = rtc::CreateRandomNonZeroId();
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000273 } while (GetStreamBySsrc(params_vec, candidate) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
275 ssrcs->push_back(candidate);
276 }
277}
278
279// Returns false if we exhaust the range of SIDs.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200280static bool GenerateSctpSid(const StreamParamsVec& params_vec, uint32_t* sid) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000281 if (params_vec.size() > kMaxSctpSid) {
282 LOG(LS_WARNING) <<
283 "Could not generate an SCTP SID: too many SCTP streams.";
284 return false;
285 }
286 while (true) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200287 uint32_t candidate = rtc::CreateRandomNonZeroId() % kMaxSctpSid;
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000288 if (!GetStreamBySsrc(params_vec, candidate)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 *sid = candidate;
290 return true;
291 }
292 }
293}
294
295static bool GenerateSctpSids(const StreamParamsVec& params_vec,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200296 std::vector<uint32_t>* sids) {
297 uint32_t sid;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000298 if (!GenerateSctpSid(params_vec, &sid)) {
299 LOG(LS_WARNING) << "Could not generated an SCTP SID.";
300 return false;
301 }
302 sids->push_back(sid);
303 return true;
304}
305
306// Finds all StreamParams of all media types and attach them to stream_params.
307static void GetCurrentStreamParams(const SessionDescription* sdesc,
308 StreamParamsVec* stream_params) {
309 if (!sdesc)
310 return;
311
312 const ContentInfos& contents = sdesc->contents();
313 for (ContentInfos::const_iterator content = contents.begin();
314 content != contents.end(); ++content) {
315 if (!IsMediaContent(&*content)) {
316 continue;
317 }
318 const MediaContentDescription* media =
319 static_cast<const MediaContentDescription*>(
320 content->description);
321 const StreamParamsVec& streams = media->streams();
322 for (StreamParamsVec::const_iterator it = streams.begin();
323 it != streams.end(); ++it) {
324 stream_params->push_back(*it);
325 }
326 }
327}
328
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000329// Filters the data codecs for the data channel type.
330void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
331 // Filter RTP codec for SCTP and vice versa.
332 int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId;
333 for (std::vector<DataCodec>::iterator iter = codecs->begin();
334 iter != codecs->end();) {
335 if (iter->id == codec_id) {
336 iter = codecs->erase(iter);
337 } else {
338 ++iter;
339 }
340 }
341}
342
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000343template <typename IdStruct>
344class UsedIds {
345 public:
346 UsedIds(int min_allowed_id, int max_allowed_id)
347 : min_allowed_id_(min_allowed_id),
348 max_allowed_id_(max_allowed_id),
349 next_id_(max_allowed_id) {
350 }
351
352 // Loops through all Id in |ids| and changes its id if it is
353 // already in use by another IdStruct. Call this methods with all Id
354 // in a session description to make sure no duplicate ids exists.
355 // Note that typename Id must be a type of IdStruct.
356 template <typename Id>
357 void FindAndSetIdUsed(std::vector<Id>* ids) {
358 for (typename std::vector<Id>::iterator it = ids->begin();
359 it != ids->end(); ++it) {
360 FindAndSetIdUsed(&*it);
361 }
362 }
363
364 // Finds and sets an unused id if the |idstruct| id is already in use.
365 void FindAndSetIdUsed(IdStruct* idstruct) {
366 const int original_id = idstruct->id;
367 int new_id = idstruct->id;
368
369 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
370 // If the original id is not in range - this is an id that can't be
371 // dynamically changed.
372 return;
373 }
374
375 if (IsIdUsed(original_id)) {
376 new_id = FindUnusedId();
377 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
378 << " to " << new_id;
379 idstruct->id = new_id;
380 }
381 SetIdUsed(new_id);
382 }
383
384 private:
385 // Returns the first unused id in reverse order.
386 // This hopefully reduce the risk of more collisions. We want to change the
387 // default ids as little as possible.
388 int FindUnusedId() {
389 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
390 --next_id_;
391 }
392 ASSERT(next_id_ >= min_allowed_id_);
393 return next_id_;
394 }
395
396 bool IsIdUsed(int new_id) {
397 return id_set_.find(new_id) != id_set_.end();
398 }
399
400 void SetIdUsed(int new_id) {
401 id_set_.insert(new_id);
402 }
403
404 const int min_allowed_id_;
405 const int max_allowed_id_;
406 int next_id_;
407 std::set<int> id_set_;
408};
409
410// Helper class used for finding duplicate RTP payload types among audio, video
411// and data codecs. When bundle is used the payload types may not collide.
412class UsedPayloadTypes : public UsedIds<Codec> {
413 public:
414 UsedPayloadTypes()
415 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
416 }
417
418
419 private:
420 static const int kDynamicPayloadTypeMin = 96;
421 static const int kDynamicPayloadTypeMax = 127;
422};
423
424// Helper class used for finding duplicate RTP Header extension ids among
425// audio and video extensions.
426class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> {
427 public:
428 UsedRtpHeaderExtensionIds()
429 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) {
430 }
431
432 private:
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000433 // Min and Max local identifier for one-byte header extensions, per RFC5285.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000434 static const int kLocalIdMin = 1;
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000435 static const int kLocalIdMax = 14;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436};
437
438static bool IsSctp(const MediaContentDescription* desc) {
439 return ((desc->protocol() == kMediaProtocolSctp) ||
440 (desc->protocol() == kMediaProtocolDtlsSctp));
441}
442
443// Adds a StreamParams for each Stream in Streams with media type
444// media_type to content_description.
445// |current_params| - All currently known StreamParams of any media type.
446template <class C>
447static bool AddStreamParams(
448 MediaType media_type,
449 const MediaSessionOptions::Streams& streams,
450 StreamParamsVec* current_streams,
451 MediaContentDescriptionImpl<C>* content_description,
452 const bool add_legacy_stream) {
Noah Richards2e7a0982015-05-18 14:02:54 -0700453 const bool include_rtx_streams =
454 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000455
456 if (streams.empty() && add_legacy_stream) {
457 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200458 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000459 if (IsSctp(content_description)) {
460 GenerateSctpSids(*current_streams, &ssrcs);
461 } else {
Noah Richards2e7a0982015-05-18 14:02:54 -0700462 int num_ssrcs = include_rtx_streams ? 2 : 1;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000463 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700465 if (include_rtx_streams) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
467 content_description->set_multistream(true);
468 } else {
469 content_description->AddLegacyStream(ssrcs[0]);
470 }
471 return true;
472 }
473
474 MediaSessionOptions::Streams::const_iterator stream_it;
475 for (stream_it = streams.begin();
476 stream_it != streams.end(); ++stream_it) {
477 if (stream_it->type != media_type)
478 continue; // Wrong media type.
479
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000480 const StreamParams* param =
481 GetStreamByIds(*current_streams, "", stream_it->id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 // groupid is empty for StreamParams generated using
483 // MediaSessionDescriptionFactory.
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000484 if (!param) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000485 // This is a new stream.
486 // Get a CNAME. Either new or same as one of the other synched streams.
487 std::string cname;
488 if (!GenerateCname(*current_streams, streams, stream_it->sync_label,
489 &cname)) {
490 return false;
491 }
492
Peter Boström0c4e06b2015-10-07 12:23:21 +0200493 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000494 if (IsSctp(content_description)) {
495 GenerateSctpSids(*current_streams, &ssrcs);
496 } else {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000497 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 }
499 StreamParams stream_param;
500 stream_param.id = stream_it->id;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000501 // Add the generated ssrc.
502 for (size_t i = 0; i < ssrcs.size(); ++i) {
503 stream_param.ssrcs.push_back(ssrcs[i]);
504 }
505 if (stream_it->num_sim_layers > 1) {
506 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
507 stream_param.ssrc_groups.push_back(group);
508 }
Noah Richards2e7a0982015-05-18 14:02:54 -0700509 // Generate extra ssrcs for include_rtx_streams case.
510 if (include_rtx_streams) {
511 // Generate an RTX ssrc for every ssrc in the group.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200512 std::vector<uint32_t> rtx_ssrcs;
Noah Richards2e7a0982015-05-18 14:02:54 -0700513 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
514 &rtx_ssrcs);
515 for (size_t i = 0; i < ssrcs.size(); ++i) {
516 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
517 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 content_description->set_multistream(true);
519 }
520 stream_param.cname = cname;
521 stream_param.sync_label = stream_it->sync_label;
522 content_description->AddStream(stream_param);
523
524 // Store the new StreamParams in current_streams.
525 // This is necessary so that we can use the CNAME for other media types.
526 current_streams->push_back(stream_param);
527 } else {
tommi@webrtc.org586f2ed2015-01-22 23:00:41 +0000528 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000529 }
530 }
531 return true;
532}
533
534// Updates the transport infos of the |sdesc| according to the given
535// |bundle_group|. The transport infos of the content names within the
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800536// |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
537// first content within the |bundle_group|.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000538static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
539 SessionDescription* sdesc) {
540 // The bundle should not be empty.
541 if (!sdesc || !bundle_group.FirstContentName()) {
542 return false;
543 }
544
545 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 02:46:32 -0700546 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000547 const TransportInfo* selected_transport_info =
548 sdesc->GetTransportInfoByName(selected_content_name);
549 if (!selected_transport_info) {
550 return false;
551 }
552
553 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 02:46:32 -0700554 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000555 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 02:46:32 -0700556 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000557 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800558 ConnectionRole selected_connection_role =
559 selected_transport_info->description.connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000560 for (TransportInfos::iterator it =
561 sdesc->transport_infos().begin();
562 it != sdesc->transport_infos().end(); ++it) {
563 if (bundle_group.HasContentName(it->content_name) &&
564 it->content_name != selected_content_name) {
565 it->description.ice_ufrag = selected_ufrag;
566 it->description.ice_pwd = selected_pwd;
Taylor Brandstetterf475d362016-01-08 15:35:57 -0800567 it->description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568 }
569 }
570 return true;
571}
572
573// Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
574// sets it to |cryptos|.
575static bool GetCryptosByName(const SessionDescription* sdesc,
576 const std::string& content_name,
577 CryptoParamsVec* cryptos) {
578 if (!sdesc || !cryptos) {
579 return false;
580 }
581
582 const ContentInfo* content = sdesc->GetContentByName(content_name);
583 if (!IsMediaContent(content) || !content->description) {
584 return false;
585 }
586
587 const MediaContentDescription* media_desc =
588 static_cast<const MediaContentDescription*>(content->description);
589 *cryptos = media_desc->cryptos();
590 return true;
591}
592
593// Predicate function used by the remove_if.
594// Returns true if the |crypto|'s cipher_suite is not found in |filter|.
595static bool CryptoNotFound(const CryptoParams crypto,
596 const CryptoParamsVec* filter) {
597 if (filter == NULL) {
598 return true;
599 }
600 for (CryptoParamsVec::const_iterator it = filter->begin();
601 it != filter->end(); ++it) {
602 if (it->cipher_suite == crypto.cipher_suite) {
603 return false;
604 }
605 }
606 return true;
607}
608
609// Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
610// which are not available in |filter|.
611static void PruneCryptos(const CryptoParamsVec& filter,
612 CryptoParamsVec* target_cryptos) {
613 if (!target_cryptos) {
614 return;
615 }
616 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
617 target_cryptos->end(),
618 bind2nd(ptr_fun(CryptoNotFound),
619 &filter)),
620 target_cryptos->end());
621}
622
deadbeefb5cb19b2015-11-23 16:39:12 -0800623static bool IsRtpProtocol(const std::string& protocol) {
624 return protocol.empty() ||
625 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
626}
627
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000628static bool IsRtpContent(SessionDescription* sdesc,
629 const std::string& content_name) {
630 bool is_rtp = false;
631 ContentInfo* content = sdesc->GetContentByName(content_name);
632 if (IsMediaContent(content)) {
633 MediaContentDescription* media_desc =
634 static_cast<MediaContentDescription*>(content->description);
635 if (!media_desc) {
636 return false;
637 }
deadbeefb5cb19b2015-11-23 16:39:12 -0800638 is_rtp = IsRtpProtocol(media_desc->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 }
640 return is_rtp;
641}
642
643// Updates the crypto parameters of the |sdesc| according to the given
644// |bundle_group|. The crypto parameters of all the contents within the
645// |bundle_group| should be updated to use the common subset of the
646// available cryptos.
647static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
648 SessionDescription* sdesc) {
649 // The bundle should not be empty.
650 if (!sdesc || !bundle_group.FirstContentName()) {
651 return false;
652 }
653
wu@webrtc.org78187522013-10-07 23:32:02 +0000654 bool common_cryptos_needed = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 // Get the common cryptos.
656 const ContentNames& content_names = bundle_group.content_names();
657 CryptoParamsVec common_cryptos;
658 for (ContentNames::const_iterator it = content_names.begin();
659 it != content_names.end(); ++it) {
660 if (!IsRtpContent(sdesc, *it)) {
661 continue;
662 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000663 // The common cryptos are needed if any of the content does not have DTLS
664 // enabled.
665 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
666 common_cryptos_needed = true;
667 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000668 if (it == content_names.begin()) {
669 // Initial the common_cryptos with the first content in the bundle group.
670 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
671 return false;
672 }
673 if (common_cryptos.empty()) {
674 // If there's no crypto params, we should just return.
675 return true;
676 }
677 } else {
678 CryptoParamsVec cryptos;
679 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
680 return false;
681 }
682 PruneCryptos(cryptos, &common_cryptos);
683 }
684 }
685
wu@webrtc.org78187522013-10-07 23:32:02 +0000686 if (common_cryptos.empty() && common_cryptos_needed) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000687 return false;
688 }
689
690 // Update to use the common cryptos.
691 for (ContentNames::const_iterator it = content_names.begin();
692 it != content_names.end(); ++it) {
693 if (!IsRtpContent(sdesc, *it)) {
694 continue;
695 }
696 ContentInfo* content = sdesc->GetContentByName(*it);
697 if (IsMediaContent(content)) {
698 MediaContentDescription* media_desc =
699 static_cast<MediaContentDescription*>(content->description);
700 if (!media_desc) {
701 return false;
702 }
703 media_desc->set_cryptos(common_cryptos);
704 }
705 }
706 return true;
707}
708
709template <class C>
710static bool ContainsRtxCodec(const std::vector<C>& codecs) {
711 typename std::vector<C>::const_iterator it;
712 for (it = codecs.begin(); it != codecs.end(); ++it) {
713 if (IsRtxCodec(*it)) {
714 return true;
715 }
716 }
717 return false;
718}
719
720template <class C>
721static bool IsRtxCodec(const C& codec) {
722 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
723}
724
deadbeef0ed85b22016-02-23 17:24:52 -0800725static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
726 const std::string& content_name) {
727 auto it = options.transport_options.find(content_name);
728 if (it == options.transport_options.end()) {
729 return TransportOptions();
730 }
731 return it->second;
732}
733
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734// Create a media content to be offered in a session-initiate,
735// according to the given options.rtcp_mux, options.is_muc,
736// options.streams, codecs, secure_transport, crypto, and streams. If we don't
737// currently have crypto (in current_cryptos) and it is enabled (in
738// secure_policy), crypto is created (according to crypto_suites). If
739// add_legacy_stream is true, and current_streams is empty, a legacy
740// stream is created. The created content is added to the offer.
741template <class C>
742static bool CreateMediaContentOffer(
743 const MediaSessionOptions& options,
744 const std::vector<C>& codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000745 const SecurePolicy& secure_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000746 const CryptoParamsVec* current_cryptos,
747 const std::vector<std::string>& crypto_suites,
748 const RtpHeaderExtensions& rtp_extensions,
749 bool add_legacy_stream,
750 StreamParamsVec* current_streams,
751 MediaContentDescriptionImpl<C>* offer) {
752 offer->AddCodecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000753
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000754 if (secure_policy == SEC_REQUIRED) {
755 offer->set_crypto_required(CT_SDES);
756 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757 offer->set_rtcp_mux(options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700758 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
759 offer->set_rtcp_reduced_size(true);
760 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000761 offer->set_multistream(options.is_muc);
762 offer->set_rtp_header_extensions(rtp_extensions);
763
764 if (!AddStreamParams(
765 offer->type(), options.streams, current_streams,
766 offer, add_legacy_stream)) {
767 return false;
768 }
769
770#ifdef HAVE_SRTP
771 if (secure_policy != SEC_DISABLED) {
772 if (current_cryptos) {
773 AddMediaCryptos(*current_cryptos, offer);
774 }
775 if (offer->cryptos().empty()) {
776 if (!CreateMediaCryptos(crypto_suites, offer)) {
777 return false;
778 }
779 }
780 }
781#endif
782
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000783 if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000784 return false;
785 }
786 return true;
787}
788
789template <class C>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +0000790static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
791 const std::string& codec1_id_str,
792 const std::vector<C>& codecs2,
793 const std::string& codec2_id_str) {
794 int codec1_id;
795 int codec2_id;
796 C codec1;
797 C codec2;
798 if (!rtc::FromString(codec1_id_str, &codec1_id) ||
799 !rtc::FromString(codec2_id_str, &codec2_id) ||
800 !FindCodecById(codecs1, codec1_id, &codec1) ||
801 !FindCodecById(codecs2, codec2_id, &codec2)) {
802 return false;
803 }
804 return codec1.Matches(codec2);
805}
806
807template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000808static void NegotiateCodecs(const std::vector<C>& local_codecs,
809 const std::vector<C>& offered_codecs,
810 std::vector<C>* negotiated_codecs) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800811 for (const C& ours : local_codecs) {
812 C theirs;
deadbeef67cf2c12016-04-13 10:07:16 -0700813 // Note that we intentionally only find one matching codec for each of our
814 // local codecs, in case the remote offer contains duplicate codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800815 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
816 C negotiated = ours;
817 negotiated.IntersectFeedbackParams(theirs);
818 if (IsRtxCodec(negotiated)) {
819 std::string offered_apt_value;
820 theirs.GetParam(kCodecParamAssociatedPayloadType, &offered_apt_value);
821 // FindMatchingCodec shouldn't return something with no apt value.
822 RTC_DCHECK(!offered_apt_value.empty());
823 negotiated.SetParam(kCodecParamAssociatedPayloadType,
824 offered_apt_value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 }
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800826 negotiated.id = theirs.id;
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800827 negotiated_codecs->push_back(negotiated);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000828 }
829 }
deadbeef67cf2c12016-04-13 10:07:16 -0700830 // RFC3264: Although the answerer MAY list the formats in their desired
831 // order of preference, it is RECOMMENDED that unless there is a
832 // specific reason, the answerer list formats in the same relative order
833 // they were present in the offer.
834 std::unordered_map<int, int> payload_type_preferences;
835 int preference = static_cast<int>(offered_codecs.size() + 1);
836 for (const C& codec : offered_codecs) {
837 payload_type_preferences[codec.id] = preference--;
838 }
839 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
840 [&payload_type_preferences](const C& a, const C& b) {
841 return payload_type_preferences[a.id] >
842 payload_type_preferences[b.id];
843 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000844}
845
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800846// Finds a codec in |codecs2| that matches |codec_to_match|, which is
847// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
848// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000849template <class C>
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800850static bool FindMatchingCodec(const std::vector<C>& codecs1,
851 const std::vector<C>& codecs2,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 const C& codec_to_match,
853 C* found_codec) {
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800854 for (const C& potential_match : codecs2) {
855 if (potential_match.Matches(codec_to_match)) {
856 if (IsRtxCodec(codec_to_match)) {
857 std::string apt_value_1;
858 std::string apt_value_2;
859 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
860 &apt_value_1) ||
861 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
862 &apt_value_2)) {
863 LOG(LS_WARNING) << "RTX missing associated payload type.";
864 continue;
865 }
866 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
867 apt_value_2)) {
868 continue;
869 }
870 }
871 if (found_codec) {
872 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000873 }
874 return true;
875 }
876 }
877 return false;
878}
879
880// Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
881// already exist in |offered_codecs| and ensure the payload types don't
882// collide.
883template <class C>
884static void FindCodecsToOffer(
885 const std::vector<C>& reference_codecs,
886 std::vector<C>* offered_codecs,
887 UsedPayloadTypes* used_pltypes) {
888
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 // Add all new codecs that are not RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800890 for (const C& reference_codec : reference_codecs) {
891 if (!IsRtxCodec(reference_codec) &&
892 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
893 reference_codec, nullptr)) {
894 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000895 used_pltypes->FindAndSetIdUsed(&codec);
896 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000897 }
898 }
899
900 // Add all new RTX codecs.
Taylor Brandstetter6ec641b2016-03-04 16:47:56 -0800901 for (const C& reference_codec : reference_codecs) {
902 if (IsRtxCodec(reference_codec) &&
903 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
904 reference_codec, nullptr)) {
905 C rtx_codec = reference_codec;
906
907 std::string associated_pt_str;
908 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
909 &associated_pt_str)) {
910 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
911 << " is missing an associated payload type.";
912 continue;
913 }
914
915 int associated_pt;
916 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
917 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
918 << " of RTX codec " << rtx_codec.name
919 << " to an integer.";
920 continue;
921 }
922
923 // Find the associated reference codec for the reference RTX codec.
924 C associated_codec;
925 if (!FindCodecById(reference_codecs, associated_pt, &associated_codec)) {
926 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
927 << associated_pt << " for RTX codec " << rtx_codec.name
928 << ".";
929 continue;
930 }
931
932 // Find a codec in the offered list that matches the reference codec.
933 // Its payload type may be different than the reference codec.
934 C matching_codec;
935 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
936 associated_codec, &matching_codec)) {
937 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec.name
938 << " codec.";
939 continue;
940 }
941
942 rtx_codec.params[kCodecParamAssociatedPayloadType] =
943 rtc::ToString(matching_codec.id);
944 used_pltypes->FindAndSetIdUsed(&rtx_codec);
945 offered_codecs->push_back(rtx_codec);
946 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000947 }
948}
949
950
951static bool FindByUri(const RtpHeaderExtensions& extensions,
952 const RtpHeaderExtension& ext_to_match,
953 RtpHeaderExtension* found_extension) {
954 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
955 it != extensions.end(); ++it) {
956 // We assume that all URIs are given in a canonical format.
957 if (it->uri == ext_to_match.uri) {
958 if (found_extension != NULL) {
henrike@webrtc.org79047f92014-03-06 23:46:59 +0000959 *found_extension = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000960 }
961 return true;
962 }
963 }
964 return false;
965}
966
deadbeefa5b273a2015-08-20 17:30:13 -0700967// Iterates through |offered_extensions|, adding each one to |all_extensions|
968// and |used_ids|, and resolving ID conflicts. If an offered extension has the
969// same URI as one in |all_extensions|, it will re-use the same ID and won't be
970// treated as a conflict.
971static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
972 RtpHeaderExtensions* all_extensions,
973 UsedRtpHeaderExtensionIds* used_ids) {
974 for (auto& extension : *offered_extensions) {
975 RtpHeaderExtension existing;
976 if (FindByUri(*all_extensions, extension, &existing)) {
977 extension.id = existing.id;
978 } else {
979 used_ids->FindAndSetIdUsed(&extension);
980 all_extensions->push_back(extension);
981 }
982 }
983}
984
985// Adds |reference_extensions| to |offered_extensions|, while updating
986// |all_extensions| and |used_ids|.
987static void FindRtpHdrExtsToOffer(
988 const RtpHeaderExtensions& reference_extensions,
989 RtpHeaderExtensions* offered_extensions,
990 RtpHeaderExtensions* all_extensions,
991 UsedRtpHeaderExtensionIds* used_ids) {
992 for (auto reference_extension : reference_extensions) {
993 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
994 RtpHeaderExtension existing;
995 if (FindByUri(*all_extensions, reference_extension, &existing)) {
996 offered_extensions->push_back(existing);
997 } else {
998 used_ids->FindAndSetIdUsed(&reference_extension);
999 all_extensions->push_back(reference_extension);
1000 offered_extensions->push_back(reference_extension);
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001001 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001002 }
1003 }
1004}
1005
1006static void NegotiateRtpHeaderExtensions(
1007 const RtpHeaderExtensions& local_extensions,
1008 const RtpHeaderExtensions& offered_extensions,
1009 RtpHeaderExtensions* negotiated_extenstions) {
1010 RtpHeaderExtensions::const_iterator ours;
1011 for (ours = local_extensions.begin();
1012 ours != local_extensions.end(); ++ours) {
1013 RtpHeaderExtension theirs;
1014 if (FindByUri(offered_extensions, *ours, &theirs)) {
1015 // We respond with their RTP header extension id.
1016 negotiated_extenstions->push_back(theirs);
1017 }
1018 }
1019}
1020
1021static void StripCNCodecs(AudioCodecs* audio_codecs) {
1022 AudioCodecs::iterator iter = audio_codecs->begin();
1023 while (iter != audio_codecs->end()) {
1024 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
1025 iter = audio_codecs->erase(iter);
1026 } else {
1027 ++iter;
1028 }
1029 }
1030}
1031
1032// Create a media content to be answered in a session-accept,
1033// according to the given options.rtcp_mux, options.streams, codecs,
1034// crypto, and streams. If we don't currently have crypto (in
1035// current_cryptos) and it is enabled (in secure_policy), crypto is
1036// created (according to crypto_suites). If add_legacy_stream is
1037// true, and current_streams is empty, a legacy stream is created.
1038// The codecs, rtcp_mux, and crypto are all negotiated with the offer
1039// from the incoming session-initiate. If the negotiation fails, this
1040// method returns false. The created content is added to the offer.
1041template <class C>
1042static bool CreateMediaContentAnswer(
1043 const MediaContentDescriptionImpl<C>* offer,
1044 const MediaSessionOptions& options,
1045 const std::vector<C>& local_codecs,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001046 const SecurePolicy& sdes_policy,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001047 const CryptoParamsVec* current_cryptos,
1048 const RtpHeaderExtensions& local_rtp_extenstions,
1049 StreamParamsVec* current_streams,
1050 bool add_legacy_stream,
1051 bool bundle_enabled,
1052 MediaContentDescriptionImpl<C>* answer) {
1053 std::vector<C> negotiated_codecs;
1054 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1055 answer->AddCodecs(negotiated_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001056 answer->set_protocol(offer->protocol());
1057 RtpHeaderExtensions negotiated_rtp_extensions;
1058 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1059 offer->rtp_header_extensions(),
1060 &negotiated_rtp_extensions);
1061 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1062
1063 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -07001064 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1065 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1066 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001067
1068 if (sdes_policy != SEC_DISABLED) {
1069 CryptoParams crypto;
1070 if (SelectCrypto(offer, bundle_enabled, &crypto)) {
1071 if (current_cryptos) {
1072 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1073 }
1074 answer->AddCrypto(crypto);
1075 }
1076 }
1077
1078 if (answer->cryptos().empty() &&
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001079 (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001080 return false;
1081 }
1082
1083 if (!AddStreamParams(
1084 answer->type(), options.streams, current_streams,
1085 answer, add_legacy_stream)) {
1086 return false; // Something went seriously wrong.
1087 }
1088
1089 // Make sure the answer media content direction is per default set as
1090 // described in RFC3264 section 6.1.
1091 switch (offer->direction()) {
1092 case MD_INACTIVE:
1093 answer->set_direction(MD_INACTIVE);
1094 break;
1095 case MD_SENDONLY:
1096 answer->set_direction(MD_RECVONLY);
1097 break;
1098 case MD_RECVONLY:
deadbeefb5cb19b2015-11-23 16:39:12 -08001099 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1100 answer->streams().empty()
1101 ? MD_INACTIVE
1102 : MD_SENDONLY);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001103 break;
1104 case MD_SENDRECV:
deadbeefb5cb19b2015-11-23 16:39:12 -08001105 answer->set_direction(IsRtpProtocol(answer->protocol()) &&
1106 answer->streams().empty()
1107 ? MD_RECVONLY
1108 : MD_SENDRECV);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001109 break;
1110 default:
deadbeefc80741f2015-10-22 13:14:45 -07001111 RTC_DCHECK(false && "MediaContentDescription has unexpected direction.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001112 break;
1113 }
1114
1115 return true;
1116}
1117
1118static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001119 const std::string& protocol,
1120 bool secure_transport) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001121 // Data channels can have a protocol of SCTP or SCTP/DTLS.
1122 if (type == MEDIA_TYPE_DATA &&
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001123 ((protocol == kMediaProtocolSctp && !secure_transport)||
1124 (protocol == kMediaProtocolDtlsSctp && secure_transport))) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001125 return true;
1126 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:59 +00001127
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001128 // Since not all applications serialize and deserialize the media protocol,
1129 // we will have to accept |protocol| to be empty.
zhihuangd713e862016-04-13 10:48:28 -07001130 return protocol == kMediaProtocolAvpf || protocol.empty() ||
1131 protocol == kMediaProtocolSavpf ||
1132 (protocol == kMediaProtocolDtlsSavpf && secure_transport);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001133}
1134
1135static void SetMediaProtocol(bool secure_transport,
1136 MediaContentDescription* desc) {
deadbeeff3938292015-07-15 12:20:53 -07001137 if (!desc->cryptos().empty())
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001138 desc->set_protocol(kMediaProtocolSavpf);
deadbeeff3938292015-07-15 12:20:53 -07001139 else if (secure_transport)
1140 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141 else
1142 desc->set_protocol(kMediaProtocolAvpf);
1143}
1144
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001145// Gets the TransportInfo of the given |content_name| from the
1146// |current_description|. If doesn't exist, returns a new one.
1147static const TransportDescription* GetTransportDescription(
1148 const std::string& content_name,
1149 const SessionDescription* current_description) {
1150 const TransportDescription* desc = NULL;
1151 if (current_description) {
1152 const TransportInfo* info =
1153 current_description->GetTransportInfoByName(content_name);
1154 if (info) {
1155 desc = &info->description;
1156 }
1157 }
1158 return desc;
1159}
1160
1161// Gets the current DTLS state from the transport description.
1162static bool IsDtlsActive(
1163 const std::string& content_name,
1164 const SessionDescription* current_description) {
1165 if (!current_description)
1166 return false;
1167
1168 const ContentInfo* content =
1169 current_description->GetContentByName(content_name);
1170 if (!content)
1171 return false;
1172
1173 const TransportDescription* current_tdesc =
1174 GetTransportDescription(content_name, current_description);
1175 if (!current_tdesc)
1176 return false;
1177
1178 return current_tdesc->secure();
1179}
1180
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001181std::string MediaTypeToString(MediaType type) {
1182 std::string type_str;
1183 switch (type) {
1184 case MEDIA_TYPE_AUDIO:
1185 type_str = "audio";
1186 break;
1187 case MEDIA_TYPE_VIDEO:
1188 type_str = "video";
1189 break;
1190 case MEDIA_TYPE_DATA:
1191 type_str = "data";
1192 break;
1193 default:
1194 ASSERT(false);
1195 break;
1196 }
1197 return type_str;
1198}
1199
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001200void MediaSessionOptions::AddSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201 const std::string& id,
1202 const std::string& sync_label) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001203 AddSendStreamInternal(type, id, sync_label, 1);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001204}
1205
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001206void MediaSessionOptions::AddSendVideoStream(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001207 const std::string& id,
1208 const std::string& sync_label,
1209 int num_sim_layers) {
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001210 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001211}
1212
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001213void MediaSessionOptions::AddSendStreamInternal(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001214 MediaType type,
1215 const std::string& id,
1216 const std::string& sync_label,
1217 int num_sim_layers) {
1218 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001219
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001220 // If we haven't already set the data_channel_type, and we add a
1221 // stream, we assume it's an RTP data stream.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001222 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001223 data_channel_type = DCT_RTP;
1224}
1225
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001226void MediaSessionOptions::RemoveSendStream(MediaType type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001227 const std::string& id) {
1228 Streams::iterator stream_it = streams.begin();
1229 for (; stream_it != streams.end(); ++stream_it) {
1230 if (stream_it->type == type && stream_it->id == id) {
1231 streams.erase(stream_it);
1232 return;
1233 }
1234 }
1235 ASSERT(false);
1236}
1237
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001238bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1239 Streams::const_iterator stream_it = streams.begin();
1240 for (; stream_it != streams.end(); ++stream_it) {
1241 if (stream_it->type == type) {
1242 return true;
1243 }
1244 }
1245 return false;
1246}
1247
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001248MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1249 const TransportDescriptionFactory* transport_desc_factory)
1250 : secure_(SEC_DISABLED),
1251 add_legacy_(true),
1252 transport_desc_factory_(transport_desc_factory) {
1253}
1254
1255MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1256 ChannelManager* channel_manager,
1257 const TransportDescriptionFactory* transport_desc_factory)
1258 : secure_(SEC_DISABLED),
1259 add_legacy_(true),
1260 transport_desc_factory_(transport_desc_factory) {
1261 channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
1262 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1263 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1264 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1265 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1266}
1267
1268SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1269 const MediaSessionOptions& options,
1270 const SessionDescription* current_description) const {
kwiberg31022942016-03-11 14:18:21 -08001271 std::unique_ptr<SessionDescription> offer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001272
1273 StreamParamsVec current_streams;
1274 GetCurrentStreamParams(current_description, &current_streams);
1275
1276 AudioCodecs audio_codecs;
1277 VideoCodecs video_codecs;
1278 DataCodecs data_codecs;
1279 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs,
1280 &data_codecs);
1281
1282 if (!options.vad_enabled) {
1283 // If application doesn't want CN codecs in offer.
1284 StripCNCodecs(&audio_codecs);
1285 }
1286
1287 RtpHeaderExtensions audio_rtp_extensions;
1288 RtpHeaderExtensions video_rtp_extensions;
1289 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1290 &video_rtp_extensions);
1291
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001292 bool audio_added = false;
1293 bool video_added = false;
1294 bool data_added = false;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001295
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001296 // Iterate through the contents of |current_description| to maintain the order
1297 // of the m-lines in the new offer.
1298 if (current_description) {
1299 ContentInfos::const_iterator it = current_description->contents().begin();
1300 for (; it != current_description->contents().end(); ++it) {
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001301 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001302 if (!AddAudioContentForOffer(options, current_description,
1303 audio_rtp_extensions, audio_codecs,
1304 &current_streams, offer.get())) {
1305 return NULL;
1306 }
1307 audio_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001308 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001309 if (!AddVideoContentForOffer(options, current_description,
1310 video_rtp_extensions, video_codecs,
1311 &current_streams, offer.get())) {
1312 return NULL;
1313 }
1314 video_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001315 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
tommi@webrtc.orgf15dee62014-10-27 22:15:04 +00001316 MediaSessionOptions options_copy(options);
1317 if (IsSctp(static_cast<const MediaContentDescription*>(
1318 it->description))) {
1319 options_copy.data_channel_type = DCT_SCTP;
1320 }
1321 if (!AddDataContentForOffer(options_copy, current_description,
1322 &data_codecs, &current_streams,
1323 offer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001324 return NULL;
1325 }
1326 data_added = true;
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001327 } else {
1328 ASSERT(false);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001329 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001330 }
1331 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001332
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001333 // Append contents that are not in |current_description|.
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001334 if (!audio_added && options.has_audio() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001335 !AddAudioContentForOffer(options, current_description,
1336 audio_rtp_extensions, audio_codecs,
1337 &current_streams, offer.get())) {
1338 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001340 if (!video_added && options.has_video() &&
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001341 !AddVideoContentForOffer(options, current_description,
1342 video_rtp_extensions, video_codecs,
1343 &current_streams, offer.get())) {
1344 return NULL;
1345 }
1346 if (!data_added && options.has_data() &&
1347 !AddDataContentForOffer(options, current_description, &data_codecs,
1348 &current_streams, offer.get())) {
1349 return NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001350 }
1351
1352 // Bundle the contents together, if we've been asked to do so, and update any
1353 // parameters that need to be tweaked for BUNDLE.
1354 if (options.bundle_enabled) {
1355 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1356 for (ContentInfos::const_iterator content = offer->contents().begin();
1357 content != offer->contents().end(); ++content) {
1358 offer_bundle.AddContentName(content->name);
1359 }
1360 offer->AddGroup(offer_bundle);
1361 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1362 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1363 return NULL;
1364 }
1365 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1366 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1367 return NULL;
1368 }
1369 }
1370
1371 return offer.release();
1372}
1373
1374SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1375 const SessionDescription* offer, const MediaSessionOptions& options,
1376 const SessionDescription* current_description) const {
1377 // The answer contains the intersection of the codecs in the offer with the
deadbeef67cf2c12016-04-13 10:07:16 -07001378 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1379 // from the offer in the answer.
kwiberg31022942016-03-11 14:18:21 -08001380 std::unique_ptr<SessionDescription> answer(new SessionDescription());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001381
1382 StreamParamsVec current_streams;
1383 GetCurrentStreamParams(current_description, &current_streams);
1384
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001385 if (offer) {
1386 ContentInfos::const_iterator it = offer->contents().begin();
1387 for (; it != offer->contents().end(); ++it) {
1388 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1389 if (!AddAudioContentForAnswer(offer, options, current_description,
1390 &current_streams, answer.get())) {
1391 return NULL;
1392 }
1393 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1394 if (!AddVideoContentForAnswer(offer, options, current_description,
1395 &current_streams, answer.get())) {
1396 return NULL;
1397 }
1398 } else {
1399 ASSERT(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1400 if (!AddDataContentForAnswer(offer, options, current_description,
1401 &current_streams, answer.get())) {
1402 return NULL;
1403 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001404 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001405 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001406 }
1407
1408 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1409 // group in the answer with the appropriate content names.
1410 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1411 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1412 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1413 for (ContentInfos::const_iterator content = answer->contents().begin();
1414 content != answer->contents().end(); ++content) {
1415 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1416 answer_bundle.AddContentName(content->name);
1417 }
1418 }
1419 if (answer_bundle.FirstContentName()) {
1420 answer->AddGroup(answer_bundle);
1421
1422 // Share the same ICE credentials and crypto params across all contents,
1423 // as BUNDLE requires.
1424 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1425 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1426 return NULL;
1427 }
1428
1429 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1430 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1431 return NULL;
1432 }
1433 }
1434 }
1435
1436 return answer.release();
1437}
1438
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001439void MediaSessionDescriptionFactory::GetCodecsToOffer(
1440 const SessionDescription* current_description,
1441 AudioCodecs* audio_codecs,
1442 VideoCodecs* video_codecs,
1443 DataCodecs* data_codecs) const {
1444 UsedPayloadTypes used_pltypes;
1445 audio_codecs->clear();
1446 video_codecs->clear();
1447 data_codecs->clear();
1448
1449
1450 // First - get all codecs from the current description if the media type
1451 // is used.
1452 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1453 // type is added.
1454 if (current_description) {
1455 const AudioContentDescription* audio =
1456 GetFirstAudioContentDescription(current_description);
1457 if (audio) {
1458 *audio_codecs = audio->codecs();
1459 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1460 }
1461 const VideoContentDescription* video =
1462 GetFirstVideoContentDescription(current_description);
1463 if (video) {
1464 *video_codecs = video->codecs();
1465 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1466 }
1467 const DataContentDescription* data =
1468 GetFirstDataContentDescription(current_description);
1469 if (data) {
1470 *data_codecs = data->codecs();
1471 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1472 }
1473 }
1474
1475 // Add our codecs that are not in |current_description|.
1476 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes);
1477 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
1478 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
1479}
1480
1481void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1482 const SessionDescription* current_description,
1483 RtpHeaderExtensions* audio_extensions,
1484 RtpHeaderExtensions* video_extensions) const {
henrike@webrtc.org79047f92014-03-06 23:46:59 +00001485 // All header extensions allocated from the same range to avoid potential
1486 // issues when using BUNDLE.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001487 UsedRtpHeaderExtensionIds used_ids;
deadbeefa5b273a2015-08-20 17:30:13 -07001488 RtpHeaderExtensions all_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001489 audio_extensions->clear();
1490 video_extensions->clear();
1491
1492 // First - get all extensions from the current description if the media type
1493 // is used.
1494 // Add them to |used_ids| so the local ids are not reused if a new media
1495 // type is added.
1496 if (current_description) {
1497 const AudioContentDescription* audio =
1498 GetFirstAudioContentDescription(current_description);
1499 if (audio) {
1500 *audio_extensions = audio->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001501 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001502 }
1503 const VideoContentDescription* video =
1504 GetFirstVideoContentDescription(current_description);
1505 if (video) {
1506 *video_extensions = video->rtp_header_extensions();
deadbeefa5b273a2015-08-20 17:30:13 -07001507 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001508 }
1509 }
1510
1511 // Add our default RTP header extensions that are not in
1512 // |current_description|.
deadbeefa5b273a2015-08-20 17:30:13 -07001513 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1514 &all_extensions, &used_ids);
1515 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1516 &all_extensions, &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001517}
1518
1519bool MediaSessionDescriptionFactory::AddTransportOffer(
1520 const std::string& content_name,
1521 const TransportOptions& transport_options,
1522 const SessionDescription* current_desc,
1523 SessionDescription* offer_desc) const {
1524 if (!transport_desc_factory_)
1525 return false;
1526 const TransportDescription* current_tdesc =
1527 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 14:18:21 -08001528 std::unique_ptr<TransportDescription> new_tdesc(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001529 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1530 bool ret = (new_tdesc.get() != NULL &&
1531 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1532 if (!ret) {
1533 LOG(LS_ERROR)
1534 << "Failed to AddTransportOffer, content name=" << content_name;
1535 }
1536 return ret;
1537}
1538
1539TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1540 const std::string& content_name,
1541 const SessionDescription* offer_desc,
1542 const TransportOptions& transport_options,
1543 const SessionDescription* current_desc) const {
1544 if (!transport_desc_factory_)
1545 return NULL;
1546 const TransportDescription* offer_tdesc =
1547 GetTransportDescription(content_name, offer_desc);
1548 const TransportDescription* current_tdesc =
1549 GetTransportDescription(content_name, current_desc);
1550 return
1551 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1552 current_tdesc);
1553}
1554
1555bool MediaSessionDescriptionFactory::AddTransportAnswer(
1556 const std::string& content_name,
1557 const TransportDescription& transport_desc,
1558 SessionDescription* answer_desc) const {
1559 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1560 transport_desc))) {
1561 LOG(LS_ERROR)
1562 << "Failed to AddTransportAnswer, content name=" << content_name;
1563 return false;
1564 }
1565 return true;
1566}
1567
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001568bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1569 const MediaSessionOptions& options,
1570 const SessionDescription* current_description,
1571 const RtpHeaderExtensions& audio_rtp_extensions,
1572 const AudioCodecs& audio_codecs,
1573 StreamParamsVec* current_streams,
1574 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001575 const ContentInfo* current_audio_content =
1576 GetFirstAudioContent(current_description);
1577 std::string content_name =
1578 current_audio_content ? current_audio_content->name : CN_AUDIO;
1579
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001580 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001581 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1582 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001583
kwiberg31022942016-03-11 14:18:21 -08001584 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001585 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001586 GetSupportedAudioCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001587 if (!CreateMediaContentOffer(
1588 options,
1589 audio_codecs,
1590 sdes_policy,
1591 GetCryptos(GetFirstAudioContentDescription(current_description)),
1592 crypto_suites,
1593 audio_rtp_extensions,
1594 add_legacy_,
1595 current_streams,
1596 audio.get())) {
1597 return false;
1598 }
1599 audio->set_lang(lang_);
1600
1601 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1602 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:15 +00001603
deadbeefc80741f2015-10-22 13:14:45 -07001604 if (!audio->streams().empty()) {
1605 if (options.recv_audio) {
1606 audio->set_direction(MD_SENDRECV);
1607 } else {
1608 audio->set_direction(MD_SENDONLY);
1609 }
1610 } else {
1611 if (options.recv_audio) {
1612 audio->set_direction(MD_RECVONLY);
1613 } else {
1614 audio->set_direction(MD_INACTIVE);
1615 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001616 }
1617
deadbeef44f08192015-12-15 16:20:09 -08001618 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001619 if (!AddTransportOffer(content_name,
1620 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001621 current_description, desc)) {
1622 return false;
1623 }
1624
1625 return true;
1626}
1627
1628bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1629 const MediaSessionOptions& options,
1630 const SessionDescription* current_description,
1631 const RtpHeaderExtensions& video_rtp_extensions,
1632 const VideoCodecs& video_codecs,
1633 StreamParamsVec* current_streams,
1634 SessionDescription* desc) const {
deadbeef44f08192015-12-15 16:20:09 -08001635 const ContentInfo* current_video_content =
1636 GetFirstVideoContent(current_description);
1637 std::string content_name =
1638 current_video_content ? current_video_content->name : CN_VIDEO;
1639
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001640 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001641 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1642 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001643
kwiberg31022942016-03-11 14:18:21 -08001644 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001645 std::vector<std::string> crypto_suites;
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001646 GetSupportedVideoCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001647 if (!CreateMediaContentOffer(
1648 options,
1649 video_codecs,
1650 sdes_policy,
1651 GetCryptos(GetFirstVideoContentDescription(current_description)),
1652 crypto_suites,
1653 video_rtp_extensions,
1654 add_legacy_,
1655 current_streams,
1656 video.get())) {
1657 return false;
1658 }
1659
1660 video->set_bandwidth(options.video_bandwidth);
1661
1662 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1663 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001664
deadbeefc80741f2015-10-22 13:14:45 -07001665 if (!video->streams().empty()) {
1666 if (options.recv_video) {
1667 video->set_direction(MD_SENDRECV);
1668 } else {
1669 video->set_direction(MD_SENDONLY);
1670 }
1671 } else {
1672 if (options.recv_video) {
1673 video->set_direction(MD_RECVONLY);
1674 } else {
1675 video->set_direction(MD_INACTIVE);
1676 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001677 }
1678
deadbeef44f08192015-12-15 16:20:09 -08001679 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
deadbeef0ed85b22016-02-23 17:24:52 -08001680 if (!AddTransportOffer(content_name,
1681 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001682 current_description, desc)) {
1683 return false;
1684 }
1685
1686 return true;
1687}
1688
1689bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1690 const MediaSessionOptions& options,
1691 const SessionDescription* current_description,
1692 DataCodecs* data_codecs,
1693 StreamParamsVec* current_streams,
1694 SessionDescription* desc) const {
1695 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1696
kwiberg31022942016-03-11 14:18:21 -08001697 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001698 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1699
1700 FilterDataCodecs(data_codecs, is_sctp);
1701
deadbeef44f08192015-12-15 16:20:09 -08001702 const ContentInfo* current_data_content =
1703 GetFirstDataContent(current_description);
1704 std::string content_name =
1705 current_data_content ? current_data_content->name : CN_DATA;
1706
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001707 cricket::SecurePolicy sdes_policy =
deadbeef44f08192015-12-15 16:20:09 -08001708 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1709 : secure();
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001710 std::vector<std::string> crypto_suites;
1711 if (is_sctp) {
1712 // SDES doesn't make sense for SCTP, so we disable it, and we only
1713 // get SDES crypto suites for RTP-based data channels.
1714 sdes_policy = cricket::SEC_DISABLED;
1715 // Unlike SetMediaProtocol below, we need to set the protocol
1716 // before we call CreateMediaContentOffer. Otherwise,
1717 // CreateMediaContentOffer won't know this is SCTP and will
1718 // generate SSRCs rather than SIDs.
1719 data->set_protocol(
1720 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1721 } else {
Guo-wei Shieh521ed7b2015-11-18 19:41:53 -08001722 GetSupportedDataCryptoSuiteNames(&crypto_suites);
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001723 }
1724
1725 if (!CreateMediaContentOffer(
1726 options,
1727 *data_codecs,
1728 sdes_policy,
1729 GetCryptos(GetFirstDataContentDescription(current_description)),
1730 crypto_suites,
1731 RtpHeaderExtensions(),
1732 add_legacy_,
1733 current_streams,
1734 data.get())) {
1735 return false;
1736 }
1737
1738 if (is_sctp) {
deadbeef44f08192015-12-15 16:20:09 -08001739 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001740 } else {
1741 data->set_bandwidth(options.data_bandwidth);
1742 SetMediaProtocol(secure_transport, data.get());
deadbeef44f08192015-12-15 16:20:09 -08001743 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001744 }
deadbeef0ed85b22016-02-23 17:24:52 -08001745 if (!AddTransportOffer(content_name,
1746 GetTransportOptions(options, content_name),
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001747 current_description, desc)) {
1748 return false;
1749 }
1750 return true;
1751}
1752
1753bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1754 const SessionDescription* offer,
1755 const MediaSessionOptions& options,
1756 const SessionDescription* current_description,
1757 StreamParamsVec* current_streams,
1758 SessionDescription* answer) const {
1759 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1760
kwiberg31022942016-03-11 14:18:21 -08001761 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001762 audio_content->name, offer,
1763 GetTransportOptions(options, audio_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001764 if (!audio_transport) {
1765 return false;
1766 }
1767
1768 AudioCodecs audio_codecs = audio_codecs_;
1769 if (!options.vad_enabled) {
1770 StripCNCodecs(&audio_codecs);
1771 }
1772
1773 bool bundle_enabled =
1774 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
kwiberg31022942016-03-11 14:18:21 -08001775 std::unique_ptr<AudioContentDescription> audio_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001776 new AudioContentDescription());
1777 // Do not require or create SDES cryptos if DTLS is used.
1778 cricket::SecurePolicy sdes_policy =
1779 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1780 if (!CreateMediaContentAnswer(
1781 static_cast<const AudioContentDescription*>(
1782 audio_content->description),
1783 options,
1784 audio_codecs,
1785 sdes_policy,
1786 GetCryptos(GetFirstAudioContentDescription(current_description)),
1787 audio_rtp_extensions_,
1788 current_streams,
1789 add_legacy_,
1790 bundle_enabled,
1791 audio_answer.get())) {
1792 return false; // Fails the session setup.
1793 }
1794
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001795 bool rejected = !options.has_audio() || audio_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001796 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1797 audio_answer->protocol(),
1798 audio_transport->secure());
1799 if (!rejected) {
1800 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1801 } else {
1802 // RFC 3264
1803 // The answer MUST contain the same number of m-lines as the offer.
1804 LOG(LS_INFO) << "Audio is not supported in the answer.";
1805 }
1806
1807 answer->AddContent(audio_content->name, audio_content->type, rejected,
1808 audio_answer.release());
1809 return true;
1810}
1811
1812bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1813 const SessionDescription* offer,
1814 const MediaSessionOptions& options,
1815 const SessionDescription* current_description,
1816 StreamParamsVec* current_streams,
1817 SessionDescription* answer) const {
1818 const ContentInfo* video_content = GetFirstVideoContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001819 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001820 video_content->name, offer,
1821 GetTransportOptions(options, video_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001822 if (!video_transport) {
1823 return false;
1824 }
1825
kwiberg31022942016-03-11 14:18:21 -08001826 std::unique_ptr<VideoContentDescription> video_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001827 new VideoContentDescription());
1828 // Do not require or create SDES cryptos if DTLS is used.
1829 cricket::SecurePolicy sdes_policy =
1830 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1831 bool bundle_enabled =
1832 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1833 if (!CreateMediaContentAnswer(
1834 static_cast<const VideoContentDescription*>(
1835 video_content->description),
1836 options,
1837 video_codecs_,
1838 sdes_policy,
1839 GetCryptos(GetFirstVideoContentDescription(current_description)),
1840 video_rtp_extensions_,
1841 current_streams,
1842 add_legacy_,
1843 bundle_enabled,
1844 video_answer.get())) {
1845 return false;
1846 }
jiayl@webrtc.org742922b2014-10-07 21:32:43 +00001847 bool rejected = !options.has_video() || video_content->rejected ||
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001848 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1849 video_answer->protocol(),
1850 video_transport->secure());
1851 if (!rejected) {
1852 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1853 answer)) {
1854 return false;
1855 }
1856 video_answer->set_bandwidth(options.video_bandwidth);
1857 } else {
1858 // RFC 3264
1859 // The answer MUST contain the same number of m-lines as the offer.
1860 LOG(LS_INFO) << "Video is not supported in the answer.";
1861 }
1862 answer->AddContent(video_content->name, video_content->type, rejected,
1863 video_answer.release());
1864 return true;
1865}
1866
1867bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1868 const SessionDescription* offer,
1869 const MediaSessionOptions& options,
1870 const SessionDescription* current_description,
1871 StreamParamsVec* current_streams,
1872 SessionDescription* answer) const {
1873 const ContentInfo* data_content = GetFirstDataContent(offer);
kwiberg31022942016-03-11 14:18:21 -08001874 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
deadbeef0ed85b22016-02-23 17:24:52 -08001875 data_content->name, offer,
1876 GetTransportOptions(options, data_content->name), current_description));
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001877 if (!data_transport) {
1878 return false;
1879 }
1880 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1881 std::vector<DataCodec> data_codecs(data_codecs_);
1882 FilterDataCodecs(&data_codecs, is_sctp);
1883
kwiberg31022942016-03-11 14:18:21 -08001884 std::unique_ptr<DataContentDescription> data_answer(
jiayl@webrtc.orge7d47a12014-08-05 19:19:05 +00001885 new DataContentDescription());
1886 // Do not require or create SDES cryptos if DTLS is used.
1887 cricket::SecurePolicy sdes_policy =
1888 data_transport->secure() ? cricket::SEC_DISABLED : secure();
1889 bool bundle_enabled =
1890 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1891 if (!CreateMediaContentAnswer(
1892 static_cast<const DataContentDescription*>(
1893 data_content->description),
1894 options,
1895 data_codecs_,
1896 sdes_policy,
1897 GetCryptos(GetFirstDataContentDescription(current_description)),
1898 RtpHeaderExtensions(),
1899 current_streams,
1900 add_legacy_,
1901 bundle_enabled,
1902 data_answer.get())) {
1903 return false; // Fails the session setup.
1904 }
1905
1906 bool rejected = !options.has_data() || data_content->rejected ||
1907 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
1908 data_answer->protocol(),
1909 data_transport->secure());
1910 if (!rejected) {
1911 data_answer->set_bandwidth(options.data_bandwidth);
1912 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
1913 answer)) {
1914 return false;
1915 }
1916 } else {
1917 // RFC 3264
1918 // The answer MUST contain the same number of m-lines as the offer.
1919 LOG(LS_INFO) << "Data is not supported in the answer.";
1920 }
1921 answer->AddContent(data_content->name, data_content->type, rejected,
1922 data_answer.release());
1923 return true;
1924}
1925
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001926bool IsMediaContent(const ContentInfo* content) {
1927 return (content &&
1928 (content->type == NS_JINGLE_RTP ||
1929 content->type == NS_JINGLE_DRAFT_SCTP));
1930}
1931
1932bool IsAudioContent(const ContentInfo* content) {
1933 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
1934}
1935
1936bool IsVideoContent(const ContentInfo* content) {
1937 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
1938}
1939
1940bool IsDataContent(const ContentInfo* content) {
1941 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
1942}
1943
deadbeef0ed85b22016-02-23 17:24:52 -08001944const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
1945 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001946 for (ContentInfos::const_iterator content = contents.begin();
1947 content != contents.end(); content++) {
1948 if (IsMediaContentOfType(&*content, media_type)) {
1949 return &*content;
1950 }
1951 }
deadbeef0ed85b22016-02-23 17:24:52 -08001952 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001953}
1954
1955const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
1956 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
1957}
1958
1959const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
1960 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
1961}
1962
1963const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
1964 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
1965}
1966
1967static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
1968 MediaType media_type) {
deadbeef0ed85b22016-02-23 17:24:52 -08001969 if (sdesc == nullptr) {
1970 return nullptr;
1971 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001972
1973 return GetFirstMediaContent(sdesc->contents(), media_type);
1974}
1975
1976const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
1977 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
1978}
1979
1980const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
1981 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
1982}
1983
1984const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
1985 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
1986}
1987
1988const MediaContentDescription* GetFirstMediaContentDescription(
1989 const SessionDescription* sdesc, MediaType media_type) {
1990 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
1991 const ContentDescription* description = content ? content->description : NULL;
1992 return static_cast<const MediaContentDescription*>(description);
1993}
1994
1995const AudioContentDescription* GetFirstAudioContentDescription(
1996 const SessionDescription* sdesc) {
1997 return static_cast<const AudioContentDescription*>(
1998 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
1999}
2000
2001const VideoContentDescription* GetFirstVideoContentDescription(
2002 const SessionDescription* sdesc) {
2003 return static_cast<const VideoContentDescription*>(
2004 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2005}
2006
2007const DataContentDescription* GetFirstDataContentDescription(
2008 const SessionDescription* sdesc) {
2009 return static_cast<const DataContentDescription*>(
2010 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2011}
2012
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002013} // namespace cricket