1 /*
2 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
3 *
4 * 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.
9 */
10
11 #include "webrtc/pc/mediasession.h"
12
13 #include <algorithm> // For std::find_if, std::sort.
14 #include <functional>
15 #include <map>
16 #include <memory>
17 #include <set>
18 #include <unordered_map>
19 #include <utility>
20
21 #include "webrtc/base/base64.h"
22 #include "webrtc/base/checks.h"
23 #include "webrtc/base/helpers.h"
24 #include "webrtc/base/logging.h"
25 #include "webrtc/base/stringutils.h"
26 #include "webrtc/common_video/h264/profile_level_id.h"
27 #include "webrtc/media/base/cryptoparams.h"
28 #include "webrtc/media/base/mediaconstants.h"
29 #include "webrtc/p2p/base/p2pconstants.h"
30 #include "webrtc/pc/channelmanager.h"
31 #include "webrtc/pc/srtpfilter.h"
32
33 namespace {
34 const char kInline[] = "inline:";
35
GetSupportedCryptoSuiteNames(void (* func)(const rtc::CryptoOptions &,std::vector<int> *),const rtc::CryptoOptions & crypto_options,std::vector<std::string> * names)36 void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
37 std::vector<int>*),
38 const rtc::CryptoOptions& crypto_options,
39 std::vector<std::string>* names) {
40 #ifdef HAVE_SRTP
41 std::vector<int> crypto_suites;
42 func(crypto_options, &crypto_suites);
43 for (const auto crypto : crypto_suites) {
44 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
45 }
46 #endif
47 }
48 } // namespace
49
50 namespace cricket {
51
52 // RTP Profile names
53 // http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
54 // RFC4585
55 const char kMediaProtocolAvpf[] = "RTP/AVPF";
56 // RFC5124
57 const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
58
59 // We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
60 // but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
61 const char kMediaProtocolSavpf[] = "RTP/SAVPF";
62
63 const char kMediaProtocolRtpPrefix[] = "RTP/";
64
65 const char kMediaProtocolSctp[] = "SCTP";
66 const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
67 const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
68 const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
69
FromMediaContentDirection(MediaContentDirection md)70 RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
71 MediaContentDirection md) {
72 const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
73 const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
74 return RtpTransceiverDirection(send, recv);
75 }
76
ToMediaContentDirection() const77 MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
78 if (send && recv) {
79 return MD_SENDRECV;
80 } else if (send) {
81 return MD_SENDONLY;
82 } else if (recv) {
83 return MD_RECVONLY;
84 }
85
86 return MD_INACTIVE;
87 }
88
89 RtpTransceiverDirection
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,RtpTransceiverDirection wants)90 NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
91 RtpTransceiverDirection wants) {
92 return RtpTransceiverDirection(offer.recv && wants.send,
93 offer.send && wants.recv);
94 }
95
IsMediaContentOfType(const ContentInfo * content,MediaType media_type)96 static bool IsMediaContentOfType(const ContentInfo* content,
97 MediaType media_type) {
98 if (!IsMediaContent(content)) {
99 return false;
100 }
101
102 const MediaContentDescription* mdesc =
103 static_cast<const MediaContentDescription*>(content->description);
104 return mdesc && mdesc->type() == media_type;
105 }
106
CreateCryptoParams(int tag,const std::string & cipher,CryptoParams * out)107 static bool CreateCryptoParams(int tag, const std::string& cipher,
108 CryptoParams *out) {
109 int key_len;
110 int salt_len;
111 if (!rtc::GetSrtpKeyAndSaltLengths(
112 rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
113 return false;
114 }
115
116 int master_key_len = key_len + salt_len;
117 std::string master_key;
118 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
119 return false;
120 }
121
122 RTC_CHECK_EQ(master_key_len, master_key.size());
123 std::string key = rtc::Base64::Encode(master_key);
124
125 out->tag = tag;
126 out->cipher_suite = cipher;
127 out->key_params = kInline;
128 out->key_params += key;
129 return true;
130 }
131
132 #ifdef HAVE_SRTP
AddCryptoParams(const std::string & cipher_suite,CryptoParamsVec * out)133 static bool AddCryptoParams(const std::string& cipher_suite,
134 CryptoParamsVec *out) {
135 int size = static_cast<int>(out->size());
136
137 out->resize(size + 1);
138 return CreateCryptoParams(size, cipher_suite, &out->at(size));
139 }
140
AddMediaCryptos(const CryptoParamsVec & cryptos,MediaContentDescription * media)141 void AddMediaCryptos(const CryptoParamsVec& cryptos,
142 MediaContentDescription* media) {
143 for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
144 crypto != cryptos.end(); ++crypto) {
145 media->AddCrypto(*crypto);
146 }
147 }
148
CreateMediaCryptos(const std::vector<std::string> & crypto_suites,MediaContentDescription * media)149 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
150 MediaContentDescription* media) {
151 CryptoParamsVec cryptos;
152 for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
153 it != crypto_suites.end(); ++it) {
154 if (!AddCryptoParams(*it, &cryptos)) {
155 return false;
156 }
157 }
158 AddMediaCryptos(cryptos, media);
159 return true;
160 }
161 #endif
162
GetCryptos(const MediaContentDescription * media)163 const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
164 if (!media) {
165 return NULL;
166 }
167 return &media->cryptos();
168 }
169
FindMatchingCrypto(const CryptoParamsVec & cryptos,const CryptoParams & crypto,CryptoParams * out)170 bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
171 const CryptoParams& crypto,
172 CryptoParams* out) {
173 for (CryptoParamsVec::const_iterator it = cryptos.begin();
174 it != cryptos.end(); ++it) {
175 if (crypto.Matches(*it)) {
176 *out = *it;
177 return true;
178 }
179 }
180 return false;
181 }
182
183 // For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
GetSupportedAudioCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)184 void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
185 std::vector<int>* crypto_suites) {
186 #ifdef HAVE_SRTP
187 if (crypto_options.enable_gcm_crypto_suites) {
188 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
189 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
190 }
191 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
192 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
193 #endif
194 }
195
GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)196 void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
197 std::vector<std::string>* crypto_suite_names) {
198 GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
199 crypto_options, crypto_suite_names);
200 }
201
GetSupportedVideoCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)202 void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
203 std::vector<int>* crypto_suites) {
204 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
205 }
206
GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)207 void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
208 std::vector<std::string>* crypto_suite_names) {
209 GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
210 crypto_options, crypto_suite_names);
211 }
212
GetSupportedDataCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)213 void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
214 std::vector<int>* crypto_suites) {
215 GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
216 }
217
GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)218 void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
219 std::vector<std::string>* crypto_suite_names) {
220 GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
221 crypto_options, crypto_suite_names);
222 }
223
GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)224 void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
225 std::vector<int>* crypto_suites) {
226 #ifdef HAVE_SRTP
227 if (crypto_options.enable_gcm_crypto_suites) {
228 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
229 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
230 }
231 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
232 #endif
233 }
234
GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)235 void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
236 std::vector<std::string>* crypto_suite_names) {
237 GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites,
238 crypto_options, crypto_suite_names);
239 }
240
241 // Support any GCM cipher (if enabled through options). For video support only
242 // 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
243 // because it is low overhead.
244 // Pick the crypto in the list that is supported.
SelectCrypto(const MediaContentDescription * offer,bool bundle,const rtc::CryptoOptions & crypto_options,CryptoParams * crypto)245 static bool SelectCrypto(const MediaContentDescription* offer,
246 bool bundle,
247 const rtc::CryptoOptions& crypto_options,
248 CryptoParams *crypto) {
249 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
250 const CryptoParamsVec& cryptos = offer->cryptos();
251
252 for (CryptoParamsVec::const_iterator i = cryptos.begin();
253 i != cryptos.end(); ++i) {
254 if ((crypto_options.enable_gcm_crypto_suites &&
255 rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
256 rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
257 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
258 !bundle)) {
259 return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
260 }
261 }
262 return false;
263 }
264
265 // Generate random SSRC values that are not already present in |params_vec|.
266 // The generated values are added to |ssrcs|.
267 // |num_ssrcs| is the number of the SSRC will be generated.
GenerateSsrcs(const StreamParamsVec & params_vec,int num_ssrcs,std::vector<uint32_t> * ssrcs)268 static void GenerateSsrcs(const StreamParamsVec& params_vec,
269 int num_ssrcs,
270 std::vector<uint32_t>* ssrcs) {
271 for (int i = 0; i < num_ssrcs; i++) {
272 uint32_t candidate;
273 do {
274 candidate = rtc::CreateRandomNonZeroId();
275 } while (GetStreamBySsrc(params_vec, candidate) ||
276 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
277 ssrcs->push_back(candidate);
278 }
279 }
280
281 // Finds all StreamParams of all media types and attach them to stream_params.
GetCurrentStreamParams(const SessionDescription * sdesc,StreamParamsVec * stream_params)282 static void GetCurrentStreamParams(const SessionDescription* sdesc,
283 StreamParamsVec* stream_params) {
284 if (!sdesc)
285 return;
286
287 const ContentInfos& contents = sdesc->contents();
288 for (ContentInfos::const_iterator content = contents.begin();
289 content != contents.end(); ++content) {
290 if (!IsMediaContent(&*content)) {
291 continue;
292 }
293 const MediaContentDescription* media =
294 static_cast<const MediaContentDescription*>(
295 content->description);
296 const StreamParamsVec& streams = media->streams();
297 for (StreamParamsVec::const_iterator it = streams.begin();
298 it != streams.end(); ++it) {
299 stream_params->push_back(*it);
300 }
301 }
302 }
303
304 // Filters the data codecs for the data channel type.
FilterDataCodecs(std::vector<DataCodec> * codecs,bool sctp)305 void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
306 // Filter RTP codec for SCTP and vice versa.
307 const char* codec_name =
308 sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
309 for (std::vector<DataCodec>::iterator iter = codecs->begin();
310 iter != codecs->end();) {
311 if (CodecNamesEq(iter->name, codec_name)) {
312 iter = codecs->erase(iter);
313 } else {
314 ++iter;
315 }
316 }
317 }
318
319 template <typename IdStruct>
320 class UsedIds {
321 public:
UsedIds(int min_allowed_id,int max_allowed_id)322 UsedIds(int min_allowed_id, int max_allowed_id)
323 : min_allowed_id_(min_allowed_id),
324 max_allowed_id_(max_allowed_id),
325 next_id_(max_allowed_id) {
326 }
327
328 // Loops through all Id in |ids| and changes its id if it is
329 // already in use by another IdStruct. Call this methods with all Id
330 // in a session description to make sure no duplicate ids exists.
331 // Note that typename Id must be a type of IdStruct.
332 template <typename Id>
FindAndSetIdUsed(std::vector<Id> * ids)333 void FindAndSetIdUsed(std::vector<Id>* ids) {
334 for (typename std::vector<Id>::iterator it = ids->begin();
335 it != ids->end(); ++it) {
336 FindAndSetIdUsed(&*it);
337 }
338 }
339
340 // Finds and sets an unused id if the |idstruct| id is already in use.
FindAndSetIdUsed(IdStruct * idstruct)341 void FindAndSetIdUsed(IdStruct* idstruct) {
342 const int original_id = idstruct->id;
343 int new_id = idstruct->id;
344
345 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
346 // If the original id is not in range - this is an id that can't be
347 // dynamically changed.
348 return;
349 }
350
351 if (IsIdUsed(original_id)) {
352 new_id = FindUnusedId();
353 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
354 << " to " << new_id;
355 idstruct->id = new_id;
356 }
357 SetIdUsed(new_id);
358 }
359
360 private:
361 // Returns the first unused id in reverse order.
362 // This hopefully reduce the risk of more collisions. We want to change the
363 // default ids as little as possible.
FindUnusedId()364 int FindUnusedId() {
365 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
366 --next_id_;
367 }
368 RTC_DCHECK(next_id_ >= min_allowed_id_);
369 return next_id_;
370 }
371
IsIdUsed(int new_id)372 bool IsIdUsed(int new_id) {
373 return id_set_.find(new_id) != id_set_.end();
374 }
375
SetIdUsed(int new_id)376 void SetIdUsed(int new_id) {
377 id_set_.insert(new_id);
378 }
379
380 const int min_allowed_id_;
381 const int max_allowed_id_;
382 int next_id_;
383 std::set<int> id_set_;
384 };
385
386 // Helper class used for finding duplicate RTP payload types among audio, video
387 // and data codecs. When bundle is used the payload types may not collide.
388 class UsedPayloadTypes : public UsedIds<Codec> {
389 public:
UsedPayloadTypes()390 UsedPayloadTypes()
391 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
392 }
393
394
395 private:
396 static const int kDynamicPayloadTypeMin = 96;
397 static const int kDynamicPayloadTypeMax = 127;
398 };
399
400 // Helper class used for finding duplicate RTP Header extension ids among
401 // audio and video extensions.
402 class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
403 public:
UsedRtpHeaderExtensionIds()404 UsedRtpHeaderExtensionIds()
405 : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
406
407 private:
408 // Min and Max local identifier for one-byte header extensions, per RFC5285.
409 static const int kLocalIdMin = 1;
410 static const int kLocalIdMax = 14;
411 };
412
IsSctp(const MediaContentDescription * desc)413 static bool IsSctp(const MediaContentDescription* desc) {
414 return ((desc->protocol() == kMediaProtocolSctp) ||
415 (desc->protocol() == kMediaProtocolDtlsSctp));
416 }
417
418 // Adds a StreamParams for each Stream in Streams with media type
419 // media_type to content_description.
420 // |current_params| - All currently known StreamParams of any media type.
421 template <class C>
AddStreamParams(MediaType media_type,const MediaSessionOptions & options,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * content_description,const bool add_legacy_stream)422 static bool AddStreamParams(MediaType media_type,
423 const MediaSessionOptions& options,
424 StreamParamsVec* current_streams,
425 MediaContentDescriptionImpl<C>* content_description,
426 const bool add_legacy_stream) {
427 // SCTP streams are not negotiated using SDP/ContentDescriptions.
428 if (IsSctp(content_description)) {
429 return true;
430 }
431
432 const bool include_rtx_streams =
433 ContainsRtxCodec(content_description->codecs());
434
435 const MediaSessionOptions::Streams& streams = options.streams;
436 if (streams.empty() && add_legacy_stream) {
437 // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
438 std::vector<uint32_t> ssrcs;
439 int num_ssrcs = include_rtx_streams ? 2 : 1;
440 GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
441 if (include_rtx_streams) {
442 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
443 content_description->set_multistream(true);
444 } else {
445 content_description->AddLegacyStream(ssrcs[0]);
446 }
447 return true;
448 }
449
450 const bool include_flexfec_stream =
451 ContainsFlexfecCodec(content_description->codecs());
452
453 MediaSessionOptions::Streams::const_iterator stream_it;
454 for (stream_it = streams.begin();
455 stream_it != streams.end(); ++stream_it) {
456 if (stream_it->type != media_type)
457 continue; // Wrong media type.
458
459 const StreamParams* param =
460 GetStreamByIds(*current_streams, "", stream_it->id);
461 // groupid is empty for StreamParams generated using
462 // MediaSessionDescriptionFactory.
463 if (!param) {
464 // This is a new stream.
465 std::vector<uint32_t> ssrcs;
466 GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
467 StreamParams stream_param;
468 stream_param.id = stream_it->id;
469 // Add the generated ssrc.
470 for (size_t i = 0; i < ssrcs.size(); ++i) {
471 stream_param.ssrcs.push_back(ssrcs[i]);
472 }
473 if (stream_it->num_sim_layers > 1) {
474 SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
475 stream_param.ssrc_groups.push_back(group);
476 }
477 // Generate extra ssrcs for include_rtx_streams case.
478 if (include_rtx_streams) {
479 // Generate an RTX ssrc for every ssrc in the group.
480 std::vector<uint32_t> rtx_ssrcs;
481 GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
482 &rtx_ssrcs);
483 for (size_t i = 0; i < ssrcs.size(); ++i) {
484 stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
485 }
486 content_description->set_multistream(true);
487 }
488 // Generate extra ssrc for include_flexfec_stream case.
489 if (include_flexfec_stream) {
490 // TODO(brandtr): Update when we support multistream protection.
491 if (ssrcs.size() == 1) {
492 std::vector<uint32_t> flexfec_ssrcs;
493 GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
494 stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
495 content_description->set_multistream(true);
496 } else if (!ssrcs.empty()) {
497 LOG(LS_WARNING)
498 << "Our FlexFEC implementation only supports protecting "
499 << "a single media streams. This session has multiple "
500 << "media streams however, so no FlexFEC SSRC will be generated.";
501 }
502 }
503 stream_param.cname = options.rtcp_cname;
504 stream_param.sync_label = stream_it->sync_label;
505 content_description->AddStream(stream_param);
506
507 // Store the new StreamParams in current_streams.
508 // This is necessary so that we can use the CNAME for other media types.
509 current_streams->push_back(stream_param);
510 } else {
511 content_description->AddStream(*param);
512 }
513 }
514 return true;
515 }
516
517 // Updates the transport infos of the |sdesc| according to the given
518 // |bundle_group|. The transport infos of the content names within the
519 // |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
520 // first content within the |bundle_group|.
UpdateTransportInfoForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)521 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
522 SessionDescription* sdesc) {
523 // The bundle should not be empty.
524 if (!sdesc || !bundle_group.FirstContentName()) {
525 return false;
526 }
527
528 // We should definitely have a transport for the first content.
529 const std::string& selected_content_name = *bundle_group.FirstContentName();
530 const TransportInfo* selected_transport_info =
531 sdesc->GetTransportInfoByName(selected_content_name);
532 if (!selected_transport_info) {
533 return false;
534 }
535
536 // Set the other contents to use the same ICE credentials.
537 const std::string& selected_ufrag =
538 selected_transport_info->description.ice_ufrag;
539 const std::string& selected_pwd =
540 selected_transport_info->description.ice_pwd;
541 ConnectionRole selected_connection_role =
542 selected_transport_info->description.connection_role;
543 for (TransportInfos::iterator it =
544 sdesc->transport_infos().begin();
545 it != sdesc->transport_infos().end(); ++it) {
546 if (bundle_group.HasContentName(it->content_name) &&
547 it->content_name != selected_content_name) {
548 it->description.ice_ufrag = selected_ufrag;
549 it->description.ice_pwd = selected_pwd;
550 it->description.connection_role = selected_connection_role;
551 }
552 }
553 return true;
554 }
555
556 // Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
557 // sets it to |cryptos|.
GetCryptosByName(const SessionDescription * sdesc,const std::string & content_name,CryptoParamsVec * cryptos)558 static bool GetCryptosByName(const SessionDescription* sdesc,
559 const std::string& content_name,
560 CryptoParamsVec* cryptos) {
561 if (!sdesc || !cryptos) {
562 return false;
563 }
564
565 const ContentInfo* content = sdesc->GetContentByName(content_name);
566 if (!IsMediaContent(content) || !content->description) {
567 return false;
568 }
569
570 const MediaContentDescription* media_desc =
571 static_cast<const MediaContentDescription*>(content->description);
572 *cryptos = media_desc->cryptos();
573 return true;
574 }
575
576 // Predicate function used by the remove_if.
577 // Returns true if the |crypto|'s cipher_suite is not found in |filter|.
CryptoNotFound(const CryptoParams crypto,const CryptoParamsVec * filter)578 static bool CryptoNotFound(const CryptoParams crypto,
579 const CryptoParamsVec* filter) {
580 if (filter == NULL) {
581 return true;
582 }
583 for (CryptoParamsVec::const_iterator it = filter->begin();
584 it != filter->end(); ++it) {
585 if (it->cipher_suite == crypto.cipher_suite) {
586 return false;
587 }
588 }
589 return true;
590 }
591
592 // Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
593 // which are not available in |filter|.
PruneCryptos(const CryptoParamsVec & filter,CryptoParamsVec * target_cryptos)594 static void PruneCryptos(const CryptoParamsVec& filter,
595 CryptoParamsVec* target_cryptos) {
596 if (!target_cryptos) {
597 return;
598 }
599 target_cryptos->erase(std::remove_if(target_cryptos->begin(),
600 target_cryptos->end(),
601 bind2nd(ptr_fun(CryptoNotFound),
602 &filter)),
603 target_cryptos->end());
604 }
605
IsRtpProtocol(const std::string & protocol)606 static bool IsRtpProtocol(const std::string& protocol) {
607 return protocol.empty() ||
608 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
609 }
610
IsRtpContent(SessionDescription * sdesc,const std::string & content_name)611 static bool IsRtpContent(SessionDescription* sdesc,
612 const std::string& content_name) {
613 bool is_rtp = false;
614 ContentInfo* content = sdesc->GetContentByName(content_name);
615 if (IsMediaContent(content)) {
616 MediaContentDescription* media_desc =
617 static_cast<MediaContentDescription*>(content->description);
618 if (!media_desc) {
619 return false;
620 }
621 is_rtp = IsRtpProtocol(media_desc->protocol());
622 }
623 return is_rtp;
624 }
625
626 // Updates the crypto parameters of the |sdesc| according to the given
627 // |bundle_group|. The crypto parameters of all the contents within the
628 // |bundle_group| should be updated to use the common subset of the
629 // available cryptos.
UpdateCryptoParamsForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)630 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
631 SessionDescription* sdesc) {
632 // The bundle should not be empty.
633 if (!sdesc || !bundle_group.FirstContentName()) {
634 return false;
635 }
636
637 bool common_cryptos_needed = false;
638 // Get the common cryptos.
639 const ContentNames& content_names = bundle_group.content_names();
640 CryptoParamsVec common_cryptos;
641 for (ContentNames::const_iterator it = content_names.begin();
642 it != content_names.end(); ++it) {
643 if (!IsRtpContent(sdesc, *it)) {
644 continue;
645 }
646 // The common cryptos are needed if any of the content does not have DTLS
647 // enabled.
648 if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
649 common_cryptos_needed = true;
650 }
651 if (it == content_names.begin()) {
652 // Initial the common_cryptos with the first content in the bundle group.
653 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
654 return false;
655 }
656 if (common_cryptos.empty()) {
657 // If there's no crypto params, we should just return.
658 return true;
659 }
660 } else {
661 CryptoParamsVec cryptos;
662 if (!GetCryptosByName(sdesc, *it, &cryptos)) {
663 return false;
664 }
665 PruneCryptos(cryptos, &common_cryptos);
666 }
667 }
668
669 if (common_cryptos.empty() && common_cryptos_needed) {
670 return false;
671 }
672
673 // Update to use the 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 }
679 ContentInfo* content = sdesc->GetContentByName(*it);
680 if (IsMediaContent(content)) {
681 MediaContentDescription* media_desc =
682 static_cast<MediaContentDescription*>(content->description);
683 if (!media_desc) {
684 return false;
685 }
686 media_desc->set_cryptos(common_cryptos);
687 }
688 }
689 return true;
690 }
691
692 template <class C>
ContainsRtxCodec(const std::vector<C> & codecs)693 static bool ContainsRtxCodec(const std::vector<C>& codecs) {
694 for (const auto& codec : codecs) {
695 if (IsRtxCodec(codec)) {
696 return true;
697 }
698 }
699 return false;
700 }
701
702 template <class C>
IsRtxCodec(const C & codec)703 static bool IsRtxCodec(const C& codec) {
704 return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
705 }
706
707 template <class C>
ContainsFlexfecCodec(const std::vector<C> & codecs)708 static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
709 for (const auto& codec : codecs) {
710 if (IsFlexfecCodec(codec)) {
711 return true;
712 }
713 }
714 return false;
715 }
716
717 template <class C>
IsFlexfecCodec(const C & codec)718 static bool IsFlexfecCodec(const C& codec) {
719 return stricmp(codec.name.c_str(), kFlexfecCodecName) == 0;
720 }
721
GetTransportOptions(const MediaSessionOptions & options,const std::string & content_name)722 static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
723 const std::string& content_name) {
724 TransportOptions transport_options;
725 auto it = options.transport_options.find(content_name);
726 if (it != options.transport_options.end()) {
727 transport_options = it->second;
728 }
729 transport_options.enable_ice_renomination = options.enable_ice_renomination;
730 return transport_options;
731 }
732
733 // Create a media content to be offered in a session-initiate,
734 // according to the given options.rtcp_mux, options.is_muc,
735 // options.streams, codecs, secure_transport, crypto, and streams. If we don't
736 // currently have crypto (in current_cryptos) and it is enabled (in
737 // secure_policy), crypto is created (according to crypto_suites). If
738 // add_legacy_stream is true, and current_streams is empty, a legacy
739 // stream is created. The created content is added to the offer.
740 template <class C>
CreateMediaContentOffer(const MediaSessionOptions & options,const std::vector<C> & codecs,const SecurePolicy & secure_policy,const CryptoParamsVec * current_cryptos,const std::vector<std::string> & crypto_suites,const RtpHeaderExtensions & rtp_extensions,bool add_legacy_stream,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * offer)741 static bool CreateMediaContentOffer(
742 const MediaSessionOptions& options,
743 const std::vector<C>& codecs,
744 const SecurePolicy& secure_policy,
745 const CryptoParamsVec* current_cryptos,
746 const std::vector<std::string>& crypto_suites,
747 const RtpHeaderExtensions& rtp_extensions,
748 bool add_legacy_stream,
749 StreamParamsVec* current_streams,
750 MediaContentDescriptionImpl<C>* offer) {
751 offer->AddCodecs(codecs);
752
753 offer->set_rtcp_mux(options.rtcp_mux_enabled);
754 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
755 offer->set_rtcp_reduced_size(true);
756 }
757 offer->set_multistream(options.is_muc);
758 offer->set_rtp_header_extensions(rtp_extensions);
759
760 if (!AddStreamParams(offer->type(), options, current_streams, offer,
761 add_legacy_stream)) {
762 return false;
763 }
764
765 #ifdef HAVE_SRTP
766 if (secure_policy != SEC_DISABLED) {
767 if (current_cryptos) {
768 AddMediaCryptos(*current_cryptos, offer);
769 }
770 if (offer->cryptos().empty()) {
771 if (!CreateMediaCryptos(crypto_suites, offer)) {
772 return false;
773 }
774 }
775 }
776 #endif
777
778 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
779 return false;
780 }
781 return true;
782 }
783
784 template <class C>
ReferencedCodecsMatch(const std::vector<C> & codecs1,const int codec1_id,const std::vector<C> & codecs2,const int codec2_id)785 static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
786 const int codec1_id,
787 const std::vector<C>& codecs2,
788 const int codec2_id) {
789 const C* codec1 = FindCodecById(codecs1, codec1_id);
790 const C* codec2 = FindCodecById(codecs2, codec2_id);
791 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
792 }
793
794 template <class C>
NegotiateCodecs(const std::vector<C> & local_codecs,const std::vector<C> & offered_codecs,std::vector<C> * negotiated_codecs)795 static void NegotiateCodecs(const std::vector<C>& local_codecs,
796 const std::vector<C>& offered_codecs,
797 std::vector<C>* negotiated_codecs) {
798 for (const C& ours : local_codecs) {
799 C theirs;
800 // Note that we intentionally only find one matching codec for each of our
801 // local codecs, in case the remote offer contains duplicate codecs.
802 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
803 C negotiated = ours;
804 negotiated.IntersectFeedbackParams(theirs);
805 if (IsRtxCodec(negotiated)) {
806 const auto apt_it =
807 theirs.params.find(kCodecParamAssociatedPayloadType);
808 // FindMatchingCodec shouldn't return something with no apt value.
809 RTC_DCHECK(apt_it != theirs.params.end());
810 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
811 }
812 if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
813 webrtc::H264::GenerateProfileLevelIdForAnswer(
814 ours.params, theirs.params, &negotiated.params);
815 }
816 negotiated.id = theirs.id;
817 negotiated.name = theirs.name;
818 negotiated_codecs->push_back(std::move(negotiated));
819 }
820 }
821 // RFC3264: Although the answerer MAY list the formats in their desired
822 // order of preference, it is RECOMMENDED that unless there is a
823 // specific reason, the answerer list formats in the same relative order
824 // they were present in the offer.
825 std::unordered_map<int, int> payload_type_preferences;
826 int preference = static_cast<int>(offered_codecs.size() + 1);
827 for (const C& codec : offered_codecs) {
828 payload_type_preferences[codec.id] = preference--;
829 }
830 std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
831 [&payload_type_preferences](const C& a, const C& b) {
832 return payload_type_preferences[a.id] >
833 payload_type_preferences[b.id];
834 });
835 }
836
837 // Finds a codec in |codecs2| that matches |codec_to_match|, which is
838 // a member of |codecs1|. If |codec_to_match| is an RTX codec, both
839 // the codecs themselves and their associated codecs must match.
840 template <class C>
FindMatchingCodec(const std::vector<C> & codecs1,const std::vector<C> & codecs2,const C & codec_to_match,C * found_codec)841 static bool FindMatchingCodec(const std::vector<C>& codecs1,
842 const std::vector<C>& codecs2,
843 const C& codec_to_match,
844 C* found_codec) {
845 for (const C& potential_match : codecs2) {
846 if (potential_match.Matches(codec_to_match)) {
847 if (IsRtxCodec(codec_to_match)) {
848 int apt_value_1 = 0;
849 int apt_value_2 = 0;
850 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
851 &apt_value_1) ||
852 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
853 &apt_value_2)) {
854 LOG(LS_WARNING) << "RTX missing associated payload type.";
855 continue;
856 }
857 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
858 apt_value_2)) {
859 continue;
860 }
861 }
862 if (found_codec) {
863 *found_codec = potential_match;
864 }
865 return true;
866 }
867 }
868 return false;
869 }
870
871 // Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
872 // already exist in |offered_codecs| and ensure the payload types don't
873 // collide.
874 template <class C>
FindCodecsToOffer(const std::vector<C> & reference_codecs,std::vector<C> * offered_codecs,UsedPayloadTypes * used_pltypes)875 static void FindCodecsToOffer(
876 const std::vector<C>& reference_codecs,
877 std::vector<C>* offered_codecs,
878 UsedPayloadTypes* used_pltypes) {
879
880 // Add all new codecs that are not RTX codecs.
881 for (const C& reference_codec : reference_codecs) {
882 if (!IsRtxCodec(reference_codec) &&
883 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
884 reference_codec, nullptr)) {
885 C codec = reference_codec;
886 used_pltypes->FindAndSetIdUsed(&codec);
887 offered_codecs->push_back(codec);
888 }
889 }
890
891 // Add all new RTX codecs.
892 for (const C& reference_codec : reference_codecs) {
893 if (IsRtxCodec(reference_codec) &&
894 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
895 reference_codec, nullptr)) {
896 C rtx_codec = reference_codec;
897
898 std::string associated_pt_str;
899 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
900 &associated_pt_str)) {
901 LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
902 << " is missing an associated payload type.";
903 continue;
904 }
905
906 int associated_pt;
907 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
908 LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
909 << " of RTX codec " << rtx_codec.name
910 << " to an integer.";
911 continue;
912 }
913
914 // Find the associated reference codec for the reference RTX codec.
915 const C* associated_codec =
916 FindCodecById(reference_codecs, associated_pt);
917 if (!associated_codec) {
918 LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
919 << associated_pt << " for RTX codec " << rtx_codec.name
920 << ".";
921 continue;
922 }
923
924 // Find a codec in the offered list that matches the reference codec.
925 // Its payload type may be different than the reference codec.
926 C matching_codec;
927 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
928 *associated_codec, &matching_codec)) {
929 LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name
930 << " codec.";
931 continue;
932 }
933
934 rtx_codec.params[kCodecParamAssociatedPayloadType] =
935 rtc::ToString(matching_codec.id);
936 used_pltypes->FindAndSetIdUsed(&rtx_codec);
937 offered_codecs->push_back(rtx_codec);
938 }
939 }
940 }
941
FindByUri(const RtpHeaderExtensions & extensions,const webrtc::RtpExtension & ext_to_match,webrtc::RtpExtension * found_extension)942 static bool FindByUri(const RtpHeaderExtensions& extensions,
943 const webrtc::RtpExtension& ext_to_match,
944 webrtc::RtpExtension* found_extension) {
945 for (RtpHeaderExtensions::const_iterator it = extensions.begin();
946 it != extensions.end(); ++it) {
947 // We assume that all URIs are given in a canonical format.
948 if (it->uri == ext_to_match.uri) {
949 if (found_extension != NULL) {
950 *found_extension = *it;
951 }
952 return true;
953 }
954 }
955 return false;
956 }
957
958 // Iterates through |offered_extensions|, adding each one to |all_extensions|
959 // and |used_ids|, and resolving ID conflicts. If an offered extension has the
960 // same URI as one in |all_extensions|, it will re-use the same ID and won't be
961 // treated as a conflict.
FindAndSetRtpHdrExtUsed(RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * all_extensions,UsedRtpHeaderExtensionIds * used_ids)962 static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
963 RtpHeaderExtensions* all_extensions,
964 UsedRtpHeaderExtensionIds* used_ids) {
965 for (auto& extension : *offered_extensions) {
966 webrtc::RtpExtension existing;
967 if (FindByUri(*all_extensions, extension, &existing)) {
968 extension.id = existing.id;
969 } else {
970 used_ids->FindAndSetIdUsed(&extension);
971 all_extensions->push_back(extension);
972 }
973 }
974 }
975
976 // Adds |reference_extensions| to |offered_extensions|, while updating
977 // |all_extensions| and |used_ids|.
FindRtpHdrExtsToOffer(const RtpHeaderExtensions & reference_extensions,RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * all_extensions,UsedRtpHeaderExtensionIds * used_ids)978 static void FindRtpHdrExtsToOffer(
979 const RtpHeaderExtensions& reference_extensions,
980 RtpHeaderExtensions* offered_extensions,
981 RtpHeaderExtensions* all_extensions,
982 UsedRtpHeaderExtensionIds* used_ids) {
983 for (auto reference_extension : reference_extensions) {
984 if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
985 webrtc::RtpExtension existing;
986 if (FindByUri(*all_extensions, reference_extension, &existing)) {
987 offered_extensions->push_back(existing);
988 } else {
989 used_ids->FindAndSetIdUsed(&reference_extension);
990 all_extensions->push_back(reference_extension);
991 offered_extensions->push_back(reference_extension);
992 }
993 }
994 }
995 }
996
NegotiateRtpHeaderExtensions(const RtpHeaderExtensions & local_extensions,const RtpHeaderExtensions & offered_extensions,RtpHeaderExtensions * negotiated_extenstions)997 static void NegotiateRtpHeaderExtensions(
998 const RtpHeaderExtensions& local_extensions,
999 const RtpHeaderExtensions& offered_extensions,
1000 RtpHeaderExtensions* negotiated_extenstions) {
1001 RtpHeaderExtensions::const_iterator ours;
1002 for (ours = local_extensions.begin();
1003 ours != local_extensions.end(); ++ours) {
1004 webrtc::RtpExtension theirs;
1005 if (FindByUri(offered_extensions, *ours, &theirs)) {
1006 // We respond with their RTP header extension id.
1007 negotiated_extenstions->push_back(theirs);
1008 }
1009 }
1010 }
1011
StripCNCodecs(AudioCodecs * audio_codecs)1012 static void StripCNCodecs(AudioCodecs* audio_codecs) {
1013 AudioCodecs::iterator iter = audio_codecs->begin();
1014 while (iter != audio_codecs->end()) {
1015 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
1016 iter = audio_codecs->erase(iter);
1017 } else {
1018 ++iter;
1019 }
1020 }
1021 }
1022
1023 // Create a media content to be answered in a session-accept,
1024 // according to the given options.rtcp_mux, options.streams, codecs,
1025 // crypto, and streams. If we don't currently have crypto (in
1026 // current_cryptos) and it is enabled (in secure_policy), crypto is
1027 // created (according to crypto_suites). If add_legacy_stream is
1028 // true, and current_streams is empty, a legacy stream is created.
1029 // The codecs, rtcp_mux, and crypto are all negotiated with the offer
1030 // from the incoming session-initiate. If the negotiation fails, this
1031 // method returns false. The created content is added to the offer.
1032 template <class C>
CreateMediaContentAnswer(const MediaContentDescriptionImpl<C> * offer,const MediaSessionOptions & options,const std::vector<C> & local_codecs,const SecurePolicy & sdes_policy,const CryptoParamsVec * current_cryptos,const RtpHeaderExtensions & local_rtp_extenstions,StreamParamsVec * current_streams,bool add_legacy_stream,bool bundle_enabled,MediaContentDescriptionImpl<C> * answer)1033 static bool CreateMediaContentAnswer(
1034 const MediaContentDescriptionImpl<C>* offer,
1035 const MediaSessionOptions& options,
1036 const std::vector<C>& local_codecs,
1037 const SecurePolicy& sdes_policy,
1038 const CryptoParamsVec* current_cryptos,
1039 const RtpHeaderExtensions& local_rtp_extenstions,
1040 StreamParamsVec* current_streams,
1041 bool add_legacy_stream,
1042 bool bundle_enabled,
1043 MediaContentDescriptionImpl<C>* answer) {
1044 std::vector<C> negotiated_codecs;
1045 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1046 answer->AddCodecs(negotiated_codecs);
1047 answer->set_protocol(offer->protocol());
1048 RtpHeaderExtensions negotiated_rtp_extensions;
1049 NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1050 offer->rtp_header_extensions(),
1051 &negotiated_rtp_extensions);
1052 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1053
1054 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
1055 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1056 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1057 }
1058
1059 if (sdes_policy != SEC_DISABLED) {
1060 CryptoParams crypto;
1061 if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
1062 if (current_cryptos) {
1063 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1064 }
1065 answer->AddCrypto(crypto);
1066 }
1067 }
1068
1069 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
1070 return false;
1071 }
1072
1073 if (!AddStreamParams(answer->type(), options, current_streams, answer,
1074 add_legacy_stream)) {
1075 return false; // Something went seriously wrong.
1076 }
1077
1078 // Make sure the answer media content direction is per default set as
1079 // described in RFC3264 section 6.1.
1080 const bool is_data = !IsRtpProtocol(answer->protocol());
1081 const bool has_send_streams = !answer->streams().empty();
1082 const bool wants_send = has_send_streams || is_data;
1083 const bool recv_audio =
1084 answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
1085 const bool recv_video =
1086 answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
1087 const bool recv_data =
1088 answer->type() == cricket::MEDIA_TYPE_DATA;
1089 const bool wants_receive = recv_audio || recv_video || recv_data;
1090
1091 auto offer_rtd =
1092 RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
1093 auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
1094 answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
1095 .ToMediaContentDirection());
1096 return true;
1097 }
1098
IsDtlsRtp(const std::string & protocol)1099 static bool IsDtlsRtp(const std::string& protocol) {
1100 // Most-likely values first.
1101 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1102 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1103 }
1104
IsPlainRtp(const std::string & protocol)1105 static bool IsPlainRtp(const std::string& protocol) {
1106 // Most-likely values first.
1107 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1108 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1109 }
1110
IsDtlsSctp(const std::string & protocol)1111 static bool IsDtlsSctp(const std::string& protocol) {
1112 return protocol == "DTLS/SCTP";
1113 }
1114
IsPlainSctp(const std::string & protocol)1115 static bool IsPlainSctp(const std::string& protocol) {
1116 return protocol == "SCTP";
1117 }
1118
IsMediaProtocolSupported(MediaType type,const std::string & protocol,bool secure_transport)1119 static bool IsMediaProtocolSupported(MediaType type,
1120 const std::string& protocol,
1121 bool secure_transport) {
1122 // Since not all applications serialize and deserialize the media protocol,
1123 // we will have to accept |protocol| to be empty.
1124 if (protocol.empty()) {
1125 return true;
1126 }
1127
1128 if (type == MEDIA_TYPE_DATA) {
1129 // Check for SCTP, but also for RTP for RTP-based data channels.
1130 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1131 if (secure_transport) {
1132 // Most likely scenarios first.
1133 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1134 IsPlainRtp(protocol);
1135 } else {
1136 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1137 }
1138 }
1139
1140 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1141 // JSEP specifies.
1142 if (secure_transport) {
1143 // Most likely scenarios first.
1144 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1145 } else {
1146 return IsPlainRtp(protocol);
1147 }
1148 }
1149
SetMediaProtocol(bool secure_transport,MediaContentDescription * desc)1150 static void SetMediaProtocol(bool secure_transport,
1151 MediaContentDescription* desc) {
1152 if (!desc->cryptos().empty())
1153 desc->set_protocol(kMediaProtocolSavpf);
1154 else if (secure_transport)
1155 desc->set_protocol(kMediaProtocolDtlsSavpf);
1156 else
1157 desc->set_protocol(kMediaProtocolAvpf);
1158 }
1159
1160 // Gets the TransportInfo of the given |content_name| from the
1161 // |current_description|. If doesn't exist, returns a new one.
GetTransportDescription(const std::string & content_name,const SessionDescription * current_description)1162 static const TransportDescription* GetTransportDescription(
1163 const std::string& content_name,
1164 const SessionDescription* current_description) {
1165 const TransportDescription* desc = NULL;
1166 if (current_description) {
1167 const TransportInfo* info =
1168 current_description->GetTransportInfoByName(content_name);
1169 if (info) {
1170 desc = &info->description;
1171 }
1172 }
1173 return desc;
1174 }
1175
1176 // Gets the current DTLS state from the transport description.
IsDtlsActive(const std::string & content_name,const SessionDescription * current_description)1177 static bool IsDtlsActive(
1178 const std::string& content_name,
1179 const SessionDescription* current_description) {
1180 if (!current_description)
1181 return false;
1182
1183 const ContentInfo* content =
1184 current_description->GetContentByName(content_name);
1185 if (!content)
1186 return false;
1187
1188 const TransportDescription* current_tdesc =
1189 GetTransportDescription(content_name, current_description);
1190 if (!current_tdesc)
1191 return false;
1192
1193 return current_tdesc->secure();
1194 }
1195
MediaTypeToString(MediaType type)1196 std::string MediaTypeToString(MediaType type) {
1197 std::string type_str;
1198 switch (type) {
1199 case MEDIA_TYPE_AUDIO:
1200 type_str = "audio";
1201 break;
1202 case MEDIA_TYPE_VIDEO:
1203 type_str = "video";
1204 break;
1205 case MEDIA_TYPE_DATA:
1206 type_str = "data";
1207 break;
1208 default:
1209 RTC_NOTREACHED();
1210 break;
1211 }
1212 return type_str;
1213 }
1214
MediaContentDirectionToString(MediaContentDirection direction)1215 std::string MediaContentDirectionToString(MediaContentDirection direction) {
1216 std::string dir_str;
1217 switch (direction) {
1218 case MD_INACTIVE:
1219 dir_str = "inactive";
1220 break;
1221 case MD_SENDONLY:
1222 dir_str = "sendonly";
1223 break;
1224 case MD_RECVONLY:
1225 dir_str = "recvonly";
1226 break;
1227 case MD_SENDRECV:
1228 dir_str = "sendrecv";
1229 break;
1230 default:
1231 RTC_NOTREACHED();
1232 break;
1233 }
1234
1235 return dir_str;
1236 }
1237
AddSendStream(MediaType type,const std::string & id,const std::string & sync_label)1238 void MediaSessionOptions::AddSendStream(MediaType type,
1239 const std::string& id,
1240 const std::string& sync_label) {
1241 AddSendStreamInternal(type, id, sync_label, 1);
1242 }
1243
AddSendVideoStream(const std::string & id,const std::string & sync_label,int num_sim_layers)1244 void MediaSessionOptions::AddSendVideoStream(
1245 const std::string& id,
1246 const std::string& sync_label,
1247 int num_sim_layers) {
1248 AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
1249 }
1250
AddSendStreamInternal(MediaType type,const std::string & id,const std::string & sync_label,int num_sim_layers)1251 void MediaSessionOptions::AddSendStreamInternal(
1252 MediaType type,
1253 const std::string& id,
1254 const std::string& sync_label,
1255 int num_sim_layers) {
1256 streams.push_back(Stream(type, id, sync_label, num_sim_layers));
1257
1258 // If we haven't already set the data_channel_type, and we add a
1259 // stream, we assume it's an RTP data stream.
1260 if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
1261 data_channel_type = DCT_RTP;
1262 }
1263
RemoveSendStream(MediaType type,const std::string & id)1264 void MediaSessionOptions::RemoveSendStream(MediaType type,
1265 const std::string& id) {
1266 Streams::iterator stream_it = streams.begin();
1267 for (; stream_it != streams.end(); ++stream_it) {
1268 if (stream_it->type == type && stream_it->id == id) {
1269 streams.erase(stream_it);
1270 return;
1271 }
1272 }
1273 RTC_NOTREACHED();
1274 }
1275
HasSendMediaStream(MediaType type) const1276 bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1277 Streams::const_iterator stream_it = streams.begin();
1278 for (; stream_it != streams.end(); ++stream_it) {
1279 if (stream_it->type == type) {
1280 return true;
1281 }
1282 }
1283 return false;
1284 }
1285
MediaSessionDescriptionFactory(const TransportDescriptionFactory * transport_desc_factory)1286 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1287 const TransportDescriptionFactory* transport_desc_factory)
1288 : secure_(SEC_DISABLED),
1289 add_legacy_(true),
1290 transport_desc_factory_(transport_desc_factory) {
1291 }
1292
MediaSessionDescriptionFactory(ChannelManager * channel_manager,const TransportDescriptionFactory * transport_desc_factory)1293 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1294 ChannelManager* channel_manager,
1295 const TransportDescriptionFactory* transport_desc_factory)
1296 : secure_(SEC_DISABLED),
1297 add_legacy_(true),
1298 transport_desc_factory_(transport_desc_factory) {
1299 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1300 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1301 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1302 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1303 channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1304 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1305 channel_manager->GetSupportedDataCodecs(&data_codecs_);
1306 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
1307 &audio_sendrecv_codecs_);
1308 }
1309
audio_sendrecv_codecs() const1310 const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1311 const {
1312 return audio_sendrecv_codecs_;
1313 }
1314
audio_send_codecs() const1315 const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1316 return audio_send_codecs_;
1317 }
1318
audio_recv_codecs() const1319 const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1320 return audio_recv_codecs_;
1321 }
1322
set_audio_codecs(const AudioCodecs & send_codecs,const AudioCodecs & recv_codecs)1323 void MediaSessionDescriptionFactory::set_audio_codecs(
1324 const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1325 audio_send_codecs_ = send_codecs;
1326 audio_recv_codecs_ = recv_codecs;
1327 audio_sendrecv_codecs_.clear();
1328 // Use NegotiateCodecs to merge our codec lists, since the operation is
1329 // essentially the same. Put send_codecs as the offered_codecs, which is the
1330 // order we'd like to follow. The reasoning is that encoding is usually more
1331 // expensive than decoding, and prioritizing a codec in the send list probably
1332 // means it's a codec we can handle efficiently.
1333 NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
1334 }
1335
CreateOffer(const MediaSessionOptions & options,const SessionDescription * current_description) const1336 SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1337 const MediaSessionOptions& options,
1338 const SessionDescription* current_description) const {
1339 std::unique_ptr<SessionDescription> offer(new SessionDescription());
1340
1341 StreamParamsVec current_streams;
1342 GetCurrentStreamParams(current_description, ¤t_streams);
1343
1344 const bool wants_send =
1345 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1346 const AudioCodecs& supported_audio_codecs =
1347 GetAudioCodecsForOffer({wants_send, options.recv_audio});
1348
1349 AudioCodecs audio_codecs;
1350 VideoCodecs video_codecs;
1351 DataCodecs data_codecs;
1352 GetCodecsToOffer(current_description, supported_audio_codecs,
1353 video_codecs_, data_codecs_,
1354 &audio_codecs, &video_codecs, &data_codecs);
1355
1356 if (!options.vad_enabled) {
1357 // If application doesn't want CN codecs in offer.
1358 StripCNCodecs(&audio_codecs);
1359 }
1360
1361 RtpHeaderExtensions audio_rtp_extensions;
1362 RtpHeaderExtensions video_rtp_extensions;
1363 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1364 &video_rtp_extensions);
1365
1366 bool audio_added = false;
1367 bool video_added = false;
1368 bool data_added = false;
1369
1370 // Iterate through the contents of |current_description| to maintain the order
1371 // of the m-lines in the new offer.
1372 if (current_description) {
1373 ContentInfos::const_iterator it = current_description->contents().begin();
1374 for (; it != current_description->contents().end(); ++it) {
1375 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1376 if (!AddAudioContentForOffer(options, current_description,
1377 audio_rtp_extensions, audio_codecs,
1378 ¤t_streams, offer.get())) {
1379 return NULL;
1380 }
1381 audio_added = true;
1382 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1383 if (!AddVideoContentForOffer(options, current_description,
1384 video_rtp_extensions, video_codecs,
1385 ¤t_streams, offer.get())) {
1386 return NULL;
1387 }
1388 video_added = true;
1389 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
1390 MediaSessionOptions options_copy(options);
1391 if (IsSctp(static_cast<const MediaContentDescription*>(
1392 it->description))) {
1393 options_copy.data_channel_type = DCT_SCTP;
1394 }
1395 if (!AddDataContentForOffer(options_copy, current_description,
1396 &data_codecs, ¤t_streams,
1397 offer.get())) {
1398 return NULL;
1399 }
1400 data_added = true;
1401 } else {
1402 RTC_NOTREACHED();
1403 }
1404 }
1405 }
1406
1407 // Append contents that are not in |current_description|.
1408 if (!audio_added && options.has_audio() &&
1409 !AddAudioContentForOffer(options, current_description,
1410 audio_rtp_extensions, audio_codecs,
1411 ¤t_streams, offer.get())) {
1412 return NULL;
1413 }
1414 if (!video_added && options.has_video() &&
1415 !AddVideoContentForOffer(options, current_description,
1416 video_rtp_extensions, video_codecs,
1417 ¤t_streams, offer.get())) {
1418 return NULL;
1419 }
1420 if (!data_added && options.has_data() &&
1421 !AddDataContentForOffer(options, current_description, &data_codecs,
1422 ¤t_streams, offer.get())) {
1423 return NULL;
1424 }
1425
1426 // Bundle the contents together, if we've been asked to do so, and update any
1427 // parameters that need to be tweaked for BUNDLE.
1428 if (options.bundle_enabled) {
1429 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1430 for (ContentInfos::const_iterator content = offer->contents().begin();
1431 content != offer->contents().end(); ++content) {
1432 offer_bundle.AddContentName(content->name);
1433 }
1434 offer->AddGroup(offer_bundle);
1435 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1436 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1437 return NULL;
1438 }
1439 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1440 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1441 return NULL;
1442 }
1443 }
1444
1445 return offer.release();
1446 }
1447
CreateAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description) const1448 SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1449 const SessionDescription* offer, const MediaSessionOptions& options,
1450 const SessionDescription* current_description) const {
1451 // The answer contains the intersection of the codecs in the offer with the
1452 // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1453 // from the offer in the answer.
1454 std::unique_ptr<SessionDescription> answer(new SessionDescription());
1455
1456 StreamParamsVec current_streams;
1457 GetCurrentStreamParams(current_description, ¤t_streams);
1458
1459 if (offer) {
1460 ContentInfos::const_iterator it = offer->contents().begin();
1461 for (; it != offer->contents().end(); ++it) {
1462 if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1463 if (!AddAudioContentForAnswer(offer, options, current_description,
1464 ¤t_streams, answer.get())) {
1465 return NULL;
1466 }
1467 } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1468 if (!AddVideoContentForAnswer(offer, options, current_description,
1469 ¤t_streams, answer.get())) {
1470 return NULL;
1471 }
1472 } else {
1473 RTC_DCHECK(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1474 if (!AddDataContentForAnswer(offer, options, current_description,
1475 ¤t_streams, answer.get())) {
1476 return NULL;
1477 }
1478 }
1479 }
1480 }
1481
1482 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1483 // group in the answer with the appropriate content names.
1484 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1485 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1486 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1487 for (ContentInfos::const_iterator content = answer->contents().begin();
1488 content != answer->contents().end(); ++content) {
1489 if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1490 answer_bundle.AddContentName(content->name);
1491 }
1492 }
1493 if (answer_bundle.FirstContentName()) {
1494 answer->AddGroup(answer_bundle);
1495
1496 // Share the same ICE credentials and crypto params across all contents,
1497 // as BUNDLE requires.
1498 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1499 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1500 return NULL;
1501 }
1502
1503 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1504 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1505 return NULL;
1506 }
1507 }
1508 }
1509
1510 return answer.release();
1511 }
1512
GetAudioCodecsForOffer(const RtpTransceiverDirection & direction) const1513 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1514 const RtpTransceiverDirection& direction) const {
1515 // If stream is inactive - generate list as if sendrecv.
1516 if (direction.send == direction.recv) {
1517 return audio_sendrecv_codecs_;
1518 } else if (direction.send) {
1519 return audio_send_codecs_;
1520 } else {
1521 return audio_recv_codecs_;
1522 }
1523 }
1524
GetAudioCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const1525 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1526 const RtpTransceiverDirection& offer,
1527 const RtpTransceiverDirection& answer) const {
1528 // For inactive and sendrecv answers, generate lists as if we were to accept
1529 // the offer's direction. See RFC 3264 Section 6.1.
1530 if (answer.send == answer.recv) {
1531 if (offer.send == offer.recv) {
1532 return audio_sendrecv_codecs_;
1533 } else if (offer.send) {
1534 return audio_recv_codecs_;
1535 } else {
1536 return audio_send_codecs_;
1537 }
1538 } else if (answer.send) {
1539 return audio_send_codecs_;
1540 } else {
1541 return audio_recv_codecs_;
1542 }
1543 }
1544
GetCodecsToOffer(const SessionDescription * current_description,const AudioCodecs & supported_audio_codecs,const VideoCodecs & supported_video_codecs,const DataCodecs & supported_data_codecs,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,DataCodecs * data_codecs) const1545 void MediaSessionDescriptionFactory::GetCodecsToOffer(
1546 const SessionDescription* current_description,
1547 const AudioCodecs& supported_audio_codecs,
1548 const VideoCodecs& supported_video_codecs,
1549 const DataCodecs& supported_data_codecs,
1550 AudioCodecs* audio_codecs,
1551 VideoCodecs* video_codecs,
1552 DataCodecs* data_codecs) const {
1553 UsedPayloadTypes used_pltypes;
1554 audio_codecs->clear();
1555 video_codecs->clear();
1556 data_codecs->clear();
1557
1558
1559 // First - get all codecs from the current description if the media type
1560 // is used.
1561 // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1562 // type is added.
1563 if (current_description) {
1564 const AudioContentDescription* audio =
1565 GetFirstAudioContentDescription(current_description);
1566 if (audio) {
1567 *audio_codecs = audio->codecs();
1568 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1569 }
1570 const VideoContentDescription* video =
1571 GetFirstVideoContentDescription(current_description);
1572 if (video) {
1573 *video_codecs = video->codecs();
1574 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1575 }
1576 const DataContentDescription* data =
1577 GetFirstDataContentDescription(current_description);
1578 if (data) {
1579 *data_codecs = data->codecs();
1580 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1581 }
1582 }
1583
1584 // Add our codecs that are not in |current_description|.
1585 FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
1586 &used_pltypes);
1587 FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
1588 &used_pltypes);
1589 FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
1590 &used_pltypes);
1591 }
1592
GetRtpHdrExtsToOffer(const SessionDescription * current_description,RtpHeaderExtensions * audio_extensions,RtpHeaderExtensions * video_extensions) const1593 void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1594 const SessionDescription* current_description,
1595 RtpHeaderExtensions* audio_extensions,
1596 RtpHeaderExtensions* video_extensions) const {
1597 // All header extensions allocated from the same range to avoid potential
1598 // issues when using BUNDLE.
1599 UsedRtpHeaderExtensionIds used_ids;
1600 RtpHeaderExtensions all_extensions;
1601 audio_extensions->clear();
1602 video_extensions->clear();
1603
1604 // First - get all extensions from the current description if the media type
1605 // is used.
1606 // Add them to |used_ids| so the local ids are not reused if a new media
1607 // type is added.
1608 if (current_description) {
1609 const AudioContentDescription* audio =
1610 GetFirstAudioContentDescription(current_description);
1611 if (audio) {
1612 *audio_extensions = audio->rtp_header_extensions();
1613 FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
1614 }
1615 const VideoContentDescription* video =
1616 GetFirstVideoContentDescription(current_description);
1617 if (video) {
1618 *video_extensions = video->rtp_header_extensions();
1619 FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
1620 }
1621 }
1622
1623 // Add our default RTP header extensions that are not in
1624 // |current_description|.
1625 FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1626 &all_extensions, &used_ids);
1627 FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1628 &all_extensions, &used_ids);
1629 }
1630
AddTransportOffer(const std::string & content_name,const TransportOptions & transport_options,const SessionDescription * current_desc,SessionDescription * offer_desc) const1631 bool MediaSessionDescriptionFactory::AddTransportOffer(
1632 const std::string& content_name,
1633 const TransportOptions& transport_options,
1634 const SessionDescription* current_desc,
1635 SessionDescription* offer_desc) const {
1636 if (!transport_desc_factory_)
1637 return false;
1638 const TransportDescription* current_tdesc =
1639 GetTransportDescription(content_name, current_desc);
1640 std::unique_ptr<TransportDescription> new_tdesc(
1641 transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1642 bool ret = (new_tdesc.get() != NULL &&
1643 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1644 if (!ret) {
1645 LOG(LS_ERROR)
1646 << "Failed to AddTransportOffer, content name=" << content_name;
1647 }
1648 return ret;
1649 }
1650
CreateTransportAnswer(const std::string & content_name,const SessionDescription * offer_desc,const TransportOptions & transport_options,const SessionDescription * current_desc) const1651 TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1652 const std::string& content_name,
1653 const SessionDescription* offer_desc,
1654 const TransportOptions& transport_options,
1655 const SessionDescription* current_desc) const {
1656 if (!transport_desc_factory_)
1657 return NULL;
1658 const TransportDescription* offer_tdesc =
1659 GetTransportDescription(content_name, offer_desc);
1660 const TransportDescription* current_tdesc =
1661 GetTransportDescription(content_name, current_desc);
1662 return
1663 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1664 current_tdesc);
1665 }
1666
AddTransportAnswer(const std::string & content_name,const TransportDescription & transport_desc,SessionDescription * answer_desc) const1667 bool MediaSessionDescriptionFactory::AddTransportAnswer(
1668 const std::string& content_name,
1669 const TransportDescription& transport_desc,
1670 SessionDescription* answer_desc) const {
1671 if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1672 transport_desc))) {
1673 LOG(LS_ERROR)
1674 << "Failed to AddTransportAnswer, content name=" << content_name;
1675 return false;
1676 }
1677 return true;
1678 }
1679
AddAudioContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,const RtpHeaderExtensions & audio_rtp_extensions,const AudioCodecs & audio_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1680 bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1681 const MediaSessionOptions& options,
1682 const SessionDescription* current_description,
1683 const RtpHeaderExtensions& audio_rtp_extensions,
1684 const AudioCodecs& audio_codecs,
1685 StreamParamsVec* current_streams,
1686 SessionDescription* desc) const {
1687 const ContentInfo* current_audio_content =
1688 GetFirstAudioContent(current_description);
1689 std::string content_name =
1690 current_audio_content ? current_audio_content->name : CN_AUDIO;
1691
1692 cricket::SecurePolicy sdes_policy =
1693 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1694 : secure();
1695
1696 std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
1697 std::vector<std::string> crypto_suites;
1698 GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
1699 if (!CreateMediaContentOffer(
1700 options,
1701 audio_codecs,
1702 sdes_policy,
1703 GetCryptos(GetFirstAudioContentDescription(current_description)),
1704 crypto_suites,
1705 audio_rtp_extensions,
1706 add_legacy_,
1707 current_streams,
1708 audio.get())) {
1709 return false;
1710 }
1711 audio->set_lang(lang_);
1712
1713 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1714 SetMediaProtocol(secure_transport, audio.get());
1715
1716 auto offer_rtd =
1717 RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
1718 audio->set_direction(offer_rtd.ToMediaContentDirection());
1719
1720 desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
1721 if (!AddTransportOffer(content_name,
1722 GetTransportOptions(options, content_name),
1723 current_description, desc)) {
1724 return false;
1725 }
1726
1727 return true;
1728 }
1729
AddVideoContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,const RtpHeaderExtensions & video_rtp_extensions,const VideoCodecs & video_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1730 bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1731 const MediaSessionOptions& options,
1732 const SessionDescription* current_description,
1733 const RtpHeaderExtensions& video_rtp_extensions,
1734 const VideoCodecs& video_codecs,
1735 StreamParamsVec* current_streams,
1736 SessionDescription* desc) const {
1737 const ContentInfo* current_video_content =
1738 GetFirstVideoContent(current_description);
1739 std::string content_name =
1740 current_video_content ? current_video_content->name : CN_VIDEO;
1741
1742 cricket::SecurePolicy sdes_policy =
1743 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1744 : secure();
1745
1746 std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
1747 std::vector<std::string> crypto_suites;
1748 GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
1749 if (!CreateMediaContentOffer(
1750 options,
1751 video_codecs,
1752 sdes_policy,
1753 GetCryptos(GetFirstVideoContentDescription(current_description)),
1754 crypto_suites,
1755 video_rtp_extensions,
1756 add_legacy_,
1757 current_streams,
1758 video.get())) {
1759 return false;
1760 }
1761
1762 video->set_bandwidth(options.video_bandwidth);
1763
1764 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1765 SetMediaProtocol(secure_transport, video.get());
1766
1767 if (!video->streams().empty()) {
1768 if (options.recv_video) {
1769 video->set_direction(MD_SENDRECV);
1770 } else {
1771 video->set_direction(MD_SENDONLY);
1772 }
1773 } else {
1774 if (options.recv_video) {
1775 video->set_direction(MD_RECVONLY);
1776 } else {
1777 video->set_direction(MD_INACTIVE);
1778 }
1779 }
1780
1781 desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
1782 if (!AddTransportOffer(content_name,
1783 GetTransportOptions(options, content_name),
1784 current_description, desc)) {
1785 return false;
1786 }
1787
1788 return true;
1789 }
1790
AddDataContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,DataCodecs * data_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1791 bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1792 const MediaSessionOptions& options,
1793 const SessionDescription* current_description,
1794 DataCodecs* data_codecs,
1795 StreamParamsVec* current_streams,
1796 SessionDescription* desc) const {
1797 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1798
1799 std::unique_ptr<DataContentDescription> data(new DataContentDescription());
1800 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1801
1802 FilterDataCodecs(data_codecs, is_sctp);
1803
1804 const ContentInfo* current_data_content =
1805 GetFirstDataContent(current_description);
1806 std::string content_name =
1807 current_data_content ? current_data_content->name : CN_DATA;
1808
1809 cricket::SecurePolicy sdes_policy =
1810 IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1811 : secure();
1812 std::vector<std::string> crypto_suites;
1813 if (is_sctp) {
1814 // SDES doesn't make sense for SCTP, so we disable it, and we only
1815 // get SDES crypto suites for RTP-based data channels.
1816 sdes_policy = cricket::SEC_DISABLED;
1817 // Unlike SetMediaProtocol below, we need to set the protocol
1818 // before we call CreateMediaContentOffer. Otherwise,
1819 // CreateMediaContentOffer won't know this is SCTP and will
1820 // generate SSRCs rather than SIDs.
1821 data->set_protocol(
1822 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1823 } else {
1824 GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
1825 }
1826
1827 if (!CreateMediaContentOffer(
1828 options,
1829 *data_codecs,
1830 sdes_policy,
1831 GetCryptos(GetFirstDataContentDescription(current_description)),
1832 crypto_suites,
1833 RtpHeaderExtensions(),
1834 add_legacy_,
1835 current_streams,
1836 data.get())) {
1837 return false;
1838 }
1839
1840 if (is_sctp) {
1841 desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
1842 } else {
1843 data->set_bandwidth(options.data_bandwidth);
1844 SetMediaProtocol(secure_transport, data.get());
1845 desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
1846 }
1847 if (!AddTransportOffer(content_name,
1848 GetTransportOptions(options, content_name),
1849 current_description, desc)) {
1850 return false;
1851 }
1852 return true;
1853 }
1854
AddAudioContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1855 bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1856 const SessionDescription* offer,
1857 const MediaSessionOptions& options,
1858 const SessionDescription* current_description,
1859 StreamParamsVec* current_streams,
1860 SessionDescription* answer) const {
1861 const ContentInfo* audio_content = GetFirstAudioContent(offer);
1862 const AudioContentDescription* offer_audio =
1863 static_cast<const AudioContentDescription*>(audio_content->description);
1864
1865 std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
1866 audio_content->name, offer,
1867 GetTransportOptions(options, audio_content->name), current_description));
1868 if (!audio_transport) {
1869 return false;
1870 }
1871
1872 // Pick codecs based on the requested communications direction in the offer.
1873 const bool wants_send =
1874 options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1875 auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
1876 auto offer_rtd =
1877 RtpTransceiverDirection::FromMediaContentDirection(
1878 offer_audio->direction());
1879 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
1880 AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
1881 if (!options.vad_enabled) {
1882 StripCNCodecs(&audio_codecs);
1883 }
1884
1885 bool bundle_enabled =
1886 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1887 std::unique_ptr<AudioContentDescription> audio_answer(
1888 new AudioContentDescription());
1889 // Do not require or create SDES cryptos if DTLS is used.
1890 cricket::SecurePolicy sdes_policy =
1891 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1892 if (!CreateMediaContentAnswer(
1893 offer_audio,
1894 options,
1895 audio_codecs,
1896 sdes_policy,
1897 GetCryptos(GetFirstAudioContentDescription(current_description)),
1898 audio_rtp_extensions_,
1899 current_streams,
1900 add_legacy_,
1901 bundle_enabled,
1902 audio_answer.get())) {
1903 return false; // Fails the session setup.
1904 }
1905
1906 bool rejected = !options.has_audio() || audio_content->rejected ||
1907 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1908 audio_answer->protocol(),
1909 audio_transport->secure());
1910 if (!rejected) {
1911 AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1912 } else {
1913 // RFC 3264
1914 // The answer MUST contain the same number of m-lines as the offer.
1915 LOG(LS_INFO) << "Audio is not supported in the answer.";
1916 }
1917
1918 answer->AddContent(audio_content->name, audio_content->type, rejected,
1919 audio_answer.release());
1920 return true;
1921 }
1922
AddVideoContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1923 bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1924 const SessionDescription* offer,
1925 const MediaSessionOptions& options,
1926 const SessionDescription* current_description,
1927 StreamParamsVec* current_streams,
1928 SessionDescription* answer) const {
1929 const ContentInfo* video_content = GetFirstVideoContent(offer);
1930 std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
1931 video_content->name, offer,
1932 GetTransportOptions(options, video_content->name), current_description));
1933 if (!video_transport) {
1934 return false;
1935 }
1936
1937 std::unique_ptr<VideoContentDescription> video_answer(
1938 new VideoContentDescription());
1939 // Do not require or create SDES cryptos if DTLS is used.
1940 cricket::SecurePolicy sdes_policy =
1941 video_transport->secure() ? cricket::SEC_DISABLED : secure();
1942 bool bundle_enabled =
1943 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1944 if (!CreateMediaContentAnswer(
1945 static_cast<const VideoContentDescription*>(
1946 video_content->description),
1947 options,
1948 video_codecs_,
1949 sdes_policy,
1950 GetCryptos(GetFirstVideoContentDescription(current_description)),
1951 video_rtp_extensions_,
1952 current_streams,
1953 add_legacy_,
1954 bundle_enabled,
1955 video_answer.get())) {
1956 return false;
1957 }
1958 bool rejected = !options.has_video() || video_content->rejected ||
1959 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1960 video_answer->protocol(),
1961 video_transport->secure());
1962 if (!rejected) {
1963 if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1964 answer)) {
1965 return false;
1966 }
1967 video_answer->set_bandwidth(options.video_bandwidth);
1968 } else {
1969 // RFC 3264
1970 // The answer MUST contain the same number of m-lines as the offer.
1971 LOG(LS_INFO) << "Video is not supported in the answer.";
1972 }
1973 answer->AddContent(video_content->name, video_content->type, rejected,
1974 video_answer.release());
1975 return true;
1976 }
1977
AddDataContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1978 bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1979 const SessionDescription* offer,
1980 const MediaSessionOptions& options,
1981 const SessionDescription* current_description,
1982 StreamParamsVec* current_streams,
1983 SessionDescription* answer) const {
1984 const ContentInfo* data_content = GetFirstDataContent(offer);
1985 std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
1986 data_content->name, offer,
1987 GetTransportOptions(options, data_content->name), current_description));
1988 if (!data_transport) {
1989 return false;
1990 }
1991 bool is_sctp = (options.data_channel_type == DCT_SCTP);
1992 std::vector<DataCodec> data_codecs(data_codecs_);
1993 FilterDataCodecs(&data_codecs, is_sctp);
1994
1995 std::unique_ptr<DataContentDescription> data_answer(
1996 new DataContentDescription());
1997 // Do not require or create SDES cryptos if DTLS is used.
1998 cricket::SecurePolicy sdes_policy =
1999 data_transport->secure() ? cricket::SEC_DISABLED : secure();
2000 bool bundle_enabled =
2001 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
2002 if (!CreateMediaContentAnswer(
2003 static_cast<const DataContentDescription*>(
2004 data_content->description),
2005 options,
2006 data_codecs_,
2007 sdes_policy,
2008 GetCryptos(GetFirstDataContentDescription(current_description)),
2009 RtpHeaderExtensions(),
2010 current_streams,
2011 add_legacy_,
2012 bundle_enabled,
2013 data_answer.get())) {
2014 return false; // Fails the session setup.
2015 }
2016
2017 bool rejected = !options.has_data() || data_content->rejected ||
2018 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2019 data_answer->protocol(),
2020 data_transport->secure());
2021 if (!rejected) {
2022 data_answer->set_bandwidth(options.data_bandwidth);
2023 if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
2024 answer)) {
2025 return false;
2026 }
2027 } else {
2028 // RFC 3264
2029 // The answer MUST contain the same number of m-lines as the offer.
2030 LOG(LS_INFO) << "Data is not supported in the answer.";
2031 }
2032 answer->AddContent(data_content->name, data_content->type, rejected,
2033 data_answer.release());
2034 return true;
2035 }
2036
IsMediaContent(const ContentInfo * content)2037 bool IsMediaContent(const ContentInfo* content) {
2038 return (content &&
2039 (content->type == NS_JINGLE_RTP ||
2040 content->type == NS_JINGLE_DRAFT_SCTP));
2041 }
2042
IsAudioContent(const ContentInfo * content)2043 bool IsAudioContent(const ContentInfo* content) {
2044 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2045 }
2046
IsVideoContent(const ContentInfo * content)2047 bool IsVideoContent(const ContentInfo* content) {
2048 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2049 }
2050
IsDataContent(const ContentInfo * content)2051 bool IsDataContent(const ContentInfo* content) {
2052 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2053 }
2054
GetFirstMediaContent(const ContentInfos & contents,MediaType media_type)2055 const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2056 MediaType media_type) {
2057 for (const ContentInfo& content : contents) {
2058 if (IsMediaContentOfType(&content, media_type)) {
2059 return &content;
2060 }
2061 }
2062 return nullptr;
2063 }
2064
GetFirstAudioContent(const ContentInfos & contents)2065 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2066 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2067 }
2068
GetFirstVideoContent(const ContentInfos & contents)2069 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2070 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2071 }
2072
GetFirstDataContent(const ContentInfos & contents)2073 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2074 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2075 }
2076
GetFirstMediaContent(const SessionDescription * sdesc,MediaType media_type)2077 static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2078 MediaType media_type) {
2079 if (sdesc == nullptr) {
2080 return nullptr;
2081 }
2082
2083 return GetFirstMediaContent(sdesc->contents(), media_type);
2084 }
2085
GetFirstAudioContent(const SessionDescription * sdesc)2086 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2087 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2088 }
2089
GetFirstVideoContent(const SessionDescription * sdesc)2090 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2091 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2092 }
2093
GetFirstDataContent(const SessionDescription * sdesc)2094 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2095 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2096 }
2097
GetFirstMediaContentDescription(const SessionDescription * sdesc,MediaType media_type)2098 const MediaContentDescription* GetFirstMediaContentDescription(
2099 const SessionDescription* sdesc, MediaType media_type) {
2100 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2101 const ContentDescription* description = content ? content->description : NULL;
2102 return static_cast<const MediaContentDescription*>(description);
2103 }
2104
GetFirstAudioContentDescription(const SessionDescription * sdesc)2105 const AudioContentDescription* GetFirstAudioContentDescription(
2106 const SessionDescription* sdesc) {
2107 return static_cast<const AudioContentDescription*>(
2108 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2109 }
2110
GetFirstVideoContentDescription(const SessionDescription * sdesc)2111 const VideoContentDescription* GetFirstVideoContentDescription(
2112 const SessionDescription* sdesc) {
2113 return static_cast<const VideoContentDescription*>(
2114 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2115 }
2116
GetFirstDataContentDescription(const SessionDescription * sdesc)2117 const DataContentDescription* GetFirstDataContentDescription(
2118 const SessionDescription* sdesc) {
2119 return static_cast<const DataContentDescription*>(
2120 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2121 }
2122
2123 //
2124 // Non-const versions of the above functions.
2125 //
2126
GetFirstMediaContent(ContentInfos & contents,MediaType media_type)2127 ContentInfo* GetFirstMediaContent(ContentInfos& contents,
2128 MediaType media_type) {
2129 for (ContentInfo& content : contents) {
2130 if (IsMediaContentOfType(&content, media_type)) {
2131 return &content;
2132 }
2133 }
2134 return nullptr;
2135 }
2136
GetFirstAudioContent(ContentInfos & contents)2137 ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2138 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2139 }
2140
GetFirstVideoContent(ContentInfos & contents)2141 ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2142 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2143 }
2144
GetFirstDataContent(ContentInfos & contents)2145 ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2146 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2147 }
2148
GetFirstMediaContent(SessionDescription * sdesc,MediaType media_type)2149 static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2150 MediaType media_type) {
2151 if (sdesc == nullptr) {
2152 return nullptr;
2153 }
2154
2155 return GetFirstMediaContent(sdesc->contents(), media_type);
2156 }
2157
GetFirstAudioContent(SessionDescription * sdesc)2158 ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2159 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2160 }
2161
GetFirstVideoContent(SessionDescription * sdesc)2162 ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2163 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2164 }
2165
GetFirstDataContent(SessionDescription * sdesc)2166 ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2167 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2168 }
2169
GetFirstMediaContentDescription(SessionDescription * sdesc,MediaType media_type)2170 MediaContentDescription* GetFirstMediaContentDescription(
2171 SessionDescription* sdesc,
2172 MediaType media_type) {
2173 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2174 ContentDescription* description = content ? content->description : NULL;
2175 return static_cast<MediaContentDescription*>(description);
2176 }
2177
GetFirstAudioContentDescription(SessionDescription * sdesc)2178 AudioContentDescription* GetFirstAudioContentDescription(
2179 SessionDescription* sdesc) {
2180 return static_cast<AudioContentDescription*>(
2181 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2182 }
2183
GetFirstVideoContentDescription(SessionDescription * sdesc)2184 VideoContentDescription* GetFirstVideoContentDescription(
2185 SessionDescription* sdesc) {
2186 return static_cast<VideoContentDescription*>(
2187 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2188 }
2189
GetFirstDataContentDescription(SessionDescription * sdesc)2190 DataContentDescription* GetFirstDataContentDescription(
2191 SessionDescription* sdesc) {
2192 return static_cast<DataContentDescription*>(
2193 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2194 }
2195
2196 } // namespace cricket
2197