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 "pc/media_session.h"
12
13 #include <algorithm>
14 #include <functional>
15 #include <map>
16 #include <memory>
17 #include <set>
18 #include <unordered_map>
19 #include <utility>
20
21 #include "absl/algorithm/container.h"
22 #include "absl/strings/match.h"
23 #include "absl/types/optional.h"
24 #include "api/crypto_params.h"
25 #include "media/base/h264_profile_level_id.h"
26 #include "media/base/media_constants.h"
27 #include "media/sctp/sctp_transport_internal.h"
28 #include "p2p/base/p2p_constants.h"
29 #include "pc/channel_manager.h"
30 #include "pc/media_protocol_names.h"
31 #include "pc/rtp_media_utils.h"
32 #include "pc/srtp_filter.h"
33 #include "pc/used_ids.h"
34 #include "rtc_base/checks.h"
35 #include "rtc_base/helpers.h"
36 #include "rtc_base/logging.h"
37 #include "rtc_base/third_party/base64/base64.h"
38 #include "rtc_base/unique_id_generator.h"
39
40 namespace {
41
42 using rtc::UniqueRandomIdGenerator;
43 using webrtc::RtpTransceiverDirection;
44
45 const char kInline[] = "inline:";
46
GetSupportedSdesCryptoSuiteNames(void (* func)(const webrtc::CryptoOptions &,std::vector<int> *),const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * names)47 void GetSupportedSdesCryptoSuiteNames(
48 void (*func)(const webrtc::CryptoOptions&, std::vector<int>*),
49 const webrtc::CryptoOptions& crypto_options,
50 std::vector<std::string>* names) {
51 std::vector<int> crypto_suites;
52 func(crypto_options, &crypto_suites);
53 for (const auto crypto : crypto_suites) {
54 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
55 }
56 }
57
RtpExtensionFromCapability(const webrtc::RtpHeaderExtensionCapability & capability)58 webrtc::RtpExtension RtpExtensionFromCapability(
59 const webrtc::RtpHeaderExtensionCapability& capability) {
60 return webrtc::RtpExtension(capability.uri,
61 capability.preferred_id.value_or(1));
62 }
63
RtpHeaderExtensionsFromCapabilities(const std::vector<webrtc::RtpHeaderExtensionCapability> & capabilities)64 cricket::RtpHeaderExtensions RtpHeaderExtensionsFromCapabilities(
65 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities) {
66 cricket::RtpHeaderExtensions exts;
67 for (const auto& capability : capabilities) {
68 exts.push_back(RtpExtensionFromCapability(capability));
69 }
70 return exts;
71 }
72
73 std::vector<webrtc::RtpHeaderExtensionCapability>
UnstoppedRtpHeaderExtensionCapabilities(std::vector<webrtc::RtpHeaderExtensionCapability> capabilities)74 UnstoppedRtpHeaderExtensionCapabilities(
75 std::vector<webrtc::RtpHeaderExtensionCapability> capabilities) {
76 capabilities.erase(
77 std::remove_if(
78 capabilities.begin(), capabilities.end(),
79 [](const webrtc::RtpHeaderExtensionCapability& capability) {
80 return capability.direction == RtpTransceiverDirection::kStopped;
81 }),
82 capabilities.end());
83 return capabilities;
84 }
85
IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability & capability,const cricket::RtpHeaderExtensions & extensions)86 bool IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability& capability,
87 const cricket::RtpHeaderExtensions& extensions) {
88 return std::find_if(extensions.begin(), extensions.end(),
89 [&capability](const webrtc::RtpExtension& extension) {
90 return capability.uri == extension.uri;
91 }) != extensions.end();
92 }
93
UnstoppedOrPresentRtpHeaderExtensions(const std::vector<webrtc::RtpHeaderExtensionCapability> & capabilities,const cricket::RtpHeaderExtensions & unencrypted,const cricket::RtpHeaderExtensions & encrypted)94 cricket::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions(
95 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities,
96 const cricket::RtpHeaderExtensions& unencrypted,
97 const cricket::RtpHeaderExtensions& encrypted) {
98 cricket::RtpHeaderExtensions extensions;
99 for (const auto& capability : capabilities) {
100 if (capability.direction != RtpTransceiverDirection::kStopped ||
101 IsCapabilityPresent(capability, unencrypted) ||
102 IsCapabilityPresent(capability, encrypted)) {
103 extensions.push_back(RtpExtensionFromCapability(capability));
104 }
105 }
106 return extensions;
107 }
108
109 } // namespace
110
111 namespace cricket {
112
113 // RTP Profile names
114 // http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
115 // RFC4585
116 const char kMediaProtocolAvpf[] = "RTP/AVPF";
117 // RFC5124
118 const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
119
120 // We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
121 // but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
122 const char kMediaProtocolSavpf[] = "RTP/SAVPF";
123
124 // Note that the below functions support some protocol strings purely for
125 // legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
126 // and Interoperability.
127
IsDtlsRtp(const std::string & protocol)128 static bool IsDtlsRtp(const std::string& protocol) {
129 // Most-likely values first.
130 return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
131 protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
132 }
133
IsPlainRtp(const std::string & protocol)134 static bool IsPlainRtp(const std::string& protocol) {
135 // Most-likely values first.
136 return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
137 protocol == "RTP/SAVP" || protocol == "RTP/AVP";
138 }
139
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,RtpTransceiverDirection wants)140 static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
141 RtpTransceiverDirection offer,
142 RtpTransceiverDirection wants) {
143 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
144 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
145 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
146 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
147 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
148 offer_send && wants_recv);
149 }
150
IsMediaContentOfType(const ContentInfo * content,MediaType media_type)151 static bool IsMediaContentOfType(const ContentInfo* content,
152 MediaType media_type) {
153 if (!content || !content->media_description()) {
154 return false;
155 }
156 return content->media_description()->type() == media_type;
157 }
158
CreateCryptoParams(int tag,const std::string & cipher,CryptoParams * crypto_out)159 static bool CreateCryptoParams(int tag,
160 const std::string& cipher,
161 CryptoParams* crypto_out) {
162 int key_len;
163 int salt_len;
164 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
165 &key_len, &salt_len)) {
166 return false;
167 }
168
169 int master_key_len = key_len + salt_len;
170 std::string master_key;
171 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
172 return false;
173 }
174
175 RTC_CHECK_EQ(master_key_len, master_key.size());
176 std::string key = rtc::Base64::Encode(master_key);
177
178 crypto_out->tag = tag;
179 crypto_out->cipher_suite = cipher;
180 crypto_out->key_params = kInline;
181 crypto_out->key_params += key;
182 return true;
183 }
184
AddCryptoParams(const std::string & cipher_suite,CryptoParamsVec * cryptos_out)185 static bool AddCryptoParams(const std::string& cipher_suite,
186 CryptoParamsVec* cryptos_out) {
187 int size = static_cast<int>(cryptos_out->size());
188
189 cryptos_out->resize(size + 1);
190 return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
191 }
192
AddMediaCryptos(const CryptoParamsVec & cryptos,MediaContentDescription * media)193 void AddMediaCryptos(const CryptoParamsVec& cryptos,
194 MediaContentDescription* media) {
195 for (const CryptoParams& crypto : cryptos) {
196 media->AddCrypto(crypto);
197 }
198 }
199
CreateMediaCryptos(const std::vector<std::string> & crypto_suites,MediaContentDescription * media)200 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
201 MediaContentDescription* media) {
202 CryptoParamsVec cryptos;
203 for (const std::string& crypto_suite : crypto_suites) {
204 if (!AddCryptoParams(crypto_suite, &cryptos)) {
205 return false;
206 }
207 }
208 AddMediaCryptos(cryptos, media);
209 return true;
210 }
211
GetCryptos(const ContentInfo * content)212 const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
213 if (!content || !content->media_description()) {
214 return nullptr;
215 }
216 return &content->media_description()->cryptos();
217 }
218
FindMatchingCrypto(const CryptoParamsVec & cryptos,const CryptoParams & crypto,CryptoParams * crypto_out)219 bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
220 const CryptoParams& crypto,
221 CryptoParams* crypto_out) {
222 auto it = absl::c_find_if(
223 cryptos, [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
224 if (it == cryptos.end()) {
225 return false;
226 }
227 *crypto_out = *it;
228 return true;
229 }
230
231 // For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
232 // low overhead.
GetSupportedAudioSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)233 void GetSupportedAudioSdesCryptoSuites(
234 const webrtc::CryptoOptions& crypto_options,
235 std::vector<int>* crypto_suites) {
236 if (crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher) {
237 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
238 }
239 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
240 if (crypto_options.srtp.enable_gcm_crypto_suites) {
241 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
242 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
243 }
244 }
245
GetSupportedAudioSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)246 void GetSupportedAudioSdesCryptoSuiteNames(
247 const webrtc::CryptoOptions& crypto_options,
248 std::vector<std::string>* crypto_suite_names) {
249 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
250 crypto_options, crypto_suite_names);
251 }
252
GetSupportedVideoSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)253 void GetSupportedVideoSdesCryptoSuites(
254 const webrtc::CryptoOptions& crypto_options,
255 std::vector<int>* crypto_suites) {
256 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
257 if (crypto_options.srtp.enable_gcm_crypto_suites) {
258 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
259 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
260 }
261 }
262
GetSupportedVideoSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)263 void GetSupportedVideoSdesCryptoSuiteNames(
264 const webrtc::CryptoOptions& crypto_options,
265 std::vector<std::string>* crypto_suite_names) {
266 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
267 crypto_options, crypto_suite_names);
268 }
269
GetSupportedDataSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)270 void GetSupportedDataSdesCryptoSuites(
271 const webrtc::CryptoOptions& crypto_options,
272 std::vector<int>* crypto_suites) {
273 crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
274 if (crypto_options.srtp.enable_gcm_crypto_suites) {
275 crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
276 crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
277 }
278 }
279
GetSupportedDataSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)280 void GetSupportedDataSdesCryptoSuiteNames(
281 const webrtc::CryptoOptions& crypto_options,
282 std::vector<std::string>* crypto_suite_names) {
283 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
284 crypto_options, crypto_suite_names);
285 }
286
287 // Support any GCM cipher (if enabled through options). For video support only
288 // 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
289 // bundle is enabled because it is low overhead.
290 // Pick the crypto in the list that is supported.
SelectCrypto(const MediaContentDescription * offer,bool bundle,const webrtc::CryptoOptions & crypto_options,CryptoParams * crypto_out)291 static bool SelectCrypto(const MediaContentDescription* offer,
292 bool bundle,
293 const webrtc::CryptoOptions& crypto_options,
294 CryptoParams* crypto_out) {
295 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
296 const CryptoParamsVec& cryptos = offer->cryptos();
297
298 for (const CryptoParams& crypto : cryptos) {
299 if ((crypto_options.srtp.enable_gcm_crypto_suites &&
300 rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
301 rtc::CS_AES_CM_128_HMAC_SHA1_80 == crypto.cipher_suite ||
302 (rtc::CS_AES_CM_128_HMAC_SHA1_32 == crypto.cipher_suite && audio &&
303 !bundle && crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher)) {
304 return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
305 }
306 }
307 return false;
308 }
309
310 // Finds all StreamParams of all media types and attach them to stream_params.
GetCurrentStreamParams(const std::vector<const ContentInfo * > & active_local_contents)311 static StreamParamsVec GetCurrentStreamParams(
312 const std::vector<const ContentInfo*>& active_local_contents) {
313 StreamParamsVec stream_params;
314 for (const ContentInfo* content : active_local_contents) {
315 for (const StreamParams& params : content->media_description()->streams()) {
316 stream_params.push_back(params);
317 }
318 }
319 return stream_params;
320 }
321
CreateStreamParamsForNewSenderWithSsrcs(const SenderOptions & sender,const std::string & rtcp_cname,bool include_rtx_streams,bool include_flexfec_stream,UniqueRandomIdGenerator * ssrc_generator)322 static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
323 const SenderOptions& sender,
324 const std::string& rtcp_cname,
325 bool include_rtx_streams,
326 bool include_flexfec_stream,
327 UniqueRandomIdGenerator* ssrc_generator) {
328 StreamParams result;
329 result.id = sender.track_id;
330
331 // TODO(brandtr): Update when we support multistream protection.
332 if (include_flexfec_stream && sender.num_sim_layers > 1) {
333 include_flexfec_stream = false;
334 RTC_LOG(LS_WARNING)
335 << "Our FlexFEC implementation only supports protecting "
336 "a single media streams. This session has multiple "
337 "media streams however, so no FlexFEC SSRC will be generated.";
338 }
339
340 result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
341 include_flexfec_stream, ssrc_generator);
342
343 result.cname = rtcp_cname;
344 result.set_stream_ids(sender.stream_ids);
345
346 return result;
347 }
348
ValidateSimulcastLayers(const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers)349 static bool ValidateSimulcastLayers(
350 const std::vector<RidDescription>& rids,
351 const SimulcastLayerList& simulcast_layers) {
352 return absl::c_all_of(
353 simulcast_layers.GetAllLayers(), [&rids](const SimulcastLayer& layer) {
354 return absl::c_any_of(rids, [&layer](const RidDescription& rid) {
355 return rid.rid == layer.rid;
356 });
357 });
358 }
359
CreateStreamParamsForNewSenderWithRids(const SenderOptions & sender,const std::string & rtcp_cname)360 static StreamParams CreateStreamParamsForNewSenderWithRids(
361 const SenderOptions& sender,
362 const std::string& rtcp_cname) {
363 RTC_DCHECK(!sender.rids.empty());
364 RTC_DCHECK_EQ(sender.num_sim_layers, 0)
365 << "RIDs are the compliant way to indicate simulcast.";
366 RTC_DCHECK(ValidateSimulcastLayers(sender.rids, sender.simulcast_layers));
367 StreamParams result;
368 result.id = sender.track_id;
369 result.cname = rtcp_cname;
370 result.set_stream_ids(sender.stream_ids);
371
372 // More than one rid should be signaled.
373 if (sender.rids.size() > 1) {
374 result.set_rids(sender.rids);
375 }
376
377 return result;
378 }
379
380 // Adds SimulcastDescription if indicated by the media description options.
381 // MediaContentDescription should already be set up with the send rids.
AddSimulcastToMediaDescription(const MediaDescriptionOptions & media_description_options,MediaContentDescription * description)382 static void AddSimulcastToMediaDescription(
383 const MediaDescriptionOptions& media_description_options,
384 MediaContentDescription* description) {
385 RTC_DCHECK(description);
386
387 // Check if we are using RIDs in this scenario.
388 if (absl::c_all_of(description->streams(), [](const StreamParams& params) {
389 return !params.has_rids();
390 })) {
391 return;
392 }
393
394 RTC_DCHECK_EQ(1, description->streams().size())
395 << "RIDs are only supported in Unified Plan semantics.";
396 RTC_DCHECK_EQ(1, media_description_options.sender_options.size());
397 RTC_DCHECK(description->type() == MediaType::MEDIA_TYPE_AUDIO ||
398 description->type() == MediaType::MEDIA_TYPE_VIDEO);
399
400 // One RID or less indicates that simulcast is not needed.
401 if (description->streams()[0].rids().size() <= 1) {
402 return;
403 }
404
405 // Only negotiate the send layers.
406 SimulcastDescription simulcast;
407 simulcast.send_layers() =
408 media_description_options.sender_options[0].simulcast_layers;
409 description->set_simulcast_description(simulcast);
410 }
411
412 // Adds a StreamParams for each SenderOptions in |sender_options| to
413 // content_description.
414 // |current_params| - All currently known StreamParams of any media type.
415 template <class C>
AddStreamParams(const std::vector<SenderOptions> & sender_options,const std::string & rtcp_cname,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * content_description)416 static bool AddStreamParams(
417 const std::vector<SenderOptions>& sender_options,
418 const std::string& rtcp_cname,
419 UniqueRandomIdGenerator* ssrc_generator,
420 StreamParamsVec* current_streams,
421 MediaContentDescriptionImpl<C>* content_description) {
422 // SCTP streams are not negotiated using SDP/ContentDescriptions.
423 if (IsSctpProtocol(content_description->protocol())) {
424 return true;
425 }
426
427 const bool include_rtx_streams =
428 ContainsRtxCodec(content_description->codecs());
429
430 const bool include_flexfec_stream =
431 ContainsFlexfecCodec(content_description->codecs());
432
433 for (const SenderOptions& sender : sender_options) {
434 // groupid is empty for StreamParams generated using
435 // MediaSessionDescriptionFactory.
436 StreamParams* param =
437 GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
438 if (!param) {
439 // This is a new sender.
440 StreamParams stream_param =
441 sender.rids.empty()
442 ?
443 // Signal SSRCs and legacy simulcast (if requested).
444 CreateStreamParamsForNewSenderWithSsrcs(
445 sender, rtcp_cname, include_rtx_streams,
446 include_flexfec_stream, ssrc_generator)
447 :
448 // Signal RIDs and spec-compliant simulcast (if requested).
449 CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
450
451 content_description->AddStream(stream_param);
452
453 // Store the new StreamParams in current_streams.
454 // This is necessary so that we can use the CNAME for other media types.
455 current_streams->push_back(stream_param);
456 } else {
457 // Use existing generated SSRCs/groups, but update the sync_label if
458 // necessary. This may be needed if a MediaStreamTrack was moved from one
459 // MediaStream to another.
460 param->set_stream_ids(sender.stream_ids);
461 content_description->AddStream(*param);
462 }
463 }
464 return true;
465 }
466
467 // Updates the transport infos of the |sdesc| according to the given
468 // |bundle_group|. The transport infos of the content names within the
469 // |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
470 // first content within the |bundle_group|.
UpdateTransportInfoForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)471 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
472 SessionDescription* sdesc) {
473 // The bundle should not be empty.
474 if (!sdesc || !bundle_group.FirstContentName()) {
475 return false;
476 }
477
478 // We should definitely have a transport for the first content.
479 const std::string& selected_content_name = *bundle_group.FirstContentName();
480 const TransportInfo* selected_transport_info =
481 sdesc->GetTransportInfoByName(selected_content_name);
482 if (!selected_transport_info) {
483 return false;
484 }
485
486 // Set the other contents to use the same ICE credentials.
487 const std::string& selected_ufrag =
488 selected_transport_info->description.ice_ufrag;
489 const std::string& selected_pwd =
490 selected_transport_info->description.ice_pwd;
491 ConnectionRole selected_connection_role =
492 selected_transport_info->description.connection_role;
493 for (TransportInfo& transport_info : sdesc->transport_infos()) {
494 if (bundle_group.HasContentName(transport_info.content_name) &&
495 transport_info.content_name != selected_content_name) {
496 transport_info.description.ice_ufrag = selected_ufrag;
497 transport_info.description.ice_pwd = selected_pwd;
498 transport_info.description.connection_role = selected_connection_role;
499 }
500 }
501 return true;
502 }
503
504 // Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
505 // sets it to |cryptos|.
GetCryptosByName(const SessionDescription * sdesc,const std::string & content_name,CryptoParamsVec * cryptos)506 static bool GetCryptosByName(const SessionDescription* sdesc,
507 const std::string& content_name,
508 CryptoParamsVec* cryptos) {
509 if (!sdesc || !cryptos) {
510 return false;
511 }
512 const ContentInfo* content = sdesc->GetContentByName(content_name);
513 if (!content || !content->media_description()) {
514 return false;
515 }
516 *cryptos = content->media_description()->cryptos();
517 return true;
518 }
519
520 // Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
521 // which are not available in |filter|.
PruneCryptos(const CryptoParamsVec & filter,CryptoParamsVec * target_cryptos)522 static void PruneCryptos(const CryptoParamsVec& filter,
523 CryptoParamsVec* target_cryptos) {
524 if (!target_cryptos) {
525 return;
526 }
527
528 target_cryptos->erase(
529 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
530 // Returns true if the |crypto|'s cipher_suite is not
531 // found in |filter|.
532 [&filter](const CryptoParams& crypto) {
533 for (const CryptoParams& entry : filter) {
534 if (entry.cipher_suite == crypto.cipher_suite)
535 return false;
536 }
537 return true;
538 }),
539 target_cryptos->end());
540 }
541
IsRtpContent(SessionDescription * sdesc,const std::string & content_name)542 static bool IsRtpContent(SessionDescription* sdesc,
543 const std::string& content_name) {
544 bool is_rtp = false;
545 ContentInfo* content = sdesc->GetContentByName(content_name);
546 if (content && content->media_description()) {
547 is_rtp = IsRtpProtocol(content->media_description()->protocol());
548 }
549 return is_rtp;
550 }
551
552 // Updates the crypto parameters of the |sdesc| according to the given
553 // |bundle_group|. The crypto parameters of all the contents within the
554 // |bundle_group| should be updated to use the common subset of the
555 // available cryptos.
UpdateCryptoParamsForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)556 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
557 SessionDescription* sdesc) {
558 // The bundle should not be empty.
559 if (!sdesc || !bundle_group.FirstContentName()) {
560 return false;
561 }
562
563 bool common_cryptos_needed = false;
564 // Get the common cryptos.
565 const ContentNames& content_names = bundle_group.content_names();
566 CryptoParamsVec common_cryptos;
567 bool first = true;
568 for (const std::string& content_name : content_names) {
569 if (!IsRtpContent(sdesc, content_name)) {
570 continue;
571 }
572 // The common cryptos are needed if any of the content does not have DTLS
573 // enabled.
574 if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
575 common_cryptos_needed = true;
576 }
577 if (first) {
578 first = false;
579 // Initial the common_cryptos with the first content in the bundle group.
580 if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
581 return false;
582 }
583 if (common_cryptos.empty()) {
584 // If there's no crypto params, we should just return.
585 return true;
586 }
587 } else {
588 CryptoParamsVec cryptos;
589 if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
590 return false;
591 }
592 PruneCryptos(cryptos, &common_cryptos);
593 }
594 }
595
596 if (common_cryptos.empty() && common_cryptos_needed) {
597 return false;
598 }
599
600 // Update to use the common cryptos.
601 for (const std::string& content_name : content_names) {
602 if (!IsRtpContent(sdesc, content_name)) {
603 continue;
604 }
605 ContentInfo* content = sdesc->GetContentByName(content_name);
606 if (IsMediaContent(content)) {
607 MediaContentDescription* media_desc = content->media_description();
608 if (!media_desc) {
609 return false;
610 }
611 media_desc->set_cryptos(common_cryptos);
612 }
613 }
614 return true;
615 }
616
GetActiveContents(const SessionDescription & description,const MediaSessionOptions & session_options)617 static std::vector<const ContentInfo*> GetActiveContents(
618 const SessionDescription& description,
619 const MediaSessionOptions& session_options) {
620 std::vector<const ContentInfo*> active_contents;
621 for (size_t i = 0; i < description.contents().size(); ++i) {
622 RTC_DCHECK_LT(i, session_options.media_description_options.size());
623 const ContentInfo& content = description.contents()[i];
624 const MediaDescriptionOptions& media_options =
625 session_options.media_description_options[i];
626 if (!content.rejected && !media_options.stopped &&
627 content.name == media_options.mid) {
628 active_contents.push_back(&content);
629 }
630 }
631 return active_contents;
632 }
633
634 template <class C>
ContainsRtxCodec(const std::vector<C> & codecs)635 static bool ContainsRtxCodec(const std::vector<C>& codecs) {
636 for (const auto& codec : codecs) {
637 if (IsRtxCodec(codec)) {
638 return true;
639 }
640 }
641 return false;
642 }
643
644 template <class C>
IsRtxCodec(const C & codec)645 static bool IsRtxCodec(const C& codec) {
646 return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
647 }
648
649 template <class C>
ContainsFlexfecCodec(const std::vector<C> & codecs)650 static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
651 for (const auto& codec : codecs) {
652 if (IsFlexfecCodec(codec)) {
653 return true;
654 }
655 }
656 return false;
657 }
658
659 template <class C>
IsFlexfecCodec(const C & codec)660 static bool IsFlexfecCodec(const C& codec) {
661 return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName);
662 }
663
664 // Create a media content to be offered for the given |sender_options|,
665 // according to the given options.rtcp_mux, session_options.is_muc, codecs,
666 // secure_transport, crypto, and current_streams. If we don't currently have
667 // crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
668 // created (according to crypto_suites). The created content is added to the
669 // offer.
CreateContentOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const SecurePolicy & secure_policy,const CryptoParamsVec * current_cryptos,const std::vector<std::string> & crypto_suites,const RtpHeaderExtensions & rtp_extensions,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescription * offer)670 static bool CreateContentOffer(
671 const MediaDescriptionOptions& media_description_options,
672 const MediaSessionOptions& session_options,
673 const SecurePolicy& secure_policy,
674 const CryptoParamsVec* current_cryptos,
675 const std::vector<std::string>& crypto_suites,
676 const RtpHeaderExtensions& rtp_extensions,
677 UniqueRandomIdGenerator* ssrc_generator,
678 StreamParamsVec* current_streams,
679 MediaContentDescription* offer) {
680 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
681 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
682 offer->set_rtcp_reduced_size(true);
683 }
684
685 // Build the vector of header extensions with directions for this
686 // media_description's options.
687 RtpHeaderExtensions extensions;
688 for (auto extension_with_id : rtp_extensions) {
689 for (const auto& extension : media_description_options.header_extensions) {
690 if (extension_with_id.uri == extension.uri) {
691 // TODO(crbug.com/1051821): Configure the extension direction from
692 // the information in the media_description_options extension
693 // capability.
694 extensions.push_back(extension_with_id);
695 }
696 }
697 }
698 offer->set_rtp_header_extensions(extensions);
699
700 AddSimulcastToMediaDescription(media_description_options, offer);
701
702 if (secure_policy != SEC_DISABLED) {
703 if (current_cryptos) {
704 AddMediaCryptos(*current_cryptos, offer);
705 }
706 if (offer->cryptos().empty()) {
707 if (!CreateMediaCryptos(crypto_suites, offer)) {
708 return false;
709 }
710 }
711 }
712
713 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
714 return false;
715 }
716 return true;
717 }
718 template <class C>
CreateMediaContentOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_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,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * offer)719 static bool CreateMediaContentOffer(
720 const MediaDescriptionOptions& media_description_options,
721 const MediaSessionOptions& session_options,
722 const std::vector<C>& codecs,
723 const SecurePolicy& secure_policy,
724 const CryptoParamsVec* current_cryptos,
725 const std::vector<std::string>& crypto_suites,
726 const RtpHeaderExtensions& rtp_extensions,
727 UniqueRandomIdGenerator* ssrc_generator,
728 StreamParamsVec* current_streams,
729 MediaContentDescriptionImpl<C>* offer) {
730 offer->AddCodecs(codecs);
731 if (!AddStreamParams(media_description_options.sender_options,
732 session_options.rtcp_cname, ssrc_generator,
733 current_streams, offer)) {
734 return false;
735 }
736
737 return CreateContentOffer(media_description_options, session_options,
738 secure_policy, current_cryptos, crypto_suites,
739 rtp_extensions, ssrc_generator, current_streams,
740 offer);
741 }
742
743 template <class C>
ReferencedCodecsMatch(const std::vector<C> & codecs1,const int codec1_id,const std::vector<C> & codecs2,const int codec2_id)744 static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
745 const int codec1_id,
746 const std::vector<C>& codecs2,
747 const int codec2_id) {
748 const C* codec1 = FindCodecById(codecs1, codec1_id);
749 const C* codec2 = FindCodecById(codecs2, codec2_id);
750 return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
751 }
752
753 template <class C>
NegotiatePacketization(const C & local_codec,const C & remote_codec,C * negotiated_codec)754 static void NegotiatePacketization(const C& local_codec,
755 const C& remote_codec,
756 C* negotiated_codec) {}
757
758 template <>
NegotiatePacketization(const VideoCodec & local_codec,const VideoCodec & remote_codec,VideoCodec * negotiated_codec)759 void NegotiatePacketization(const VideoCodec& local_codec,
760 const VideoCodec& remote_codec,
761 VideoCodec* negotiated_codec) {
762 negotiated_codec->packetization =
763 VideoCodec::IntersectPacketization(local_codec, remote_codec);
764 }
765
766 template <class C>
NegotiateCodecs(const std::vector<C> & local_codecs,const std::vector<C> & offered_codecs,std::vector<C> * negotiated_codecs,bool keep_offer_order)767 static void NegotiateCodecs(const std::vector<C>& local_codecs,
768 const std::vector<C>& offered_codecs,
769 std::vector<C>* negotiated_codecs,
770 bool keep_offer_order) {
771 for (const C& ours : local_codecs) {
772 C theirs;
773 // Note that we intentionally only find one matching codec for each of our
774 // local codecs, in case the remote offer contains duplicate codecs.
775 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
776 C negotiated = ours;
777 NegotiatePacketization(ours, theirs, &negotiated);
778 negotiated.IntersectFeedbackParams(theirs);
779 if (IsRtxCodec(negotiated)) {
780 const auto apt_it =
781 theirs.params.find(kCodecParamAssociatedPayloadType);
782 // FindMatchingCodec shouldn't return something with no apt value.
783 RTC_DCHECK(apt_it != theirs.params.end());
784 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
785 }
786 if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) {
787 webrtc::H264::GenerateProfileLevelIdForAnswer(
788 ours.params, theirs.params, &negotiated.params);
789 }
790 negotiated.id = theirs.id;
791 negotiated.name = theirs.name;
792 negotiated_codecs->push_back(std::move(negotiated));
793 }
794 }
795 if (keep_offer_order) {
796 // RFC3264: Although the answerer MAY list the formats in their desired
797 // order of preference, it is RECOMMENDED that unless there is a
798 // specific reason, the answerer list formats in the same relative order
799 // they were present in the offer.
800 // This can be skipped when the transceiver has any codec preferences.
801 std::unordered_map<int, int> payload_type_preferences;
802 int preference = static_cast<int>(offered_codecs.size() + 1);
803 for (const C& codec : offered_codecs) {
804 payload_type_preferences[codec.id] = preference--;
805 }
806 absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
807 const C& b) {
808 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
809 });
810 }
811 }
812
813 // Finds a codec in |codecs2| that matches |codec_to_match|, which is
814 // a member of |codecs1|. If |codec_to_match| is an RTX codec, both
815 // the codecs themselves and their associated codecs must match.
816 template <class C>
FindMatchingCodec(const std::vector<C> & codecs1,const std::vector<C> & codecs2,const C & codec_to_match,C * found_codec)817 static bool FindMatchingCodec(const std::vector<C>& codecs1,
818 const std::vector<C>& codecs2,
819 const C& codec_to_match,
820 C* found_codec) {
821 // |codec_to_match| should be a member of |codecs1|, in order to look up RTX
822 // codecs' associated codecs correctly. If not, that's a programming error.
823 RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
824 return &codec == &codec_to_match;
825 }));
826 for (const C& potential_match : codecs2) {
827 if (potential_match.Matches(codec_to_match)) {
828 if (IsRtxCodec(codec_to_match)) {
829 int apt_value_1 = 0;
830 int apt_value_2 = 0;
831 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
832 &apt_value_1) ||
833 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
834 &apt_value_2)) {
835 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
836 continue;
837 }
838 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
839 apt_value_2)) {
840 continue;
841 }
842 }
843 if (found_codec) {
844 *found_codec = potential_match;
845 }
846 return true;
847 }
848 }
849 return false;
850 }
851
852 // Find the codec in |codec_list| that |rtx_codec| is associated with.
853 template <class C>
GetAssociatedCodec(const std::vector<C> & codec_list,const C & rtx_codec)854 static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
855 const C& rtx_codec) {
856 std::string associated_pt_str;
857 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
858 &associated_pt_str)) {
859 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
860 << " is missing an associated payload type.";
861 return nullptr;
862 }
863
864 int associated_pt;
865 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
866 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
867 << " of RTX codec " << rtx_codec.name
868 << " to an integer.";
869 return nullptr;
870 }
871
872 // Find the associated reference codec for the reference RTX codec.
873 const C* associated_codec = FindCodecById(codec_list, associated_pt);
874 if (!associated_codec) {
875 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
876 << associated_pt << " for RTX codec " << rtx_codec.name
877 << ".";
878 }
879 return associated_codec;
880 }
881
882 // Adds all codecs from |reference_codecs| to |offered_codecs| that don't
883 // already exist in |offered_codecs| and ensure the payload types don't
884 // collide.
885 template <class C>
MergeCodecs(const std::vector<C> & reference_codecs,std::vector<C> * offered_codecs,UsedPayloadTypes * used_pltypes)886 static void MergeCodecs(const std::vector<C>& reference_codecs,
887 std::vector<C>* offered_codecs,
888 UsedPayloadTypes* used_pltypes) {
889 // Add all new codecs that are not RTX codecs.
890 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;
895 used_pltypes->FindAndSetIdUsed(&codec);
896 offered_codecs->push_back(codec);
897 }
898 }
899
900 // Add all new RTX codecs.
901 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 const C* associated_codec =
907 GetAssociatedCodec(reference_codecs, rtx_codec);
908 if (!associated_codec) {
909 continue;
910 }
911 // Find a codec in the offered list that matches the reference codec.
912 // Its payload type may be different than the reference codec.
913 C matching_codec;
914 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
915 *associated_codec, &matching_codec)) {
916 RTC_LOG(LS_WARNING)
917 << "Couldn't find matching " << associated_codec->name << " codec.";
918 continue;
919 }
920
921 rtx_codec.params[kCodecParamAssociatedPayloadType] =
922 rtc::ToString(matching_codec.id);
923 used_pltypes->FindAndSetIdUsed(&rtx_codec);
924 offered_codecs->push_back(rtx_codec);
925 }
926 }
927 }
928
929 template <typename Codecs>
MatchCodecPreference(const std::vector<webrtc::RtpCodecCapability> & codec_preferences,const Codecs & codecs)930 static Codecs MatchCodecPreference(
931 const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
932 const Codecs& codecs) {
933 Codecs filtered_codecs;
934 std::set<std::string> kept_codecs_ids;
935 bool want_rtx = false;
936
937 for (const auto& codec_preference : codec_preferences) {
938 auto found_codec = absl::c_find_if(
939 codecs, [&codec_preference](const typename Codecs::value_type& codec) {
940 webrtc::RtpCodecParameters codec_parameters =
941 codec.ToCodecParameters();
942 return codec_parameters.name == codec_preference.name &&
943 codec_parameters.kind == codec_preference.kind &&
944 codec_parameters.num_channels ==
945 codec_preference.num_channels &&
946 codec_parameters.clock_rate == codec_preference.clock_rate &&
947 codec_parameters.parameters == codec_preference.parameters;
948 });
949
950 if (found_codec != codecs.end()) {
951 filtered_codecs.push_back(*found_codec);
952 kept_codecs_ids.insert(std::to_string(found_codec->id));
953 } else if (IsRtxCodec(codec_preference)) {
954 want_rtx = true;
955 }
956 }
957
958 if (want_rtx) {
959 for (const auto& codec : codecs) {
960 if (IsRtxCodec(codec)) {
961 const auto apt =
962 codec.params.find(cricket::kCodecParamAssociatedPayloadType);
963 if (apt != codec.params.end() &&
964 kept_codecs_ids.count(apt->second) > 0) {
965 filtered_codecs.push_back(codec);
966 }
967 }
968 }
969 }
970
971 return filtered_codecs;
972 }
973
FindByUriAndEncryption(const RtpHeaderExtensions & extensions,const webrtc::RtpExtension & ext_to_match,webrtc::RtpExtension * found_extension)974 static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
975 const webrtc::RtpExtension& ext_to_match,
976 webrtc::RtpExtension* found_extension) {
977 auto it = absl::c_find_if(
978 extensions, [&ext_to_match](const webrtc::RtpExtension& extension) {
979 // We assume that all URIs are given in a canonical
980 // format.
981 return extension.uri == ext_to_match.uri &&
982 extension.encrypt == ext_to_match.encrypt;
983 });
984 if (it == extensions.end()) {
985 return false;
986 }
987 if (found_extension) {
988 *found_extension = *it;
989 }
990 return true;
991 }
992
FindByUri(const RtpHeaderExtensions & extensions,const webrtc::RtpExtension & ext_to_match,webrtc::RtpExtension * found_extension)993 static bool FindByUri(const RtpHeaderExtensions& extensions,
994 const webrtc::RtpExtension& ext_to_match,
995 webrtc::RtpExtension* found_extension) {
996 // We assume that all URIs are given in a canonical format.
997 const webrtc::RtpExtension* found =
998 webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
999 ext_to_match.uri);
1000 if (!found) {
1001 return false;
1002 }
1003 if (found_extension) {
1004 *found_extension = *found;
1005 }
1006 return true;
1007 }
1008
FindByUriWithEncryptionPreference(const RtpHeaderExtensions & extensions,absl::string_view uri_to_match,bool encryption_preference,webrtc::RtpExtension * found_extension)1009 static bool FindByUriWithEncryptionPreference(
1010 const RtpHeaderExtensions& extensions,
1011 absl::string_view uri_to_match,
1012 bool encryption_preference,
1013 webrtc::RtpExtension* found_extension) {
1014 const webrtc::RtpExtension* unencrypted_extension = nullptr;
1015 for (const webrtc::RtpExtension& extension : extensions) {
1016 // We assume that all URIs are given in a canonical format.
1017 if (extension.uri == uri_to_match) {
1018 if (!encryption_preference || extension.encrypt) {
1019 if (found_extension) {
1020 *found_extension = extension;
1021 }
1022 return true;
1023 }
1024 unencrypted_extension = &extension;
1025 }
1026 }
1027 if (unencrypted_extension) {
1028 if (found_extension) {
1029 *found_extension = *unencrypted_extension;
1030 }
1031 return true;
1032 }
1033 return false;
1034 }
1035
1036 // Adds all extensions from |reference_extensions| to |offered_extensions| that
1037 // don't already exist in |offered_extensions| and ensure the IDs don't
1038 // collide. If an extension is added, it's also added to |regular_extensions| or
1039 // |encrypted_extensions|, and if the extension is in |regular_extensions| or
1040 // |encrypted_extensions|, its ID is marked as used in |used_ids|.
1041 // |offered_extensions| is for either audio or video while |regular_extensions|
1042 // and |encrypted_extensions| are used for both audio and video. There could be
1043 // overlap between audio extensions and video extensions.
MergeRtpHdrExts(const RtpHeaderExtensions & reference_extensions,RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * regular_extensions,RtpHeaderExtensions * encrypted_extensions,UsedRtpHeaderExtensionIds * used_ids)1044 static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1045 RtpHeaderExtensions* offered_extensions,
1046 RtpHeaderExtensions* regular_extensions,
1047 RtpHeaderExtensions* encrypted_extensions,
1048 UsedRtpHeaderExtensionIds* used_ids) {
1049 for (auto reference_extension : reference_extensions) {
1050 if (!FindByUriAndEncryption(*offered_extensions, reference_extension,
1051 nullptr)) {
1052 webrtc::RtpExtension existing;
1053 if (reference_extension.encrypt) {
1054 if (FindByUriAndEncryption(*encrypted_extensions, reference_extension,
1055 &existing)) {
1056 offered_extensions->push_back(existing);
1057 } else {
1058 used_ids->FindAndSetIdUsed(&reference_extension);
1059 encrypted_extensions->push_back(reference_extension);
1060 offered_extensions->push_back(reference_extension);
1061 }
1062 } else {
1063 if (FindByUriAndEncryption(*regular_extensions, reference_extension,
1064 &existing)) {
1065 offered_extensions->push_back(existing);
1066 } else {
1067 used_ids->FindAndSetIdUsed(&reference_extension);
1068 regular_extensions->push_back(reference_extension);
1069 offered_extensions->push_back(reference_extension);
1070 }
1071 }
1072 }
1073 }
1074 }
1075
AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions * extensions,RtpHeaderExtensions * all_extensions,UsedRtpHeaderExtensionIds * used_ids)1076 static void AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions* extensions,
1077 RtpHeaderExtensions* all_extensions,
1078 UsedRtpHeaderExtensionIds* used_ids) {
1079 RtpHeaderExtensions encrypted_extensions;
1080 for (const webrtc::RtpExtension& extension : *extensions) {
1081 webrtc::RtpExtension existing;
1082 // Don't add encrypted extensions again that were already included in a
1083 // previous offer or regular extensions that are also included as encrypted
1084 // extensions.
1085 if (extension.encrypt ||
1086 !webrtc::RtpExtension::IsEncryptionSupported(extension.uri) ||
1087 (FindByUriWithEncryptionPreference(*extensions, extension.uri, true,
1088 &existing) &&
1089 existing.encrypt)) {
1090 continue;
1091 }
1092
1093 if (FindByUri(*all_extensions, extension, &existing)) {
1094 encrypted_extensions.push_back(existing);
1095 } else {
1096 webrtc::RtpExtension encrypted(extension);
1097 encrypted.encrypt = true;
1098 used_ids->FindAndSetIdUsed(&encrypted);
1099 all_extensions->push_back(encrypted);
1100 encrypted_extensions.push_back(encrypted);
1101 }
1102 }
1103 extensions->insert(extensions->end(), encrypted_extensions.begin(),
1104 encrypted_extensions.end());
1105 }
1106
NegotiateRtpHeaderExtensions(const RtpHeaderExtensions & local_extensions,const RtpHeaderExtensions & offered_extensions,bool enable_encrypted_rtp_header_extensions,RtpHeaderExtensions * negotiated_extensions)1107 static void NegotiateRtpHeaderExtensions(
1108 const RtpHeaderExtensions& local_extensions,
1109 const RtpHeaderExtensions& offered_extensions,
1110 bool enable_encrypted_rtp_header_extensions,
1111 RtpHeaderExtensions* negotiated_extensions) {
1112 // TransportSequenceNumberV2 is not offered by default. The special logic for
1113 // the TransportSequenceNumber extensions works as follows:
1114 // Offer Answer
1115 // V1 V1 if in local_extensions.
1116 // V1 and V2 V2 regardless of local_extensions.
1117 // V2 V2 regardless of local_extensions.
1118 const webrtc::RtpExtension* transport_sequence_number_v2_offer =
1119 webrtc::RtpExtension::FindHeaderExtensionByUri(
1120 offered_extensions,
1121 webrtc::RtpExtension::kTransportSequenceNumberV2Uri);
1122
1123 bool frame_descriptor_in_local = false;
1124 bool dependency_descriptor_in_local = false;
1125 bool abs_capture_time_in_local = false;
1126
1127 for (const webrtc::RtpExtension& ours : local_extensions) {
1128 if (ours.uri == webrtc::RtpExtension::kGenericFrameDescriptorUri00)
1129 frame_descriptor_in_local = true;
1130 else if (ours.uri == webrtc::RtpExtension::kDependencyDescriptorUri)
1131 dependency_descriptor_in_local = true;
1132 else if (ours.uri == webrtc::RtpExtension::kAbsoluteCaptureTimeUri)
1133 abs_capture_time_in_local = true;
1134 webrtc::RtpExtension theirs;
1135 if (FindByUriWithEncryptionPreference(
1136 offered_extensions, ours.uri,
1137 enable_encrypted_rtp_header_extensions, &theirs)) {
1138 if (transport_sequence_number_v2_offer &&
1139 ours.uri == webrtc::RtpExtension::kTransportSequenceNumberUri) {
1140 // Don't respond to
1141 // http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
1142 // if we get an offer including
1143 // http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02
1144 continue;
1145 } else {
1146 // We respond with their RTP header extension id.
1147 negotiated_extensions->push_back(theirs);
1148 }
1149 }
1150 }
1151
1152 if (transport_sequence_number_v2_offer) {
1153 // Respond that we support kTransportSequenceNumberV2Uri.
1154 negotiated_extensions->push_back(*transport_sequence_number_v2_offer);
1155 }
1156
1157 // Frame descriptors support. If the extension is not present locally, but is
1158 // in the offer, we add it to the list.
1159 webrtc::RtpExtension theirs;
1160 if (!dependency_descriptor_in_local &&
1161 FindByUriWithEncryptionPreference(
1162 offered_extensions, webrtc::RtpExtension::kDependencyDescriptorUri,
1163 enable_encrypted_rtp_header_extensions, &theirs)) {
1164 negotiated_extensions->push_back(theirs);
1165 }
1166 if (!frame_descriptor_in_local &&
1167 FindByUriWithEncryptionPreference(
1168 offered_extensions,
1169 webrtc::RtpExtension::kGenericFrameDescriptorUri00,
1170 enable_encrypted_rtp_header_extensions, &theirs)) {
1171 negotiated_extensions->push_back(theirs);
1172 }
1173
1174 // Absolute capture time support. If the extension is not present locally, but
1175 // is in the offer, we add it to the list.
1176 if (!abs_capture_time_in_local &&
1177 FindByUriWithEncryptionPreference(
1178 offered_extensions, webrtc::RtpExtension::kAbsoluteCaptureTimeUri,
1179 enable_encrypted_rtp_header_extensions, &theirs)) {
1180 negotiated_extensions->push_back(theirs);
1181 }
1182 }
1183
StripCNCodecs(AudioCodecs * audio_codecs)1184 static void StripCNCodecs(AudioCodecs* audio_codecs) {
1185 audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
1186 [](const AudioCodec& codec) {
1187 return absl::EqualsIgnoreCase(
1188 codec.name, kComfortNoiseCodecName);
1189 }),
1190 audio_codecs->end());
1191 }
1192
1193 template <class C>
SetCodecsInAnswer(const MediaContentDescriptionImpl<C> * offer,const std::vector<C> & local_codecs,const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * answer)1194 static bool SetCodecsInAnswer(
1195 const MediaContentDescriptionImpl<C>* offer,
1196 const std::vector<C>& local_codecs,
1197 const MediaDescriptionOptions& media_description_options,
1198 const MediaSessionOptions& session_options,
1199 UniqueRandomIdGenerator* ssrc_generator,
1200 StreamParamsVec* current_streams,
1201 MediaContentDescriptionImpl<C>* answer) {
1202 std::vector<C> negotiated_codecs;
1203 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
1204 media_description_options.codec_preferences.empty());
1205 answer->AddCodecs(negotiated_codecs);
1206 answer->set_protocol(offer->protocol());
1207 if (!AddStreamParams(media_description_options.sender_options,
1208 session_options.rtcp_cname, ssrc_generator,
1209 current_streams, answer)) {
1210 return false; // Something went seriously wrong.
1211 }
1212 return true;
1213 }
1214
1215 // Create a media content to be answered for the given |sender_options|
1216 // according to the given session_options.rtcp_mux, session_options.streams,
1217 // codecs, crypto, and current_streams. If we don't currently have crypto (in
1218 // current_cryptos) and it is enabled (in secure_policy), crypto is created
1219 // (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1220 // negotiated with the offer. If the negotiation fails, this method returns
1221 // false. The created content is added to the offer.
CreateMediaContentAnswer(const MediaContentDescription * offer,const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const SecurePolicy & sdes_policy,const CryptoParamsVec * current_cryptos,const RtpHeaderExtensions & local_rtp_extensions,UniqueRandomIdGenerator * ssrc_generator,bool enable_encrypted_rtp_header_extensions,StreamParamsVec * current_streams,bool bundle_enabled,MediaContentDescription * answer)1222 static bool CreateMediaContentAnswer(
1223 const MediaContentDescription* offer,
1224 const MediaDescriptionOptions& media_description_options,
1225 const MediaSessionOptions& session_options,
1226 const SecurePolicy& sdes_policy,
1227 const CryptoParamsVec* current_cryptos,
1228 const RtpHeaderExtensions& local_rtp_extensions,
1229 UniqueRandomIdGenerator* ssrc_generator,
1230 bool enable_encrypted_rtp_header_extensions,
1231 StreamParamsVec* current_streams,
1232 bool bundle_enabled,
1233 MediaContentDescription* answer) {
1234 answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum());
1235 RtpHeaderExtensions negotiated_rtp_extensions;
1236 NegotiateRtpHeaderExtensions(
1237 local_rtp_extensions, offer->rtp_header_extensions(),
1238 enable_encrypted_rtp_header_extensions, &negotiated_rtp_extensions);
1239 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1240
1241 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
1242 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1243 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1244 }
1245
1246 answer->set_remote_estimate(offer->remote_estimate());
1247
1248 if (sdes_policy != SEC_DISABLED) {
1249 CryptoParams crypto;
1250 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1251 &crypto)) {
1252 if (current_cryptos) {
1253 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1254 }
1255 answer->AddCrypto(crypto);
1256 }
1257 }
1258
1259 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
1260 return false;
1261 }
1262
1263 AddSimulcastToMediaDescription(media_description_options, answer);
1264
1265 answer->set_direction(NegotiateRtpTransceiverDirection(
1266 offer->direction(), media_description_options.direction));
1267
1268 return true;
1269 }
1270
IsMediaProtocolSupported(MediaType type,const std::string & protocol,bool secure_transport)1271 static bool IsMediaProtocolSupported(MediaType type,
1272 const std::string& protocol,
1273 bool secure_transport) {
1274 // Since not all applications serialize and deserialize the media protocol,
1275 // we will have to accept |protocol| to be empty.
1276 if (protocol.empty()) {
1277 return true;
1278 }
1279
1280 if (type == MEDIA_TYPE_DATA) {
1281 // Check for SCTP, but also for RTP for RTP-based data channels.
1282 // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1283 if (secure_transport) {
1284 // Most likely scenarios first.
1285 return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1286 IsPlainRtp(protocol);
1287 } else {
1288 return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1289 }
1290 }
1291
1292 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1293 // JSEP specifies.
1294 if (secure_transport) {
1295 // Most likely scenarios first.
1296 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1297 } else {
1298 return IsPlainRtp(protocol);
1299 }
1300 }
1301
SetMediaProtocol(bool secure_transport,MediaContentDescription * desc)1302 static void SetMediaProtocol(bool secure_transport,
1303 MediaContentDescription* desc) {
1304 if (!desc->cryptos().empty())
1305 desc->set_protocol(kMediaProtocolSavpf);
1306 else if (secure_transport)
1307 desc->set_protocol(kMediaProtocolDtlsSavpf);
1308 else
1309 desc->set_protocol(kMediaProtocolAvpf);
1310 }
1311
1312 // Gets the TransportInfo of the given |content_name| from the
1313 // |current_description|. If doesn't exist, returns a new one.
GetTransportDescription(const std::string & content_name,const SessionDescription * current_description)1314 static const TransportDescription* GetTransportDescription(
1315 const std::string& content_name,
1316 const SessionDescription* current_description) {
1317 const TransportDescription* desc = NULL;
1318 if (current_description) {
1319 const TransportInfo* info =
1320 current_description->GetTransportInfoByName(content_name);
1321 if (info) {
1322 desc = &info->description;
1323 }
1324 }
1325 return desc;
1326 }
1327
1328 // Gets the current DTLS state from the transport description.
IsDtlsActive(const ContentInfo * content,const SessionDescription * current_description)1329 static bool IsDtlsActive(const ContentInfo* content,
1330 const SessionDescription* current_description) {
1331 if (!content) {
1332 return false;
1333 }
1334
1335 size_t msection_index = content - ¤t_description->contents()[0];
1336
1337 if (current_description->transport_infos().size() <= msection_index) {
1338 return false;
1339 }
1340
1341 return current_description->transport_infos()[msection_index]
1342 .description.secure();
1343 }
1344
AddAudioSender(const std::string & track_id,const std::vector<std::string> & stream_ids)1345 void MediaDescriptionOptions::AddAudioSender(
1346 const std::string& track_id,
1347 const std::vector<std::string>& stream_ids) {
1348 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
1349 AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1);
1350 }
1351
AddVideoSender(const std::string & track_id,const std::vector<std::string> & stream_ids,const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers,int num_sim_layers)1352 void MediaDescriptionOptions::AddVideoSender(
1353 const std::string& track_id,
1354 const std::vector<std::string>& stream_ids,
1355 const std::vector<RidDescription>& rids,
1356 const SimulcastLayerList& simulcast_layers,
1357 int num_sim_layers) {
1358 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
1359 RTC_DCHECK(rids.empty() || num_sim_layers == 0)
1360 << "RIDs are the compliant way to indicate simulcast.";
1361 RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers));
1362 AddSenderInternal(track_id, stream_ids, rids, simulcast_layers,
1363 num_sim_layers);
1364 }
1365
AddRtpDataChannel(const std::string & track_id,const std::string & stream_id)1366 void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id,
1367 const std::string& stream_id) {
1368 RTC_DCHECK(type == MEDIA_TYPE_DATA);
1369 // TODO(steveanton): Is it the case that RtpDataChannel will never have more
1370 // than one stream?
1371 AddSenderInternal(track_id, {stream_id}, {}, SimulcastLayerList(), 1);
1372 }
1373
AddSenderInternal(const std::string & track_id,const std::vector<std::string> & stream_ids,const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers,int num_sim_layers)1374 void MediaDescriptionOptions::AddSenderInternal(
1375 const std::string& track_id,
1376 const std::vector<std::string>& stream_ids,
1377 const std::vector<RidDescription>& rids,
1378 const SimulcastLayerList& simulcast_layers,
1379 int num_sim_layers) {
1380 // TODO(steveanton): Support any number of stream ids.
1381 RTC_CHECK(stream_ids.size() == 1U);
1382 SenderOptions options;
1383 options.track_id = track_id;
1384 options.stream_ids = stream_ids;
1385 options.simulcast_layers = simulcast_layers;
1386 options.rids = rids;
1387 options.num_sim_layers = num_sim_layers;
1388 sender_options.push_back(options);
1389 }
1390
HasMediaDescription(MediaType type) const1391 bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1392 return absl::c_any_of(
1393 media_description_options,
1394 [type](const MediaDescriptionOptions& t) { return t.type == type; });
1395 }
1396
MediaSessionDescriptionFactory(const TransportDescriptionFactory * transport_desc_factory,rtc::UniqueRandomIdGenerator * ssrc_generator)1397 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1398 const TransportDescriptionFactory* transport_desc_factory,
1399 rtc::UniqueRandomIdGenerator* ssrc_generator)
1400 : ssrc_generator_(ssrc_generator),
1401 transport_desc_factory_(transport_desc_factory) {
1402 RTC_DCHECK(ssrc_generator_);
1403 }
1404
MediaSessionDescriptionFactory(ChannelManager * channel_manager,const TransportDescriptionFactory * transport_desc_factory,rtc::UniqueRandomIdGenerator * ssrc_generator)1405 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1406 ChannelManager* channel_manager,
1407 const TransportDescriptionFactory* transport_desc_factory,
1408 rtc::UniqueRandomIdGenerator* ssrc_generator)
1409 : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
1410 channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1411 channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1412 channel_manager->GetSupportedVideoSendCodecs(&video_send_codecs_);
1413 channel_manager->GetSupportedVideoReceiveCodecs(&video_recv_codecs_);
1414 channel_manager->GetSupportedDataCodecs(&rtp_data_codecs_);
1415 ComputeAudioCodecsIntersectionAndUnion();
1416 ComputeVideoCodecsIntersectionAndUnion();
1417 }
1418
audio_sendrecv_codecs() const1419 const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1420 const {
1421 return audio_sendrecv_codecs_;
1422 }
1423
audio_send_codecs() const1424 const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1425 return audio_send_codecs_;
1426 }
1427
audio_recv_codecs() const1428 const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1429 return audio_recv_codecs_;
1430 }
1431
set_audio_codecs(const AudioCodecs & send_codecs,const AudioCodecs & recv_codecs)1432 void MediaSessionDescriptionFactory::set_audio_codecs(
1433 const AudioCodecs& send_codecs,
1434 const AudioCodecs& recv_codecs) {
1435 audio_send_codecs_ = send_codecs;
1436 audio_recv_codecs_ = recv_codecs;
1437 ComputeAudioCodecsIntersectionAndUnion();
1438 }
1439
video_sendrecv_codecs() const1440 const VideoCodecs& MediaSessionDescriptionFactory::video_sendrecv_codecs()
1441 const {
1442 return video_sendrecv_codecs_;
1443 }
1444
video_send_codecs() const1445 const VideoCodecs& MediaSessionDescriptionFactory::video_send_codecs() const {
1446 return video_send_codecs_;
1447 }
1448
video_recv_codecs() const1449 const VideoCodecs& MediaSessionDescriptionFactory::video_recv_codecs() const {
1450 return video_recv_codecs_;
1451 }
1452
set_video_codecs(const VideoCodecs & send_codecs,const VideoCodecs & recv_codecs)1453 void MediaSessionDescriptionFactory::set_video_codecs(
1454 const VideoCodecs& send_codecs,
1455 const VideoCodecs& recv_codecs) {
1456 video_send_codecs_ = send_codecs;
1457 video_recv_codecs_ = recv_codecs;
1458 ComputeVideoCodecsIntersectionAndUnion();
1459 }
1460
RemoveUnifiedPlanExtensions(RtpHeaderExtensions * extensions)1461 static void RemoveUnifiedPlanExtensions(RtpHeaderExtensions* extensions) {
1462 RTC_DCHECK(extensions);
1463
1464 extensions->erase(
1465 std::remove_if(extensions->begin(), extensions->end(),
1466 [](auto extension) {
1467 return extension.uri == webrtc::RtpExtension::kMidUri ||
1468 extension.uri == webrtc::RtpExtension::kRidUri ||
1469 extension.uri ==
1470 webrtc::RtpExtension::kRepairedRidUri;
1471 }),
1472 extensions->end());
1473 }
1474
1475 RtpHeaderExtensions
filtered_rtp_header_extensions(RtpHeaderExtensions extensions) const1476 MediaSessionDescriptionFactory::filtered_rtp_header_extensions(
1477 RtpHeaderExtensions extensions) const {
1478 if (!is_unified_plan_) {
1479 RemoveUnifiedPlanExtensions(&extensions);
1480 }
1481 return extensions;
1482 }
1483
CreateOffer(const MediaSessionOptions & session_options,const SessionDescription * current_description) const1484 std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
1485 const MediaSessionOptions& session_options,
1486 const SessionDescription* current_description) const {
1487 // Must have options for each existing section.
1488 if (current_description) {
1489 RTC_DCHECK_LE(current_description->contents().size(),
1490 session_options.media_description_options.size());
1491 }
1492
1493 IceCredentialsIterator ice_credentials(
1494 session_options.pooled_ice_credentials);
1495
1496 std::vector<const ContentInfo*> current_active_contents;
1497 if (current_description) {
1498 current_active_contents =
1499 GetActiveContents(*current_description, session_options);
1500 }
1501
1502 StreamParamsVec current_streams =
1503 GetCurrentStreamParams(current_active_contents);
1504
1505 AudioCodecs offer_audio_codecs;
1506 VideoCodecs offer_video_codecs;
1507 RtpDataCodecs offer_rtp_data_codecs;
1508 GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
1509 &offer_video_codecs, &offer_rtp_data_codecs);
1510
1511 if (!session_options.vad_enabled) {
1512 // If application doesn't want CN codecs in offer.
1513 StripCNCodecs(&offer_audio_codecs);
1514 }
1515 AudioVideoRtpHeaderExtensions extensions_with_ids =
1516 GetOfferedRtpHeaderExtensionsWithIds(
1517 current_active_contents, session_options.offer_extmap_allow_mixed,
1518 session_options.media_description_options);
1519
1520 auto offer = std::make_unique<SessionDescription>();
1521
1522 // Iterate through the media description options, matching with existing media
1523 // descriptions in |current_description|.
1524 size_t msection_index = 0;
1525 for (const MediaDescriptionOptions& media_description_options :
1526 session_options.media_description_options) {
1527 const ContentInfo* current_content = nullptr;
1528 if (current_description &&
1529 msection_index < current_description->contents().size()) {
1530 current_content = ¤t_description->contents()[msection_index];
1531 // Media type must match unless this media section is being recycled.
1532 RTC_DCHECK(current_content->name != media_description_options.mid ||
1533 IsMediaContentOfType(current_content,
1534 media_description_options.type));
1535 }
1536 switch (media_description_options.type) {
1537 case MEDIA_TYPE_AUDIO:
1538 if (!AddAudioContentForOffer(media_description_options, session_options,
1539 current_content, current_description,
1540 extensions_with_ids.audio,
1541 offer_audio_codecs, ¤t_streams,
1542 offer.get(), &ice_credentials)) {
1543 return nullptr;
1544 }
1545 break;
1546 case MEDIA_TYPE_VIDEO:
1547 if (!AddVideoContentForOffer(media_description_options, session_options,
1548 current_content, current_description,
1549 extensions_with_ids.video,
1550 offer_video_codecs, ¤t_streams,
1551 offer.get(), &ice_credentials)) {
1552 return nullptr;
1553 }
1554 break;
1555 case MEDIA_TYPE_DATA:
1556 if (!AddDataContentForOffer(media_description_options, session_options,
1557 current_content, current_description,
1558 offer_rtp_data_codecs, ¤t_streams,
1559 offer.get(), &ice_credentials)) {
1560 return nullptr;
1561 }
1562 break;
1563 default:
1564 RTC_NOTREACHED();
1565 }
1566 ++msection_index;
1567 }
1568
1569 // Bundle the contents together, if we've been asked to do so, and update any
1570 // parameters that need to be tweaked for BUNDLE.
1571 if (session_options.bundle_enabled) {
1572 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1573 for (const ContentInfo& content : offer->contents()) {
1574 if (content.rejected) {
1575 continue;
1576 }
1577 // TODO(deadbeef): There are conditions that make bundling two media
1578 // descriptions together illegal. For example, they use the same payload
1579 // type to represent different codecs, or same IDs for different header
1580 // extensions. We need to detect this and not try to bundle those media
1581 // descriptions together.
1582 offer_bundle.AddContentName(content.name);
1583 }
1584 if (!offer_bundle.content_names().empty()) {
1585 offer->AddGroup(offer_bundle);
1586 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1587 RTC_LOG(LS_ERROR)
1588 << "CreateOffer failed to UpdateTransportInfoForBundle.";
1589 return nullptr;
1590 }
1591 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1592 RTC_LOG(LS_ERROR)
1593 << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1594 return nullptr;
1595 }
1596 }
1597 }
1598
1599 // The following determines how to signal MSIDs to ensure compatibility with
1600 // older endpoints (in particular, older Plan B endpoints).
1601 if (is_unified_plan_) {
1602 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1603 // Plan answerers will look at a=msid and Plan B answerers will look at the
1604 // a=ssrc MSID line.
1605 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1606 cricket::kMsidSignalingSsrcAttribute);
1607 } else {
1608 // Plan B always signals MSID using a=ssrc lines.
1609 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1610 }
1611
1612 offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
1613
1614 return offer;
1615 }
1616
1617 std::unique_ptr<SessionDescription>
CreateAnswer(const SessionDescription * offer,const MediaSessionOptions & session_options,const SessionDescription * current_description) const1618 MediaSessionDescriptionFactory::CreateAnswer(
1619 const SessionDescription* offer,
1620 const MediaSessionOptions& session_options,
1621 const SessionDescription* current_description) const {
1622 if (!offer) {
1623 return nullptr;
1624 }
1625
1626 // Must have options for exactly as many sections as in the offer.
1627 RTC_DCHECK_EQ(offer->contents().size(),
1628 session_options.media_description_options.size());
1629
1630 IceCredentialsIterator ice_credentials(
1631 session_options.pooled_ice_credentials);
1632
1633 std::vector<const ContentInfo*> current_active_contents;
1634 if (current_description) {
1635 current_active_contents =
1636 GetActiveContents(*current_description, session_options);
1637 }
1638
1639 StreamParamsVec current_streams =
1640 GetCurrentStreamParams(current_active_contents);
1641
1642 // Get list of all possible codecs that respects existing payload type
1643 // mappings and uses a single payload type space.
1644 //
1645 // Note that these lists may be further filtered for each m= section; this
1646 // step is done just to establish the payload type mappings shared by all
1647 // sections.
1648 AudioCodecs answer_audio_codecs;
1649 VideoCodecs answer_video_codecs;
1650 RtpDataCodecs answer_rtp_data_codecs;
1651 GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
1652 &answer_video_codecs, &answer_rtp_data_codecs);
1653
1654 if (!session_options.vad_enabled) {
1655 // If application doesn't want CN codecs in answer.
1656 StripCNCodecs(&answer_audio_codecs);
1657 }
1658
1659 auto answer = std::make_unique<SessionDescription>();
1660
1661 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1662 // group in the answer with the appropriate content names.
1663 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1664 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1665 // Transport info shared by the bundle group.
1666 std::unique_ptr<TransportInfo> bundle_transport;
1667
1668 answer->set_extmap_allow_mixed(offer->extmap_allow_mixed());
1669
1670 // Iterate through the media description options, matching with existing
1671 // media descriptions in |current_description|.
1672 size_t msection_index = 0;
1673 for (const MediaDescriptionOptions& media_description_options :
1674 session_options.media_description_options) {
1675 const ContentInfo* offer_content = &offer->contents()[msection_index];
1676 // Media types and MIDs must match between the remote offer and the
1677 // MediaDescriptionOptions.
1678 RTC_DCHECK(
1679 IsMediaContentOfType(offer_content, media_description_options.type));
1680 RTC_DCHECK(media_description_options.mid == offer_content->name);
1681 const ContentInfo* current_content = nullptr;
1682 if (current_description &&
1683 msection_index < current_description->contents().size()) {
1684 current_content = ¤t_description->contents()[msection_index];
1685 }
1686 RtpHeaderExtensions header_extensions = RtpHeaderExtensionsFromCapabilities(
1687 UnstoppedRtpHeaderExtensionCapabilities(
1688 media_description_options.header_extensions));
1689 switch (media_description_options.type) {
1690 case MEDIA_TYPE_AUDIO:
1691 if (!AddAudioContentForAnswer(
1692 media_description_options, session_options, offer_content,
1693 offer, current_content, current_description,
1694 bundle_transport.get(), answer_audio_codecs, header_extensions,
1695 ¤t_streams, answer.get(), &ice_credentials)) {
1696 return nullptr;
1697 }
1698 break;
1699 case MEDIA_TYPE_VIDEO:
1700 if (!AddVideoContentForAnswer(
1701 media_description_options, session_options, offer_content,
1702 offer, current_content, current_description,
1703 bundle_transport.get(), answer_video_codecs, header_extensions,
1704 ¤t_streams, answer.get(), &ice_credentials)) {
1705 return nullptr;
1706 }
1707 break;
1708 case MEDIA_TYPE_DATA:
1709 if (!AddDataContentForAnswer(
1710 media_description_options, session_options, offer_content,
1711 offer, current_content, current_description,
1712 bundle_transport.get(), answer_rtp_data_codecs,
1713 ¤t_streams, answer.get(), &ice_credentials)) {
1714 return nullptr;
1715 }
1716 break;
1717 default:
1718 RTC_NOTREACHED();
1719 }
1720 ++msection_index;
1721 // See if we can add the newly generated m= section to the BUNDLE group in
1722 // the answer.
1723 ContentInfo& added = answer->contents().back();
1724 if (!added.rejected && session_options.bundle_enabled && offer_bundle &&
1725 offer_bundle->HasContentName(added.name)) {
1726 answer_bundle.AddContentName(added.name);
1727 bundle_transport.reset(
1728 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
1729 }
1730 }
1731
1732 // If a BUNDLE group was offered, put a BUNDLE group in the answer even if
1733 // it's empty. RFC5888 says:
1734 //
1735 // A SIP entity that receives an offer that contains an "a=group" line
1736 // with semantics that are understood MUST return an answer that
1737 // contains an "a=group" line with the same semantics.
1738 if (offer_bundle) {
1739 answer->AddGroup(answer_bundle);
1740 }
1741
1742 if (answer_bundle.FirstContentName()) {
1743 // Share the same ICE credentials and crypto params across all contents,
1744 // as BUNDLE requires.
1745 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1746 RTC_LOG(LS_ERROR)
1747 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1748 return NULL;
1749 }
1750
1751 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1752 RTC_LOG(LS_ERROR)
1753 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1754 return NULL;
1755 }
1756 }
1757
1758 // The following determines how to signal MSIDs to ensure compatibility with
1759 // older endpoints (in particular, older Plan B endpoints).
1760 if (is_unified_plan_) {
1761 // Unified Plan needs to look at what the offer included to find the most
1762 // compatible answer.
1763 if (offer->msid_signaling() == 0) {
1764 // We end up here in one of three cases:
1765 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1766 // matter what we pick here.
1767 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1768 // it also doesn't matter what we pick here.
1769 // 3. Media that's either sendonly or inactive from the remote endpoint.
1770 // We don't have any information to say whether the endpoint is Plan B
1771 // or Unified Plan, so be conservative and send both.
1772 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1773 cricket::kMsidSignalingSsrcAttribute);
1774 } else if (offer->msid_signaling() ==
1775 (cricket::kMsidSignalingMediaSection |
1776 cricket::kMsidSignalingSsrcAttribute)) {
1777 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1778 // probably talking to a Unified Plan endpoint so respond with just
1779 // a=msid.
1780 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1781 } else {
1782 // Otherwise, it's clear which method the offerer is using so repeat that
1783 // back to them.
1784 answer->set_msid_signaling(offer->msid_signaling());
1785 }
1786 } else {
1787 // Plan B always signals MSID using a=ssrc lines.
1788 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1789 }
1790
1791 return answer;
1792 }
1793
GetAudioCodecsForOffer(const RtpTransceiverDirection & direction) const1794 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1795 const RtpTransceiverDirection& direction) const {
1796 switch (direction) {
1797 // If stream is inactive - generate list as if sendrecv.
1798 case RtpTransceiverDirection::kSendRecv:
1799 case RtpTransceiverDirection::kInactive:
1800 return audio_sendrecv_codecs_;
1801 case RtpTransceiverDirection::kSendOnly:
1802 return audio_send_codecs_;
1803 case RtpTransceiverDirection::kRecvOnly:
1804 return audio_recv_codecs_;
1805 case RtpTransceiverDirection::kStopped:
1806 RTC_NOTREACHED();
1807 return audio_sendrecv_codecs_;
1808 }
1809 }
1810
GetAudioCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const1811 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1812 const RtpTransceiverDirection& offer,
1813 const RtpTransceiverDirection& answer) const {
1814 switch (answer) {
1815 // For inactive and sendrecv answers, generate lists as if we were to accept
1816 // the offer's direction. See RFC 3264 Section 6.1.
1817 case RtpTransceiverDirection::kSendRecv:
1818 case RtpTransceiverDirection::kInactive:
1819 return GetAudioCodecsForOffer(
1820 webrtc::RtpTransceiverDirectionReversed(offer));
1821 case RtpTransceiverDirection::kSendOnly:
1822 return audio_send_codecs_;
1823 case RtpTransceiverDirection::kRecvOnly:
1824 return audio_recv_codecs_;
1825 case RtpTransceiverDirection::kStopped:
1826 RTC_NOTREACHED();
1827 return audio_sendrecv_codecs_;
1828 }
1829 }
1830
GetVideoCodecsForOffer(const RtpTransceiverDirection & direction) const1831 const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForOffer(
1832 const RtpTransceiverDirection& direction) const {
1833 switch (direction) {
1834 // If stream is inactive - generate list as if sendrecv.
1835 case RtpTransceiverDirection::kSendRecv:
1836 case RtpTransceiverDirection::kInactive:
1837 return video_sendrecv_codecs_;
1838 case RtpTransceiverDirection::kSendOnly:
1839 return video_send_codecs_;
1840 case RtpTransceiverDirection::kRecvOnly:
1841 return video_recv_codecs_;
1842 case RtpTransceiverDirection::kStopped:
1843 RTC_NOTREACHED();
1844 return video_sendrecv_codecs_;
1845 }
1846 }
1847
GetVideoCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const1848 const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForAnswer(
1849 const RtpTransceiverDirection& offer,
1850 const RtpTransceiverDirection& answer) const {
1851 switch (answer) {
1852 // For inactive and sendrecv answers, generate lists as if we were to accept
1853 // the offer's direction. See RFC 3264 Section 6.1.
1854 case RtpTransceiverDirection::kSendRecv:
1855 case RtpTransceiverDirection::kInactive:
1856 return GetVideoCodecsForOffer(
1857 webrtc::RtpTransceiverDirectionReversed(offer));
1858 case RtpTransceiverDirection::kSendOnly:
1859 return video_send_codecs_;
1860 case RtpTransceiverDirection::kRecvOnly:
1861 return video_recv_codecs_;
1862 case RtpTransceiverDirection::kStopped:
1863 RTC_NOTREACHED();
1864 return video_sendrecv_codecs_;
1865 }
1866 }
1867
MergeCodecsFromDescription(const std::vector<const ContentInfo * > & current_active_contents,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,RtpDataCodecs * rtp_data_codecs,UsedPayloadTypes * used_pltypes)1868 void MergeCodecsFromDescription(
1869 const std::vector<const ContentInfo*>& current_active_contents,
1870 AudioCodecs* audio_codecs,
1871 VideoCodecs* video_codecs,
1872 RtpDataCodecs* rtp_data_codecs,
1873 UsedPayloadTypes* used_pltypes) {
1874 for (const ContentInfo* content : current_active_contents) {
1875 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
1876 const AudioContentDescription* audio =
1877 content->media_description()->as_audio();
1878 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes);
1879 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
1880 const VideoContentDescription* video =
1881 content->media_description()->as_video();
1882 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
1883 } else if (IsMediaContentOfType(content, MEDIA_TYPE_DATA)) {
1884 const RtpDataContentDescription* data =
1885 content->media_description()->as_rtp_data();
1886 if (data) {
1887 // Only relevant for RTP datachannels
1888 MergeCodecs<RtpDataCodec>(data->codecs(), rtp_data_codecs,
1889 used_pltypes);
1890 }
1891 }
1892 }
1893 }
1894
1895 // Getting codecs for an offer involves these steps:
1896 //
1897 // 1. Construct payload type -> codec mappings for current description.
1898 // 2. Add any reference codecs that weren't already present
1899 // 3. For each individual media description (m= section), filter codecs based
1900 // on the directional attribute (happens in another method).
GetCodecsForOffer(const std::vector<const ContentInfo * > & current_active_contents,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,RtpDataCodecs * rtp_data_codecs) const1901 void MediaSessionDescriptionFactory::GetCodecsForOffer(
1902 const std::vector<const ContentInfo*>& current_active_contents,
1903 AudioCodecs* audio_codecs,
1904 VideoCodecs* video_codecs,
1905 RtpDataCodecs* rtp_data_codecs) const {
1906 // First - get all codecs from the current description if the media type
1907 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1908 // new media type is added.
1909 UsedPayloadTypes used_pltypes;
1910 MergeCodecsFromDescription(current_active_contents, audio_codecs,
1911 video_codecs, rtp_data_codecs, &used_pltypes);
1912
1913 // Add our codecs that are not in the current description.
1914 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
1915 MergeCodecs<VideoCodec>(all_video_codecs_, video_codecs, &used_pltypes);
1916 MergeCodecs<DataCodec>(rtp_data_codecs_, rtp_data_codecs, &used_pltypes);
1917 }
1918
1919 // Getting codecs for an answer involves these steps:
1920 //
1921 // 1. Construct payload type -> codec mappings for current description.
1922 // 2. Add any codecs from the offer that weren't already present.
1923 // 3. Add any remaining codecs that weren't already present.
1924 // 4. For each individual media description (m= section), filter codecs based
1925 // on the directional attribute (happens in another method).
GetCodecsForAnswer(const std::vector<const ContentInfo * > & current_active_contents,const SessionDescription & remote_offer,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,RtpDataCodecs * rtp_data_codecs) const1926 void MediaSessionDescriptionFactory::GetCodecsForAnswer(
1927 const std::vector<const ContentInfo*>& current_active_contents,
1928 const SessionDescription& remote_offer,
1929 AudioCodecs* audio_codecs,
1930 VideoCodecs* video_codecs,
1931 RtpDataCodecs* rtp_data_codecs) const {
1932 // First - get all codecs from the current description if the media type
1933 // is used. Add them to |used_pltypes| so the payload type is not reused if a
1934 // new media type is added.
1935 UsedPayloadTypes used_pltypes;
1936 MergeCodecsFromDescription(current_active_contents, audio_codecs,
1937 video_codecs, rtp_data_codecs, &used_pltypes);
1938
1939 // Second - filter out codecs that we don't support at all and should ignore.
1940 AudioCodecs filtered_offered_audio_codecs;
1941 VideoCodecs filtered_offered_video_codecs;
1942 RtpDataCodecs filtered_offered_rtp_data_codecs;
1943 for (const ContentInfo& content : remote_offer.contents()) {
1944 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
1945 const AudioContentDescription* audio =
1946 content.media_description()->as_audio();
1947 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
1948 if (!FindMatchingCodec<AudioCodec>(audio->codecs(),
1949 filtered_offered_audio_codecs,
1950 offered_audio_codec, nullptr) &&
1951 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
1952 offered_audio_codec, nullptr)) {
1953 filtered_offered_audio_codecs.push_back(offered_audio_codec);
1954 }
1955 }
1956 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
1957 const VideoContentDescription* video =
1958 content.media_description()->as_video();
1959 for (const VideoCodec& offered_video_codec : video->codecs()) {
1960 if (!FindMatchingCodec<VideoCodec>(video->codecs(),
1961 filtered_offered_video_codecs,
1962 offered_video_codec, nullptr) &&
1963 FindMatchingCodec<VideoCodec>(video->codecs(), all_video_codecs_,
1964 offered_video_codec, nullptr)) {
1965 filtered_offered_video_codecs.push_back(offered_video_codec);
1966 }
1967 }
1968 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
1969 const RtpDataContentDescription* data =
1970 content.media_description()->as_rtp_data();
1971 if (data) {
1972 // RTP data. This part is inactive for SCTP data.
1973 for (const RtpDataCodec& offered_rtp_data_codec : data->codecs()) {
1974 if (!FindMatchingCodec<RtpDataCodec>(
1975 data->codecs(), filtered_offered_rtp_data_codecs,
1976 offered_rtp_data_codec, nullptr) &&
1977 FindMatchingCodec<RtpDataCodec>(data->codecs(), rtp_data_codecs_,
1978 offered_rtp_data_codec,
1979 nullptr)) {
1980 filtered_offered_rtp_data_codecs.push_back(offered_rtp_data_codec);
1981 }
1982 }
1983 }
1984 }
1985 }
1986
1987 // Add codecs that are not in the current description but were in
1988 // |remote_offer|.
1989 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
1990 &used_pltypes);
1991 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
1992 &used_pltypes);
1993 MergeCodecs<DataCodec>(filtered_offered_rtp_data_codecs, rtp_data_codecs,
1994 &used_pltypes);
1995 }
1996
1997 MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions
GetOfferedRtpHeaderExtensionsWithIds(const std::vector<const ContentInfo * > & current_active_contents,bool extmap_allow_mixed,const std::vector<MediaDescriptionOptions> & media_description_options) const1998 MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds(
1999 const std::vector<const ContentInfo*>& current_active_contents,
2000 bool extmap_allow_mixed,
2001 const std::vector<MediaDescriptionOptions>& media_description_options)
2002 const {
2003 // All header extensions allocated from the same range to avoid potential
2004 // issues when using BUNDLE.
2005
2006 // Strictly speaking the SDP attribute extmap_allow_mixed signals that the
2007 // receiver supports an RTP stream where one- and two-byte RTP header
2008 // extensions are mixed. For backwards compatibility reasons it's used in
2009 // WebRTC to signal that two-byte RTP header extensions are supported.
2010 UsedRtpHeaderExtensionIds used_ids(
2011 extmap_allow_mixed ? UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed
2012 : UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly);
2013 RtpHeaderExtensions all_regular_extensions;
2014 RtpHeaderExtensions all_encrypted_extensions;
2015
2016 AudioVideoRtpHeaderExtensions offered_extensions;
2017 // First - get all extensions from the current description if the media type
2018 // is used.
2019 // Add them to |used_ids| so the local ids are not reused if a new media
2020 // type is added.
2021 for (const ContentInfo* content : current_active_contents) {
2022 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
2023 const AudioContentDescription* audio =
2024 content->media_description()->as_audio();
2025 MergeRtpHdrExts(audio->rtp_header_extensions(), &offered_extensions.audio,
2026 &all_regular_extensions, &all_encrypted_extensions,
2027 &used_ids);
2028 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
2029 const VideoContentDescription* video =
2030 content->media_description()->as_video();
2031 MergeRtpHdrExts(video->rtp_header_extensions(), &offered_extensions.video,
2032 &all_regular_extensions, &all_encrypted_extensions,
2033 &used_ids);
2034 }
2035 }
2036
2037 // Add all encountered header extensions in the media description options that
2038 // are not in the current description.
2039
2040 for (const auto& entry : media_description_options) {
2041 RtpHeaderExtensions filtered_extensions =
2042 filtered_rtp_header_extensions(UnstoppedOrPresentRtpHeaderExtensions(
2043 entry.header_extensions, all_regular_extensions,
2044 all_encrypted_extensions));
2045 if (entry.type == MEDIA_TYPE_AUDIO)
2046 MergeRtpHdrExts(filtered_extensions, &offered_extensions.audio,
2047 &all_regular_extensions, &all_encrypted_extensions,
2048 &used_ids);
2049 else if (entry.type == MEDIA_TYPE_VIDEO)
2050 MergeRtpHdrExts(filtered_extensions, &offered_extensions.video,
2051 &all_regular_extensions, &all_encrypted_extensions,
2052 &used_ids);
2053 }
2054 // TODO(jbauch): Support adding encrypted header extensions to existing
2055 // sessions.
2056 if (enable_encrypted_rtp_header_extensions_ &&
2057 current_active_contents.empty()) {
2058 AddEncryptedVersionsOfHdrExts(&offered_extensions.audio,
2059 &all_encrypted_extensions, &used_ids);
2060 AddEncryptedVersionsOfHdrExts(&offered_extensions.video,
2061 &all_encrypted_extensions, &used_ids);
2062 }
2063 return offered_extensions;
2064 }
2065
AddTransportOffer(const std::string & content_name,const TransportOptions & transport_options,const SessionDescription * current_desc,SessionDescription * offer_desc,IceCredentialsIterator * ice_credentials) const2066 bool MediaSessionDescriptionFactory::AddTransportOffer(
2067 const std::string& content_name,
2068 const TransportOptions& transport_options,
2069 const SessionDescription* current_desc,
2070 SessionDescription* offer_desc,
2071 IceCredentialsIterator* ice_credentials) const {
2072 if (!transport_desc_factory_)
2073 return false;
2074 const TransportDescription* current_tdesc =
2075 GetTransportDescription(content_name, current_desc);
2076 std::unique_ptr<TransportDescription> new_tdesc(
2077 transport_desc_factory_->CreateOffer(transport_options, current_tdesc,
2078 ice_credentials));
2079 if (!new_tdesc) {
2080 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
2081 << content_name;
2082 }
2083 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc));
2084 return true;
2085 }
2086
2087 std::unique_ptr<TransportDescription>
CreateTransportAnswer(const std::string & content_name,const SessionDescription * offer_desc,const TransportOptions & transport_options,const SessionDescription * current_desc,bool require_transport_attributes,IceCredentialsIterator * ice_credentials) const2088 MediaSessionDescriptionFactory::CreateTransportAnswer(
2089 const std::string& content_name,
2090 const SessionDescription* offer_desc,
2091 const TransportOptions& transport_options,
2092 const SessionDescription* current_desc,
2093 bool require_transport_attributes,
2094 IceCredentialsIterator* ice_credentials) const {
2095 if (!transport_desc_factory_)
2096 return NULL;
2097 const TransportDescription* offer_tdesc =
2098 GetTransportDescription(content_name, offer_desc);
2099 const TransportDescription* current_tdesc =
2100 GetTransportDescription(content_name, current_desc);
2101 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
2102 require_transport_attributes,
2103 current_tdesc, ice_credentials);
2104 }
2105
AddTransportAnswer(const std::string & content_name,const TransportDescription & transport_desc,SessionDescription * answer_desc) const2106 bool MediaSessionDescriptionFactory::AddTransportAnswer(
2107 const std::string& content_name,
2108 const TransportDescription& transport_desc,
2109 SessionDescription* answer_desc) const {
2110 answer_desc->AddTransportInfo(TransportInfo(content_name, transport_desc));
2111 return true;
2112 }
2113
2114 // |audio_codecs| = set of all possible codecs that can be used, with correct
2115 // payload type mappings
2116 //
2117 // |supported_audio_codecs| = set of codecs that are supported for the direction
2118 // of this m= section
2119 //
2120 // acd->codecs() = set of previously negotiated codecs for this m= section
2121 //
2122 // The payload types should come from audio_codecs, but the order should come
2123 // from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2124 // change existing codec priority, and that new codecs are added with the right
2125 // priority.
AddAudioContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpHeaderExtensions & audio_rtp_extensions,const AudioCodecs & audio_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2126 bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
2127 const MediaDescriptionOptions& media_description_options,
2128 const MediaSessionOptions& session_options,
2129 const ContentInfo* current_content,
2130 const SessionDescription* current_description,
2131 const RtpHeaderExtensions& audio_rtp_extensions,
2132 const AudioCodecs& audio_codecs,
2133 StreamParamsVec* current_streams,
2134 SessionDescription* desc,
2135 IceCredentialsIterator* ice_credentials) const {
2136 // Filter audio_codecs (which includes all codecs, with correctly remapped
2137 // payload types) based on transceiver direction.
2138 const AudioCodecs& supported_audio_codecs =
2139 GetAudioCodecsForOffer(media_description_options.direction);
2140
2141 AudioCodecs filtered_codecs;
2142
2143 if (!media_description_options.codec_preferences.empty()) {
2144 // Add the codecs from the current transceiver's codec preferences.
2145 // They override any existing codecs from previous negotiations.
2146 filtered_codecs = MatchCodecPreference(
2147 media_description_options.codec_preferences, supported_audio_codecs);
2148 } else {
2149 // Add the codecs from current content if it exists and is not rejected nor
2150 // recycled.
2151 if (current_content && !current_content->rejected &&
2152 current_content->name == media_description_options.mid) {
2153 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2154 const AudioContentDescription* acd =
2155 current_content->media_description()->as_audio();
2156 for (const AudioCodec& codec : acd->codecs()) {
2157 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2158 nullptr)) {
2159 filtered_codecs.push_back(codec);
2160 }
2161 }
2162 }
2163 // Add other supported audio codecs.
2164 AudioCodec found_codec;
2165 for (const AudioCodec& codec : supported_audio_codecs) {
2166 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2167 codec, &found_codec) &&
2168 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2169 filtered_codecs, codec, nullptr)) {
2170 // Use the |found_codec| from |audio_codecs| because it has the
2171 // correctly mapped payload type.
2172 filtered_codecs.push_back(found_codec);
2173 }
2174 }
2175 }
2176
2177 cricket::SecurePolicy sdes_policy =
2178 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2179 : secure();
2180
2181 auto audio = std::make_unique<AudioContentDescription>();
2182 std::vector<std::string> crypto_suites;
2183 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
2184 &crypto_suites);
2185 if (!CreateMediaContentOffer(media_description_options, session_options,
2186 filtered_codecs, sdes_policy,
2187 GetCryptos(current_content), crypto_suites,
2188 audio_rtp_extensions, ssrc_generator_,
2189 current_streams, audio.get())) {
2190 return false;
2191 }
2192
2193 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2194 SetMediaProtocol(secure_transport, audio.get());
2195
2196 audio->set_direction(media_description_options.direction);
2197
2198 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2199 media_description_options.stopped, std::move(audio));
2200 if (!AddTransportOffer(media_description_options.mid,
2201 media_description_options.transport_options,
2202 current_description, desc, ice_credentials)) {
2203 return false;
2204 }
2205
2206 return true;
2207 }
2208
2209 // TODO(kron): This function is very similar to AddAudioContentForOffer.
2210 // Refactor to reuse shared code.
AddVideoContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpHeaderExtensions & video_rtp_extensions,const VideoCodecs & video_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2211 bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
2212 const MediaDescriptionOptions& media_description_options,
2213 const MediaSessionOptions& session_options,
2214 const ContentInfo* current_content,
2215 const SessionDescription* current_description,
2216 const RtpHeaderExtensions& video_rtp_extensions,
2217 const VideoCodecs& video_codecs,
2218 StreamParamsVec* current_streams,
2219 SessionDescription* desc,
2220 IceCredentialsIterator* ice_credentials) const {
2221 // Filter video_codecs (which includes all codecs, with correctly remapped
2222 // payload types) based on transceiver direction.
2223 const VideoCodecs& supported_video_codecs =
2224 GetVideoCodecsForOffer(media_description_options.direction);
2225
2226 VideoCodecs filtered_codecs;
2227
2228 if (!media_description_options.codec_preferences.empty()) {
2229 // Add the codecs from the current transceiver's codec preferences.
2230 // They override any existing codecs from previous negotiations.
2231 filtered_codecs = MatchCodecPreference(
2232 media_description_options.codec_preferences, supported_video_codecs);
2233 } else {
2234 // Add the codecs from current content if it exists and is not rejected nor
2235 // recycled.
2236 if (current_content && !current_content->rejected &&
2237 current_content->name == media_description_options.mid) {
2238 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2239 const VideoContentDescription* vcd =
2240 current_content->media_description()->as_video();
2241 for (const VideoCodec& codec : vcd->codecs()) {
2242 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2243 nullptr)) {
2244 filtered_codecs.push_back(codec);
2245 }
2246 }
2247 }
2248 // Add other supported video codecs.
2249 VideoCodec found_codec;
2250 for (const VideoCodec& codec : supported_video_codecs) {
2251 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
2252 codec, &found_codec) &&
2253 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
2254 filtered_codecs, codec, nullptr)) {
2255 // Use the |found_codec| from |video_codecs| because it has the
2256 // correctly mapped payload type.
2257 filtered_codecs.push_back(found_codec);
2258 }
2259 }
2260 }
2261
2262 if (session_options.raw_packetization_for_video) {
2263 for (VideoCodec& codec : filtered_codecs) {
2264 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2265 codec.packetization = kPacketizationParamRaw;
2266 }
2267 }
2268 }
2269
2270 cricket::SecurePolicy sdes_policy =
2271 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2272 : secure();
2273 auto video = std::make_unique<VideoContentDescription>();
2274 std::vector<std::string> crypto_suites;
2275 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
2276 &crypto_suites);
2277 if (!CreateMediaContentOffer(media_description_options, session_options,
2278 filtered_codecs, sdes_policy,
2279 GetCryptos(current_content), crypto_suites,
2280 video_rtp_extensions, ssrc_generator_,
2281 current_streams, video.get())) {
2282 return false;
2283 }
2284
2285 video->set_bandwidth(kAutoBandwidth);
2286
2287 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2288 SetMediaProtocol(secure_transport, video.get());
2289
2290 video->set_direction(media_description_options.direction);
2291
2292 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2293 media_description_options.stopped, std::move(video));
2294 if (!AddTransportOffer(media_description_options.mid,
2295 media_description_options.transport_options,
2296 current_description, desc, ice_credentials)) {
2297 return false;
2298 }
2299
2300 return true;
2301 }
2302
AddSctpDataContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2303 bool MediaSessionDescriptionFactory::AddSctpDataContentForOffer(
2304 const MediaDescriptionOptions& media_description_options,
2305 const MediaSessionOptions& session_options,
2306 const ContentInfo* current_content,
2307 const SessionDescription* current_description,
2308 StreamParamsVec* current_streams,
2309 SessionDescription* desc,
2310 IceCredentialsIterator* ice_credentials) const {
2311 auto data = std::make_unique<SctpDataContentDescription>();
2312
2313 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2314
2315 cricket::SecurePolicy sdes_policy =
2316 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2317 : secure();
2318 std::vector<std::string> crypto_suites;
2319 // SDES doesn't make sense for SCTP, so we disable it, and we only
2320 // get SDES crypto suites for RTP-based data channels.
2321 sdes_policy = cricket::SEC_DISABLED;
2322 // Unlike SetMediaProtocol below, we need to set the protocol
2323 // before we call CreateMediaContentOffer. Otherwise,
2324 // CreateMediaContentOffer won't know this is SCTP and will
2325 // generate SSRCs rather than SIDs.
2326 data->set_protocol(secure_transport ? kMediaProtocolUdpDtlsSctp
2327 : kMediaProtocolSctp);
2328 data->set_use_sctpmap(session_options.use_obsolete_sctp_sdp);
2329 data->set_max_message_size(kSctpSendBufferSize);
2330
2331 if (!CreateContentOffer(media_description_options, session_options,
2332 sdes_policy, GetCryptos(current_content),
2333 crypto_suites, RtpHeaderExtensions(), ssrc_generator_,
2334 current_streams, data.get())) {
2335 return false;
2336 }
2337
2338 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
2339 media_description_options.stopped, std::move(data));
2340 if (!AddTransportOffer(media_description_options.mid,
2341 media_description_options.transport_options,
2342 current_description, desc, ice_credentials)) {
2343 return false;
2344 }
2345 return true;
2346 }
2347
AddRtpDataContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpDataCodecs & rtp_data_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2348 bool MediaSessionDescriptionFactory::AddRtpDataContentForOffer(
2349 const MediaDescriptionOptions& media_description_options,
2350 const MediaSessionOptions& session_options,
2351 const ContentInfo* current_content,
2352 const SessionDescription* current_description,
2353 const RtpDataCodecs& rtp_data_codecs,
2354 StreamParamsVec* current_streams,
2355 SessionDescription* desc,
2356 IceCredentialsIterator* ice_credentials) const {
2357 auto data = std::make_unique<RtpDataContentDescription>();
2358 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2359
2360 cricket::SecurePolicy sdes_policy =
2361 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2362 : secure();
2363 std::vector<std::string> crypto_suites;
2364 GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
2365 &crypto_suites);
2366 if (!CreateMediaContentOffer(media_description_options, session_options,
2367 rtp_data_codecs, sdes_policy,
2368 GetCryptos(current_content), crypto_suites,
2369 RtpHeaderExtensions(), ssrc_generator_,
2370 current_streams, data.get())) {
2371 return false;
2372 }
2373
2374 data->set_bandwidth(kDataMaxBandwidth);
2375 SetMediaProtocol(secure_transport, data.get());
2376 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2377 media_description_options.stopped, std::move(data));
2378 if (!AddTransportOffer(media_description_options.mid,
2379 media_description_options.transport_options,
2380 current_description, desc, ice_credentials)) {
2381 return false;
2382 }
2383 return true;
2384 }
2385
AddDataContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpDataCodecs & rtp_data_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2386 bool MediaSessionDescriptionFactory::AddDataContentForOffer(
2387 const MediaDescriptionOptions& media_description_options,
2388 const MediaSessionOptions& session_options,
2389 const ContentInfo* current_content,
2390 const SessionDescription* current_description,
2391 const RtpDataCodecs& rtp_data_codecs,
2392 StreamParamsVec* current_streams,
2393 SessionDescription* desc,
2394 IceCredentialsIterator* ice_credentials) const {
2395 bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
2396 // If the DataChannel type is not specified, use the DataChannel type in
2397 // the current description.
2398 if (session_options.data_channel_type == DCT_NONE && current_content) {
2399 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_DATA));
2400 is_sctp = (current_content->media_description()->protocol() ==
2401 kMediaProtocolSctp);
2402 }
2403 if (is_sctp) {
2404 return AddSctpDataContentForOffer(
2405 media_description_options, session_options, current_content,
2406 current_description, current_streams, desc, ice_credentials);
2407 } else {
2408 return AddRtpDataContentForOffer(media_description_options, session_options,
2409 current_content, current_description,
2410 rtp_data_codecs, current_streams, desc,
2411 ice_credentials);
2412 }
2413 }
2414
2415 // |audio_codecs| = set of all possible codecs that can be used, with correct
2416 // payload type mappings
2417 //
2418 // |supported_audio_codecs| = set of codecs that are supported for the direction
2419 // of this m= section
2420 //
2421 // acd->codecs() = set of previously negotiated codecs for this m= section
2422 //
2423 // The payload types should come from audio_codecs, but the order should come
2424 // from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2425 // change existing codec priority, and that new codecs are added with the right
2426 // priority.
AddAudioContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,const AudioCodecs & audio_codecs,const RtpHeaderExtensions & default_audio_rtp_header_extensions,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2427 bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
2428 const MediaDescriptionOptions& media_description_options,
2429 const MediaSessionOptions& session_options,
2430 const ContentInfo* offer_content,
2431 const SessionDescription* offer_description,
2432 const ContentInfo* current_content,
2433 const SessionDescription* current_description,
2434 const TransportInfo* bundle_transport,
2435 const AudioCodecs& audio_codecs,
2436 const RtpHeaderExtensions& default_audio_rtp_header_extensions,
2437 StreamParamsVec* current_streams,
2438 SessionDescription* answer,
2439 IceCredentialsIterator* ice_credentials) const {
2440 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
2441 const AudioContentDescription* offer_audio_description =
2442 offer_content->media_description()->as_audio();
2443
2444 std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
2445 media_description_options.mid, offer_description,
2446 media_description_options.transport_options, current_description,
2447 bundle_transport != nullptr, ice_credentials);
2448 if (!audio_transport) {
2449 return false;
2450 }
2451
2452 // Pick codecs based on the requested communications direction in the offer
2453 // and the selected direction in the answer.
2454 // Note these will be filtered one final time in CreateMediaContentAnswer.
2455 auto wants_rtd = media_description_options.direction;
2456 auto offer_rtd = offer_audio_description->direction();
2457 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
2458 AudioCodecs supported_audio_codecs =
2459 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2460
2461 AudioCodecs filtered_codecs;
2462
2463 if (!media_description_options.codec_preferences.empty()) {
2464 filtered_codecs = MatchCodecPreference(
2465 media_description_options.codec_preferences, supported_audio_codecs);
2466 } else {
2467 // Add the codecs from current content if it exists and is not rejected nor
2468 // recycled.
2469 if (current_content && !current_content->rejected &&
2470 current_content->name == media_description_options.mid) {
2471 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2472 const AudioContentDescription* acd =
2473 current_content->media_description()->as_audio();
2474 for (const AudioCodec& codec : acd->codecs()) {
2475 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2476 nullptr)) {
2477 filtered_codecs.push_back(codec);
2478 }
2479 }
2480 }
2481 // Add other supported audio codecs.
2482 for (const AudioCodec& codec : supported_audio_codecs) {
2483 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2484 codec, nullptr) &&
2485 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2486 filtered_codecs, codec, nullptr)) {
2487 // We should use the local codec with local parameters and the codec id
2488 // would be correctly mapped in |NegotiateCodecs|.
2489 filtered_codecs.push_back(codec);
2490 }
2491 }
2492 }
2493
2494 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2495 session_options.bundle_enabled;
2496 auto audio_answer = std::make_unique<AudioContentDescription>();
2497 // Do not require or create SDES cryptos if DTLS is used.
2498 cricket::SecurePolicy sdes_policy =
2499 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2500 if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
2501 media_description_options, session_options,
2502 ssrc_generator_, current_streams,
2503 audio_answer.get())) {
2504 return false;
2505 }
2506 if (!CreateMediaContentAnswer(
2507 offer_audio_description, media_description_options, session_options,
2508 sdes_policy, GetCryptos(current_content),
2509 filtered_rtp_header_extensions(default_audio_rtp_header_extensions),
2510 ssrc_generator_, enable_encrypted_rtp_header_extensions_,
2511 current_streams, bundle_enabled, audio_answer.get())) {
2512 return false; // Fails the session setup.
2513 }
2514
2515 bool secure = bundle_transport ? bundle_transport->description.secure()
2516 : audio_transport->secure();
2517 bool rejected = media_description_options.stopped ||
2518 offer_content->rejected ||
2519 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2520 audio_answer->protocol(), secure);
2521 if (!AddTransportAnswer(media_description_options.mid,
2522 *(audio_transport.get()), answer)) {
2523 return false;
2524 }
2525
2526 if (rejected) {
2527 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2528 << "' being rejected in answer.";
2529 }
2530
2531 answer->AddContent(media_description_options.mid, offer_content->type,
2532 rejected, std::move(audio_answer));
2533 return true;
2534 }
2535
2536 // TODO(kron): This function is very similar to AddAudioContentForAnswer.
2537 // Refactor to reuse shared code.
AddVideoContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,const VideoCodecs & video_codecs,const RtpHeaderExtensions & default_video_rtp_header_extensions,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2538 bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
2539 const MediaDescriptionOptions& media_description_options,
2540 const MediaSessionOptions& session_options,
2541 const ContentInfo* offer_content,
2542 const SessionDescription* offer_description,
2543 const ContentInfo* current_content,
2544 const SessionDescription* current_description,
2545 const TransportInfo* bundle_transport,
2546 const VideoCodecs& video_codecs,
2547 const RtpHeaderExtensions& default_video_rtp_header_extensions,
2548 StreamParamsVec* current_streams,
2549 SessionDescription* answer,
2550 IceCredentialsIterator* ice_credentials) const {
2551 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
2552 const VideoContentDescription* offer_video_description =
2553 offer_content->media_description()->as_video();
2554
2555 std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
2556 media_description_options.mid, offer_description,
2557 media_description_options.transport_options, current_description,
2558 bundle_transport != nullptr, ice_credentials);
2559 if (!video_transport) {
2560 return false;
2561 }
2562
2563 // Pick codecs based on the requested communications direction in the offer
2564 // and the selected direction in the answer.
2565 // Note these will be filtered one final time in CreateMediaContentAnswer.
2566 auto wants_rtd = media_description_options.direction;
2567 auto offer_rtd = offer_video_description->direction();
2568 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
2569 VideoCodecs supported_video_codecs =
2570 GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
2571
2572 VideoCodecs filtered_codecs;
2573
2574 if (!media_description_options.codec_preferences.empty()) {
2575 filtered_codecs = MatchCodecPreference(
2576 media_description_options.codec_preferences, supported_video_codecs);
2577 } else {
2578 // Add the codecs from current content if it exists and is not rejected nor
2579 // recycled.
2580 if (current_content && !current_content->rejected &&
2581 current_content->name == media_description_options.mid) {
2582 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2583 const VideoContentDescription* vcd =
2584 current_content->media_description()->as_video();
2585 for (const VideoCodec& codec : vcd->codecs()) {
2586 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2587 nullptr)) {
2588 filtered_codecs.push_back(codec);
2589 }
2590 }
2591 }
2592 // Add other supported video codecs.
2593 for (const VideoCodec& codec : supported_video_codecs) {
2594 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
2595 codec, nullptr) &&
2596 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
2597 filtered_codecs, codec, nullptr)) {
2598 // We should use the local codec with local parameters and the codec id
2599 // would be correctly mapped in |NegotiateCodecs|.
2600 filtered_codecs.push_back(codec);
2601 }
2602 }
2603 }
2604
2605 if (session_options.raw_packetization_for_video) {
2606 for (VideoCodec& codec : filtered_codecs) {
2607 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2608 codec.packetization = kPacketizationParamRaw;
2609 }
2610 }
2611 }
2612
2613 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2614 session_options.bundle_enabled;
2615 auto video_answer = std::make_unique<VideoContentDescription>();
2616 // Do not require or create SDES cryptos if DTLS is used.
2617 cricket::SecurePolicy sdes_policy =
2618 video_transport->secure() ? cricket::SEC_DISABLED : secure();
2619 if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
2620 media_description_options, session_options,
2621 ssrc_generator_, current_streams,
2622 video_answer.get())) {
2623 return false;
2624 }
2625 if (!CreateMediaContentAnswer(
2626 offer_video_description, media_description_options, session_options,
2627 sdes_policy, GetCryptos(current_content),
2628 filtered_rtp_header_extensions(default_video_rtp_header_extensions),
2629 ssrc_generator_, enable_encrypted_rtp_header_extensions_,
2630 current_streams, bundle_enabled, video_answer.get())) {
2631 return false; // Failed the sessin setup.
2632 }
2633 bool secure = bundle_transport ? bundle_transport->description.secure()
2634 : video_transport->secure();
2635 bool rejected = media_description_options.stopped ||
2636 offer_content->rejected ||
2637 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2638 video_answer->protocol(), secure);
2639 if (!AddTransportAnswer(media_description_options.mid,
2640 *(video_transport.get()), answer)) {
2641 return false;
2642 }
2643
2644 if (!rejected) {
2645 video_answer->set_bandwidth(kAutoBandwidth);
2646 } else {
2647 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2648 << "' being rejected in answer.";
2649 }
2650 answer->AddContent(media_description_options.mid, offer_content->type,
2651 rejected, std::move(video_answer));
2652 return true;
2653 }
2654
AddDataContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,const RtpDataCodecs & rtp_data_codecs,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2655 bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
2656 const MediaDescriptionOptions& media_description_options,
2657 const MediaSessionOptions& session_options,
2658 const ContentInfo* offer_content,
2659 const SessionDescription* offer_description,
2660 const ContentInfo* current_content,
2661 const SessionDescription* current_description,
2662 const TransportInfo* bundle_transport,
2663 const RtpDataCodecs& rtp_data_codecs,
2664 StreamParamsVec* current_streams,
2665 SessionDescription* answer,
2666 IceCredentialsIterator* ice_credentials) const {
2667 std::unique_ptr<TransportDescription> data_transport = CreateTransportAnswer(
2668 media_description_options.mid, offer_description,
2669 media_description_options.transport_options, current_description,
2670 bundle_transport != nullptr, ice_credentials);
2671 if (!data_transport) {
2672 return false;
2673 }
2674
2675 // Do not require or create SDES cryptos if DTLS is used.
2676 cricket::SecurePolicy sdes_policy =
2677 data_transport->secure() ? cricket::SEC_DISABLED : secure();
2678 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2679 session_options.bundle_enabled;
2680 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2681 std::unique_ptr<MediaContentDescription> data_answer;
2682 if (offer_content->media_description()->as_sctp()) {
2683 // SCTP data content
2684 data_answer = std::make_unique<SctpDataContentDescription>();
2685 const SctpDataContentDescription* offer_data_description =
2686 offer_content->media_description()->as_sctp();
2687 // Respond with the offerer's proto, whatever it is.
2688 data_answer->as_sctp()->set_protocol(offer_data_description->protocol());
2689 // Respond with our max message size or the remote max messsage size,
2690 // whichever is smaller.
2691 // 0 is treated specially - it means "I can accept any size". Since
2692 // we do not implement infinite size messages, reply with
2693 // kSctpSendBufferSize.
2694 if (offer_data_description->max_message_size() == 0) {
2695 data_answer->as_sctp()->set_max_message_size(kSctpSendBufferSize);
2696 } else {
2697 data_answer->as_sctp()->set_max_message_size(std::min(
2698 offer_data_description->max_message_size(), kSctpSendBufferSize));
2699 }
2700 if (!CreateMediaContentAnswer(
2701 offer_data_description, media_description_options, session_options,
2702 sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
2703 ssrc_generator_, enable_encrypted_rtp_header_extensions_,
2704 current_streams, bundle_enabled, data_answer.get())) {
2705 return false; // Fails the session setup.
2706 }
2707 // Respond with sctpmap if the offer uses sctpmap.
2708 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2709 data_answer->as_sctp()->set_use_sctpmap(offer_uses_sctpmap);
2710 } else {
2711 // RTP offer
2712 data_answer = std::make_unique<RtpDataContentDescription>();
2713
2714 const RtpDataContentDescription* offer_data_description =
2715 offer_content->media_description()->as_rtp_data();
2716 RTC_CHECK(offer_data_description);
2717 if (!SetCodecsInAnswer(offer_data_description, rtp_data_codecs,
2718 media_description_options, session_options,
2719 ssrc_generator_, current_streams,
2720 data_answer->as_rtp_data())) {
2721 return false;
2722 }
2723 if (!CreateMediaContentAnswer(
2724 offer_data_description, media_description_options, session_options,
2725 sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
2726 ssrc_generator_, enable_encrypted_rtp_header_extensions_,
2727 current_streams, bundle_enabled, data_answer.get())) {
2728 return false; // Fails the session setup.
2729 }
2730 }
2731
2732 bool secure = bundle_transport ? bundle_transport->description.secure()
2733 : data_transport->secure();
2734
2735 bool rejected = session_options.data_channel_type == DCT_NONE ||
2736 media_description_options.stopped ||
2737 offer_content->rejected ||
2738 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2739 data_answer->protocol(), secure);
2740 if (!AddTransportAnswer(media_description_options.mid,
2741 *(data_transport.get()), answer)) {
2742 return false;
2743 }
2744
2745 if (!rejected) {
2746 data_answer->set_bandwidth(kDataMaxBandwidth);
2747 } else {
2748 // RFC 3264
2749 // The answer MUST contain the same number of m-lines as the offer.
2750 RTC_LOG(LS_INFO) << "Data is not supported in the answer.";
2751 }
2752 answer->AddContent(media_description_options.mid, offer_content->type,
2753 rejected, std::move(data_answer));
2754 return true;
2755 }
2756
ComputeAudioCodecsIntersectionAndUnion()2757 void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2758 audio_sendrecv_codecs_.clear();
2759 all_audio_codecs_.clear();
2760 // Compute the audio codecs union.
2761 for (const AudioCodec& send : audio_send_codecs_) {
2762 all_audio_codecs_.push_back(send);
2763 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2764 send, nullptr)) {
2765 // It doesn't make sense to have an RTX codec we support sending but not
2766 // receiving.
2767 RTC_DCHECK(!IsRtxCodec(send));
2768 }
2769 }
2770 for (const AudioCodec& recv : audio_recv_codecs_) {
2771 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2772 recv, nullptr)) {
2773 all_audio_codecs_.push_back(recv);
2774 }
2775 }
2776 // Use NegotiateCodecs to merge our codec lists, since the operation is
2777 // essentially the same. Put send_codecs as the offered_codecs, which is the
2778 // order we'd like to follow. The reasoning is that encoding is usually more
2779 // expensive than decoding, and prioritizing a codec in the send list probably
2780 // means it's a codec we can handle efficiently.
2781 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2782 &audio_sendrecv_codecs_, true);
2783 }
2784
ComputeVideoCodecsIntersectionAndUnion()2785 void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() {
2786 video_sendrecv_codecs_.clear();
2787 all_video_codecs_.clear();
2788 // Compute the video codecs union.
2789 for (const VideoCodec& send : video_send_codecs_) {
2790 all_video_codecs_.push_back(send);
2791 if (!FindMatchingCodec<VideoCodec>(video_send_codecs_, video_recv_codecs_,
2792 send, nullptr)) {
2793 // TODO(kron): This check is violated by the unit test:
2794 // MediaSessionDescriptionFactoryTest.RtxWithoutApt
2795 // Remove either the test or the check.
2796
2797 // It doesn't make sense to have an RTX codec we support sending but not
2798 // receiving.
2799 // RTC_DCHECK(!IsRtxCodec(send));
2800 }
2801 }
2802 for (const VideoCodec& recv : video_recv_codecs_) {
2803 if (!FindMatchingCodec<VideoCodec>(video_recv_codecs_, video_send_codecs_,
2804 recv, nullptr)) {
2805 all_video_codecs_.push_back(recv);
2806 }
2807 }
2808 // Use NegotiateCodecs to merge our codec lists, since the operation is
2809 // essentially the same. Put send_codecs as the offered_codecs, which is the
2810 // order we'd like to follow. The reasoning is that encoding is usually more
2811 // expensive than decoding, and prioritizing a codec in the send list probably
2812 // means it's a codec we can handle efficiently.
2813 NegotiateCodecs(video_recv_codecs_, video_send_codecs_,
2814 &video_sendrecv_codecs_, true);
2815 }
2816
IsMediaContent(const ContentInfo * content)2817 bool IsMediaContent(const ContentInfo* content) {
2818 return (content && (content->type == MediaProtocolType::kRtp ||
2819 content->type == MediaProtocolType::kSctp));
2820 }
2821
IsAudioContent(const ContentInfo * content)2822 bool IsAudioContent(const ContentInfo* content) {
2823 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2824 }
2825
IsVideoContent(const ContentInfo * content)2826 bool IsVideoContent(const ContentInfo* content) {
2827 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2828 }
2829
IsDataContent(const ContentInfo * content)2830 bool IsDataContent(const ContentInfo* content) {
2831 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2832 }
2833
GetFirstMediaContent(const ContentInfos & contents,MediaType media_type)2834 const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2835 MediaType media_type) {
2836 for (const ContentInfo& content : contents) {
2837 if (IsMediaContentOfType(&content, media_type)) {
2838 return &content;
2839 }
2840 }
2841 return nullptr;
2842 }
2843
GetFirstAudioContent(const ContentInfos & contents)2844 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2845 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2846 }
2847
GetFirstVideoContent(const ContentInfos & contents)2848 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2849 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2850 }
2851
GetFirstDataContent(const ContentInfos & contents)2852 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2853 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2854 }
2855
GetFirstMediaContent(const SessionDescription * sdesc,MediaType media_type)2856 const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2857 MediaType media_type) {
2858 if (sdesc == nullptr) {
2859 return nullptr;
2860 }
2861
2862 return GetFirstMediaContent(sdesc->contents(), media_type);
2863 }
2864
GetFirstAudioContent(const SessionDescription * sdesc)2865 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2866 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2867 }
2868
GetFirstVideoContent(const SessionDescription * sdesc)2869 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2870 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2871 }
2872
GetFirstDataContent(const SessionDescription * sdesc)2873 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2874 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2875 }
2876
GetFirstMediaContentDescription(const SessionDescription * sdesc,MediaType media_type)2877 const MediaContentDescription* GetFirstMediaContentDescription(
2878 const SessionDescription* sdesc,
2879 MediaType media_type) {
2880 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2881 return (content ? content->media_description() : nullptr);
2882 }
2883
GetFirstAudioContentDescription(const SessionDescription * sdesc)2884 const AudioContentDescription* GetFirstAudioContentDescription(
2885 const SessionDescription* sdesc) {
2886 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
2887 return desc ? desc->as_audio() : nullptr;
2888 }
2889
GetFirstVideoContentDescription(const SessionDescription * sdesc)2890 const VideoContentDescription* GetFirstVideoContentDescription(
2891 const SessionDescription* sdesc) {
2892 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
2893 return desc ? desc->as_video() : nullptr;
2894 }
2895
GetFirstRtpDataContentDescription(const SessionDescription * sdesc)2896 const RtpDataContentDescription* GetFirstRtpDataContentDescription(
2897 const SessionDescription* sdesc) {
2898 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
2899 return desc ? desc->as_rtp_data() : nullptr;
2900 }
2901
GetFirstSctpDataContentDescription(const SessionDescription * sdesc)2902 const SctpDataContentDescription* GetFirstSctpDataContentDescription(
2903 const SessionDescription* sdesc) {
2904 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
2905 return desc ? desc->as_sctp() : nullptr;
2906 }
2907
2908 //
2909 // Non-const versions of the above functions.
2910 //
2911
GetFirstMediaContent(ContentInfos * contents,MediaType media_type)2912 ContentInfo* GetFirstMediaContent(ContentInfos* contents,
2913 MediaType media_type) {
2914 for (ContentInfo& content : *contents) {
2915 if (IsMediaContentOfType(&content, media_type)) {
2916 return &content;
2917 }
2918 }
2919 return nullptr;
2920 }
2921
GetFirstAudioContent(ContentInfos * contents)2922 ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
2923 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2924 }
2925
GetFirstVideoContent(ContentInfos * contents)2926 ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
2927 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2928 }
2929
GetFirstDataContent(ContentInfos * contents)2930 ContentInfo* GetFirstDataContent(ContentInfos* contents) {
2931 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2932 }
2933
GetFirstMediaContent(SessionDescription * sdesc,MediaType media_type)2934 ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2935 MediaType media_type) {
2936 if (sdesc == nullptr) {
2937 return nullptr;
2938 }
2939
2940 return GetFirstMediaContent(&sdesc->contents(), media_type);
2941 }
2942
GetFirstAudioContent(SessionDescription * sdesc)2943 ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2944 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2945 }
2946
GetFirstVideoContent(SessionDescription * sdesc)2947 ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2948 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2949 }
2950
GetFirstDataContent(SessionDescription * sdesc)2951 ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2952 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2953 }
2954
GetFirstMediaContentDescription(SessionDescription * sdesc,MediaType media_type)2955 MediaContentDescription* GetFirstMediaContentDescription(
2956 SessionDescription* sdesc,
2957 MediaType media_type) {
2958 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2959 return (content ? content->media_description() : nullptr);
2960 }
2961
GetFirstAudioContentDescription(SessionDescription * sdesc)2962 AudioContentDescription* GetFirstAudioContentDescription(
2963 SessionDescription* sdesc) {
2964 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
2965 return desc ? desc->as_audio() : nullptr;
2966 }
2967
GetFirstVideoContentDescription(SessionDescription * sdesc)2968 VideoContentDescription* GetFirstVideoContentDescription(
2969 SessionDescription* sdesc) {
2970 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
2971 return desc ? desc->as_video() : nullptr;
2972 }
2973
GetFirstRtpDataContentDescription(SessionDescription * sdesc)2974 RtpDataContentDescription* GetFirstRtpDataContentDescription(
2975 SessionDescription* sdesc) {
2976 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
2977 return desc ? desc->as_rtp_data() : nullptr;
2978 }
2979
GetFirstSctpDataContentDescription(SessionDescription * sdesc)2980 SctpDataContentDescription* GetFirstSctpDataContentDescription(
2981 SessionDescription* sdesc) {
2982 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
2983 return desc ? desc->as_sctp() : nullptr;
2984 }
2985
2986 } // namespace cricket
2987