blob: 274d7ce8b2b8e9a954be4cd546892b707377a474 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellander65c7f672016-02-12 08:05:012 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellander65c7f672016-02-12 08:05:014 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:369 */
10
Steve Anton10542f22019-01-11 17:11:0011#include "pc/media_session.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
Harald Alvestrand5761e7b2021-01-29 14:45:0813#include <stddef.h>
14
Elad Alon157540a2019-02-08 22:37:5215#include <algorithm>
henrike@webrtc.org28e20752013-07-10 00:45:3616#include <map>
Harald Alvestrandc24a2182022-02-23 13:44:5917#include <string>
deadbeef67cf2c12016-04-13 17:07:1618#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:3619#include <utility>
20
Steve Anton64b626b2019-01-29 01:25:2621#include "absl/algorithm/container.h"
Niels Möller2edab4c2018-10-22 07:48:0822#include "absl/strings/match.h"
Harald Alvestrand5761e7b2021-01-29 14:45:0823#include "absl/strings/string_view.h"
Danil Chapovalov66cadcc2018-06-19 14:47:4324#include "absl/types/optional.h"
Harald Alvestrand0d018412021-11-04 13:52:3125#include "api/crypto_params.h"
Harald Alvestrand5761e7b2021-01-29 14:45:0826#include "media/base/codec.h"
Steve Anton10542f22019-01-11 17:11:0027#include "media/base/media_constants.h"
Harald Alvestrandc3fa7c32022-05-22 10:57:0128#include "media/base/media_engine.h"
Johannes Kronc3fcee72021-04-19 07:09:2629#include "media/base/sdp_video_format_utils.h"
Harald Alvestrandfbb45bd2019-05-15 06:07:4730#include "media/sctp/sctp_transport_internal.h"
Steve Anton10542f22019-01-11 17:11:0031#include "p2p/base/p2p_constants.h"
Harald Alvestrand5fc28b12019-05-13 11:36:1632#include "pc/media_protocol_names.h"
Steve Anton10542f22019-01-11 17:11:0033#include "pc/rtp_media_utils.h"
Johannes Kron746dd0d2019-06-20 13:37:5234#include "pc/used_ids.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3135#include "rtc_base/checks.h"
36#include "rtc_base/helpers.h"
37#include "rtc_base/logging.h"
Harald Alvestrand5761e7b2021-01-29 14:45:0838#include "rtc_base/ssl_stream_adapter.h"
39#include "rtc_base/string_encode.h"
Artem Titova76af0c2018-07-23 15:38:1240#include "rtc_base/third_party/base64/base64.h"
Amit Hilbuchdbb49df2019-01-23 22:54:2441#include "rtc_base/unique_id_generator.h"
henrike@webrtc.org28e20752013-07-10 00:45:3642
43namespace {
Steve Anton1d03a752017-11-27 22:30:0944
Amit Hilbuchdbb49df2019-01-23 22:54:2445using rtc::UniqueRandomIdGenerator;
Steve Anton1d03a752017-11-27 22:30:0946using webrtc::RtpTransceiverDirection;
47
Harald Alvestrand0d018412021-11-04 13:52:3148const char kInline[] = "inline:";
49
50void GetSupportedSdesCryptoSuiteNames(
51 void (*func)(const webrtc::CryptoOptions&, std::vector<int>*),
52 const webrtc::CryptoOptions& crypto_options,
53 std::vector<std::string>* names) {
54 std::vector<int> crypto_suites;
55 func(crypto_options, &crypto_suites);
56 for (const auto crypto : crypto_suites) {
57 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
58 }
59}
60
Markus Handell755c65d2020-06-23 23:06:1061webrtc::RtpExtension RtpExtensionFromCapability(
62 const webrtc::RtpHeaderExtensionCapability& capability) {
63 return webrtc::RtpExtension(capability.uri,
64 capability.preferred_id.value_or(1));
65}
66
67cricket::RtpHeaderExtensions RtpHeaderExtensionsFromCapabilities(
68 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities) {
69 cricket::RtpHeaderExtensions exts;
70 for (const auto& capability : capabilities) {
71 exts.push_back(RtpExtensionFromCapability(capability));
72 }
73 return exts;
74}
75
76std::vector<webrtc::RtpHeaderExtensionCapability>
77UnstoppedRtpHeaderExtensionCapabilities(
78 std::vector<webrtc::RtpHeaderExtensionCapability> capabilities) {
79 capabilities.erase(
80 std::remove_if(
81 capabilities.begin(), capabilities.end(),
82 [](const webrtc::RtpHeaderExtensionCapability& capability) {
83 return capability.direction == RtpTransceiverDirection::kStopped;
84 }),
85 capabilities.end());
86 return capabilities;
87}
88
89bool IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability& capability,
90 const cricket::RtpHeaderExtensions& extensions) {
91 return std::find_if(extensions.begin(), extensions.end(),
92 [&capability](const webrtc::RtpExtension& extension) {
93 return capability.uri == extension.uri;
94 }) != extensions.end();
95}
96
97cricket::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions(
98 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities,
99 const cricket::RtpHeaderExtensions& unencrypted,
100 const cricket::RtpHeaderExtensions& encrypted) {
101 cricket::RtpHeaderExtensions extensions;
102 for (const auto& capability : capabilities) {
103 if (capability.direction != RtpTransceiverDirection::kStopped ||
104 IsCapabilityPresent(capability, unencrypted) ||
105 IsCapabilityPresent(capability, encrypted)) {
106 extensions.push_back(RtpExtensionFromCapability(capability));
107 }
108 }
109 return extensions;
110}
111
terelius8c011e52016-04-26 12:28:11112} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36113
114namespace cricket {
115
Steve Anton1d03a752017-11-27 22:30:09116static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
117 RtpTransceiverDirection offer,
118 RtpTransceiverDirection wants) {
119 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
120 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
121 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
122 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
123 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
124 offer_send && wants_recv);
ossu075af922016-06-14 10:29:38125}
126
henrike@webrtc.org28e20752013-07-10 00:45:36127static bool IsMediaContentOfType(const ContentInfo* content,
128 MediaType media_type) {
Steve Antonb1c1de12017-12-21 23:14:30129 if (!content || !content->media_description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36130 return false;
131 }
Steve Antonb1c1de12017-12-21 23:14:30132 return content->media_description()->type() == media_type;
henrike@webrtc.org28e20752013-07-10 00:45:36133}
134
Harald Alvestrand0d018412021-11-04 13:52:31135static bool CreateCryptoParams(int tag,
136 const std::string& cipher,
137 CryptoParams* crypto_out) {
138 int key_len;
139 int salt_len;
140 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
141 &key_len, &salt_len)) {
142 return false;
143 }
144
145 int master_key_len = key_len + salt_len;
146 std::string master_key;
147 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
148 return false;
149 }
150
151 RTC_CHECK_EQ(master_key_len, master_key.size());
152 std::string key = rtc::Base64::Encode(master_key);
153
154 crypto_out->tag = tag;
155 crypto_out->cipher_suite = cipher;
156 crypto_out->key_params = kInline;
157 crypto_out->key_params += key;
158 return true;
159}
160
161static bool AddCryptoParams(const std::string& cipher_suite,
162 CryptoParamsVec* cryptos_out) {
163 int size = static_cast<int>(cryptos_out->size());
164
165 cryptos_out->resize(size + 1);
166 return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
167}
168
169void AddMediaCryptos(const CryptoParamsVec& cryptos,
170 MediaContentDescription* media) {
171 for (const CryptoParams& crypto : cryptos) {
172 media->AddCrypto(crypto);
173 }
174}
175
176bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
177 MediaContentDescription* media) {
178 CryptoParamsVec cryptos;
179 for (const std::string& crypto_suite : crypto_suites) {
180 if (!AddCryptoParams(crypto_suite, &cryptos)) {
181 return false;
182 }
183 }
184 AddMediaCryptos(cryptos, media);
185 return true;
186}
187
188const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
189 if (!content || !content->media_description()) {
190 return nullptr;
191 }
192 return &content->media_description()->cryptos();
193}
194
195bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
196 const CryptoParams& crypto,
197 CryptoParams* crypto_out) {
198 auto it = absl::c_find_if(
199 cryptos, [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
200 if (it == cryptos.end()) {
201 return false;
202 }
203 *crypto_out = *it;
204 return true;
205}
206
207// For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
208// low overhead.
209void GetSupportedAudioSdesCryptoSuites(
210 const webrtc::CryptoOptions& crypto_options,
211 std::vector<int>* crypto_suites) {
212 if (crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher) {
213 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_32);
214 }
215 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
216 if (crypto_options.srtp.enable_gcm_crypto_suites) {
217 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
218 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
219 }
220}
221
222void GetSupportedAudioSdesCryptoSuiteNames(
223 const webrtc::CryptoOptions& crypto_options,
224 std::vector<std::string>* crypto_suite_names) {
225 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
226 crypto_options, crypto_suite_names);
227}
228
229void GetSupportedVideoSdesCryptoSuites(
230 const webrtc::CryptoOptions& crypto_options,
231 std::vector<int>* crypto_suites) {
232 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
233 if (crypto_options.srtp.enable_gcm_crypto_suites) {
234 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
235 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
236 }
237}
238
239void GetSupportedVideoSdesCryptoSuiteNames(
240 const webrtc::CryptoOptions& crypto_options,
241 std::vector<std::string>* crypto_suite_names) {
242 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
243 crypto_options, crypto_suite_names);
244}
245
246void GetSupportedDataSdesCryptoSuites(
247 const webrtc::CryptoOptions& crypto_options,
248 std::vector<int>* crypto_suites) {
249 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
250 if (crypto_options.srtp.enable_gcm_crypto_suites) {
251 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
252 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
253 }
254}
255
256void GetSupportedDataSdesCryptoSuiteNames(
257 const webrtc::CryptoOptions& crypto_options,
258 std::vector<std::string>* crypto_suite_names) {
259 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
260 crypto_options, crypto_suite_names);
261}
262
263// Support any GCM cipher (if enabled through options). For video support only
264// 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
265// bundle is enabled because it is low overhead.
266// Pick the crypto in the list that is supported.
267static bool SelectCrypto(const MediaContentDescription* offer,
268 bool bundle,
269 const webrtc::CryptoOptions& crypto_options,
270 CryptoParams* crypto_out) {
271 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
272 const CryptoParamsVec& cryptos = offer->cryptos();
273
274 for (const CryptoParams& crypto : cryptos) {
275 if ((crypto_options.srtp.enable_gcm_crypto_suites &&
276 rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
277 rtc::kCsAesCm128HmacSha1_80 == crypto.cipher_suite ||
278 (rtc::kCsAesCm128HmacSha1_32 == crypto.cipher_suite && audio &&
279 !bundle && crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher)) {
280 return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
281 }
282 }
283 return false;
284}
285
henrike@webrtc.org28e20752013-07-10 00:45:36286// Finds all StreamParams of all media types and attach them to stream_params.
Steve Anton5c72e712018-12-10 22:25:30287static StreamParamsVec GetCurrentStreamParams(
288 const std::vector<const ContentInfo*>& active_local_contents) {
289 StreamParamsVec stream_params;
290 for (const ContentInfo* content : active_local_contents) {
291 for (const StreamParams& params : content->media_description()->streams()) {
292 stream_params.push_back(params);
henrike@webrtc.org28e20752013-07-10 00:45:36293 }
294 }
Steve Anton5c72e712018-12-10 22:25:30295 return stream_params;
henrike@webrtc.org28e20752013-07-10 00:45:36296}
297
Amit Hilbuchc63ddb22019-01-02 18:13:58298static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
299 const SenderOptions& sender,
300 const std::string& rtcp_cname,
Amit Hilbuchc63ddb22019-01-02 18:13:58301 bool include_rtx_streams,
Amit Hilbuchbcd39d42019-01-26 01:13:56302 bool include_flexfec_stream,
Jonas Orelanded99dae2022-03-09 08:28:10303 UniqueRandomIdGenerator* ssrc_generator,
Jonas Orelande62c2f22022-03-29 09:04:48304 const webrtc::FieldTrialsView& field_trials) {
Amit Hilbuchc63ddb22019-01-02 18:13:58305 StreamParams result;
306 result.id = sender.track_id;
307
Amit Hilbuchbcd39d42019-01-26 01:13:56308 // TODO(brandtr): Update when we support multistream protection.
309 if (include_flexfec_stream && sender.num_sim_layers > 1) {
310 include_flexfec_stream = false;
311 RTC_LOG(LS_WARNING)
312 << "Our FlexFEC implementation only supports protecting "
313 "a single media streams. This session has multiple "
314 "media streams however, so no FlexFEC SSRC will be generated.";
Amit Hilbuchc63ddb22019-01-02 18:13:58315 }
Jonas Orelanded99dae2022-03-09 08:28:10316 if (include_flexfec_stream && !field_trials.IsEnabled("WebRTC-FlexFEC-03")) {
Philipp Hanckefedc7ab2020-11-17 20:59:12317 include_flexfec_stream = false;
318 RTC_LOG(LS_WARNING)
319 << "WebRTC-FlexFEC trial is not enabled, not sending FlexFEC";
320 }
Amit Hilbuchc63ddb22019-01-02 18:13:58321
Amit Hilbuchbcd39d42019-01-26 01:13:56322 result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
323 include_flexfec_stream, ssrc_generator);
Amit Hilbuchc63ddb22019-01-02 18:13:58324
Amit Hilbuchc63ddb22019-01-02 18:13:58325 result.cname = rtcp_cname;
326 result.set_stream_ids(sender.stream_ids);
327
328 return result;
329}
330
331static bool ValidateSimulcastLayers(
332 const std::vector<RidDescription>& rids,
333 const SimulcastLayerList& simulcast_layers) {
Steve Anton64b626b2019-01-29 01:25:26334 return absl::c_all_of(
335 simulcast_layers.GetAllLayers(), [&rids](const SimulcastLayer& layer) {
336 return absl::c_any_of(rids, [&layer](const RidDescription& rid) {
337 return rid.rid == layer.rid;
338 });
339 });
Amit Hilbuchc63ddb22019-01-02 18:13:58340}
341
342static StreamParams CreateStreamParamsForNewSenderWithRids(
343 const SenderOptions& sender,
344 const std::string& rtcp_cname) {
345 RTC_DCHECK(!sender.rids.empty());
346 RTC_DCHECK_EQ(sender.num_sim_layers, 0)
347 << "RIDs are the compliant way to indicate simulcast.";
348 RTC_DCHECK(ValidateSimulcastLayers(sender.rids, sender.simulcast_layers));
349 StreamParams result;
350 result.id = sender.track_id;
351 result.cname = rtcp_cname;
352 result.set_stream_ids(sender.stream_ids);
353
354 // More than one rid should be signaled.
355 if (sender.rids.size() > 1) {
356 result.set_rids(sender.rids);
357 }
358
359 return result;
360}
361
362// Adds SimulcastDescription if indicated by the media description options.
363// MediaContentDescription should already be set up with the send rids.
364static void AddSimulcastToMediaDescription(
365 const MediaDescriptionOptions& media_description_options,
366 MediaContentDescription* description) {
367 RTC_DCHECK(description);
368
369 // Check if we are using RIDs in this scenario.
Steve Anton64b626b2019-01-29 01:25:26370 if (absl::c_all_of(description->streams(), [](const StreamParams& params) {
371 return !params.has_rids();
372 })) {
Amit Hilbuchc63ddb22019-01-02 18:13:58373 return;
374 }
375
376 RTC_DCHECK_EQ(1, description->streams().size())
377 << "RIDs are only supported in Unified Plan semantics.";
378 RTC_DCHECK_EQ(1, media_description_options.sender_options.size());
379 RTC_DCHECK(description->type() == MediaType::MEDIA_TYPE_AUDIO ||
380 description->type() == MediaType::MEDIA_TYPE_VIDEO);
381
382 // One RID or less indicates that simulcast is not needed.
383 if (description->streams()[0].rids().size() <= 1) {
384 return;
385 }
386
Amit Hilbuchb7446ed2019-01-28 20:25:25387 // Only negotiate the send layers.
Amit Hilbuchc63ddb22019-01-02 18:13:58388 SimulcastDescription simulcast;
389 simulcast.send_layers() =
390 media_description_options.sender_options[0].simulcast_layers;
Amit Hilbuchc63ddb22019-01-02 18:13:58391 description->set_simulcast_description(simulcast);
392}
393
Artem Titov880fa812021-07-30 20:30:23394// Adds a StreamParams for each SenderOptions in `sender_options` to
zhihuang1c378ed2017-08-17 21:10:50395// content_description.
Artem Titov880fa812021-07-30 20:30:23396// `current_params` - All currently known StreamParams of any media type.
henrike@webrtc.org28e20752013-07-10 00:45:36397template <class C>
Jonas Orelanded99dae2022-03-09 08:28:10398static bool AddStreamParams(const std::vector<SenderOptions>& sender_options,
399 const std::string& rtcp_cname,
400 UniqueRandomIdGenerator* ssrc_generator,
401 StreamParamsVec* current_streams,
402 MediaContentDescriptionImpl<C>* content_description,
Jonas Orelande62c2f22022-03-29 09:04:48403 const webrtc::FieldTrialsView& field_trials) {
Taylor Brandstetter1d7a6372016-08-24 20:15:27404 // SCTP streams are not negotiated using SDP/ContentDescriptions.
Harald Alvestrand5fc28b12019-05-13 11:36:16405 if (IsSctpProtocol(content_description->protocol())) {
Taylor Brandstetter1d7a6372016-08-24 20:15:27406 return true;
407 }
408
Noah Richards2e7a0982015-05-18 21:02:54409 const bool include_rtx_streams =
410 ContainsRtxCodec(content_description->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:36411
brandtr03d5fb12016-11-22 11:37:59412 const bool include_flexfec_stream =
413 ContainsFlexfecCodec(content_description->codecs());
414
zhihuang1c378ed2017-08-17 21:10:50415 for (const SenderOptions& sender : sender_options) {
Harald Alvestrand519c15d2020-10-19 18:46:37416 StreamParams* param = GetStreamByIds(*current_streams, sender.track_id);
tommi@webrtc.org586f2ed2015-01-22 23:00:41417 if (!param) {
zhihuang1c378ed2017-08-17 21:10:50418 // This is a new sender.
Amit Hilbuchc63ddb22019-01-02 18:13:58419 StreamParams stream_param =
420 sender.rids.empty()
421 ?
422 // Signal SSRCs and legacy simulcast (if requested).
423 CreateStreamParamsForNewSenderWithSsrcs(
Amit Hilbuchbcd39d42019-01-26 01:13:56424 sender, rtcp_cname, include_rtx_streams,
Jonas Orelanded99dae2022-03-09 08:28:10425 include_flexfec_stream, ssrc_generator, field_trials)
Amit Hilbuchc63ddb22019-01-02 18:13:58426 :
427 // Signal RIDs and spec-compliant simulcast (if requested).
428 CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
429
henrike@webrtc.org28e20752013-07-10 00:45:36430 content_description->AddStream(stream_param);
431
432 // Store the new StreamParams in current_streams.
433 // This is necessary so that we can use the CNAME for other media types.
434 current_streams->push_back(stream_param);
435 } else {
deadbeef2f425aa2017-04-14 17:41:32436 // Use existing generated SSRCs/groups, but update the sync_label if
437 // necessary. This may be needed if a MediaStreamTrack was moved from one
438 // MediaStream to another.
Seth Hampson845e8782018-03-02 19:34:10439 param->set_stream_ids(sender.stream_ids);
tommi@webrtc.org586f2ed2015-01-22 23:00:41440 content_description->AddStream(*param);
henrike@webrtc.org28e20752013-07-10 00:45:36441 }
442 }
443 return true;
444}
445
Artem Titov880fa812021-07-30 20:30:23446// Updates the transport infos of the `sdesc` according to the given
447// `bundle_group`. The transport infos of the content names within the
448// `bundle_group` should be updated to use the ufrag, pwd and DTLS role of the
449// first content within the `bundle_group`.
henrike@webrtc.org28e20752013-07-10 00:45:36450static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
451 SessionDescription* sdesc) {
452 // The bundle should not be empty.
453 if (!sdesc || !bundle_group.FirstContentName()) {
454 return false;
455 }
456
457 // We should definitely have a transport for the first content.
jbauch083b73f2015-07-16 09:46:32458 const std::string& selected_content_name = *bundle_group.FirstContentName();
henrike@webrtc.org28e20752013-07-10 00:45:36459 const TransportInfo* selected_transport_info =
460 sdesc->GetTransportInfoByName(selected_content_name);
461 if (!selected_transport_info) {
462 return false;
463 }
464
465 // Set the other contents to use the same ICE credentials.
jbauch083b73f2015-07-16 09:46:32466 const std::string& selected_ufrag =
henrike@webrtc.org28e20752013-07-10 00:45:36467 selected_transport_info->description.ice_ufrag;
jbauch083b73f2015-07-16 09:46:32468 const std::string& selected_pwd =
henrike@webrtc.org28e20752013-07-10 00:45:36469 selected_transport_info->description.ice_pwd;
Taylor Brandstetterf475d3652016-01-08 23:35:57470 ConnectionRole selected_connection_role =
471 selected_transport_info->description.connection_role;
Steve Anton3a66edf2018-09-10 19:57:37472 for (TransportInfo& transport_info : sdesc->transport_infos()) {
473 if (bundle_group.HasContentName(transport_info.content_name) &&
474 transport_info.content_name != selected_content_name) {
475 transport_info.description.ice_ufrag = selected_ufrag;
476 transport_info.description.ice_pwd = selected_pwd;
477 transport_info.description.connection_role = selected_connection_role;
henrike@webrtc.org28e20752013-07-10 00:45:36478 }
479 }
480 return true;
481}
482
Harald Alvestrand0d018412021-11-04 13:52:31483// Gets the CryptoParamsVec of the given `content_name` from `sdesc`, and
484// sets it to `cryptos`.
485static bool GetCryptosByName(const SessionDescription* sdesc,
486 const std::string& content_name,
487 CryptoParamsVec* cryptos) {
488 if (!sdesc || !cryptos) {
489 return false;
490 }
491 const ContentInfo* content = sdesc->GetContentByName(content_name);
492 if (!content || !content->media_description()) {
493 return false;
494 }
495 *cryptos = content->media_description()->cryptos();
496 return true;
497}
498
499// Prunes the `target_cryptos` by removing the crypto params (cipher_suite)
500// which are not available in `filter`.
501static void PruneCryptos(const CryptoParamsVec& filter,
502 CryptoParamsVec* target_cryptos) {
503 if (!target_cryptos) {
504 return;
505 }
506
507 target_cryptos->erase(
508 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
509 // Returns true if the `crypto`'s cipher_suite is not
510 // found in `filter`.
511 [&filter](const CryptoParams& crypto) {
512 for (const CryptoParams& entry : filter) {
513 if (entry.cipher_suite == crypto.cipher_suite)
514 return false;
515 }
516 return true;
517 }),
518 target_cryptos->end());
519}
520
521static bool IsRtpContent(SessionDescription* sdesc,
522 const std::string& content_name) {
523 bool is_rtp = false;
524 ContentInfo* content = sdesc->GetContentByName(content_name);
525 if (content && content->media_description()) {
526 is_rtp = IsRtpProtocol(content->media_description()->protocol());
527 }
528 return is_rtp;
529}
530
531// Updates the crypto parameters of the `sdesc` according to the given
532// `bundle_group`. The crypto parameters of all the contents within the
533// `bundle_group` should be updated to use the common subset of the
534// available cryptos.
535static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
536 SessionDescription* sdesc) {
537 // The bundle should not be empty.
538 if (!sdesc || !bundle_group.FirstContentName()) {
539 return false;
540 }
541
542 bool common_cryptos_needed = false;
543 // Get the common cryptos.
544 const ContentNames& content_names = bundle_group.content_names();
545 CryptoParamsVec common_cryptos;
546 bool first = true;
547 for (const std::string& content_name : content_names) {
548 if (!IsRtpContent(sdesc, content_name)) {
549 continue;
550 }
551 // The common cryptos are needed if any of the content does not have DTLS
552 // enabled.
553 if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
554 common_cryptos_needed = true;
555 }
556 if (first) {
557 first = false;
558 // Initial the common_cryptos with the first content in the bundle group.
559 if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
560 return false;
561 }
562 if (common_cryptos.empty()) {
563 // If there's no crypto params, we should just return.
564 return true;
565 }
566 } else {
567 CryptoParamsVec cryptos;
568 if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
569 return false;
570 }
571 PruneCryptos(cryptos, &common_cryptos);
572 }
573 }
574
575 if (common_cryptos.empty() && common_cryptos_needed) {
576 return false;
577 }
578
579 // Update to use the common cryptos.
580 for (const std::string& content_name : content_names) {
581 if (!IsRtpContent(sdesc, content_name)) {
582 continue;
583 }
584 ContentInfo* content = sdesc->GetContentByName(content_name);
585 if (IsMediaContent(content)) {
586 MediaContentDescription* media_desc = content->media_description();
587 if (!media_desc) {
588 return false;
589 }
590 media_desc->set_cryptos(common_cryptos);
591 }
592 }
593 return true;
594}
595
Steve Anton5c72e712018-12-10 22:25:30596static std::vector<const ContentInfo*> GetActiveContents(
597 const SessionDescription& description,
598 const MediaSessionOptions& session_options) {
599 std::vector<const ContentInfo*> active_contents;
600 for (size_t i = 0; i < description.contents().size(); ++i) {
601 RTC_DCHECK_LT(i, session_options.media_description_options.size());
602 const ContentInfo& content = description.contents()[i];
603 const MediaDescriptionOptions& media_options =
604 session_options.media_description_options[i];
605 if (!content.rejected && !media_options.stopped &&
606 content.name == media_options.mid) {
607 active_contents.push_back(&content);
608 }
609 }
610 return active_contents;
611}
612
henrike@webrtc.org28e20752013-07-10 00:45:36613template <class C>
614static bool ContainsRtxCodec(const std::vector<C>& codecs) {
brandtr03d5fb12016-11-22 11:37:59615 for (const auto& codec : codecs) {
616 if (IsRtxCodec(codec)) {
henrike@webrtc.org28e20752013-07-10 00:45:36617 return true;
618 }
619 }
620 return false;
621}
622
623template <class C>
Philipp Hancke7145a142021-09-28 05:46:06624static bool IsRedCodec(const C& codec) {
625 return absl::EqualsIgnoreCase(codec.name, kRedCodecName);
626}
627
628template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36629static bool IsRtxCodec(const C& codec) {
Niels Möller2edab4c2018-10-22 07:48:08630 return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
henrike@webrtc.org28e20752013-07-10 00:45:36631}
632
brandtr03d5fb12016-11-22 11:37:59633template <class C>
634static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
635 for (const auto& codec : codecs) {
636 if (IsFlexfecCodec(codec)) {
637 return true;
638 }
639 }
640 return false;
641}
642
643template <class C>
644static bool IsFlexfecCodec(const C& codec) {
Niels Möller2edab4c2018-10-22 07:48:08645 return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName);
brandtr03d5fb12016-11-22 11:37:59646}
647
Philipp Hancke2f3168f2022-05-16 12:41:32648template <class C>
649static bool IsUlpfecCodec(const C& codec) {
650 return absl::EqualsIgnoreCase(codec.name, kUlpfecCodecName);
651}
652
653template <class C>
654static bool IsComfortNoiseCodec(const C& codec) {
655 return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName);
656}
657
Artem Titov880fa812021-07-30 20:30:23658// Create a media content to be offered for the given `sender_options`,
Harald Alvestrand0d018412021-11-04 13:52:31659// according to the given options.rtcp_mux, session_options.is_muc, codecs,
660// secure_transport, crypto, and current_streams. If we don't currently have
661// crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
662// created (according to crypto_suites). The created content is added to the
663// offer.
Harald Alvestrand5fc28b12019-05-13 11:36:16664static bool CreateContentOffer(
Amit Hilbuchc63ddb22019-01-02 18:13:58665 const MediaDescriptionOptions& media_description_options,
zhihuang1c378ed2017-08-17 21:10:50666 const MediaSessionOptions& session_options,
Harald Alvestrand0d018412021-11-04 13:52:31667 const SecurePolicy& secure_policy,
668 const CryptoParamsVec* current_cryptos,
669 const std::vector<std::string>& crypto_suites,
henrike@webrtc.org28e20752013-07-10 00:45:36670 const RtpHeaderExtensions& rtp_extensions,
Amit Hilbuchbcd39d42019-01-26 01:13:56671 UniqueRandomIdGenerator* ssrc_generator,
henrike@webrtc.org28e20752013-07-10 00:45:36672 StreamParamsVec* current_streams,
Harald Alvestrand5fc28b12019-05-13 11:36:16673 MediaContentDescription* offer) {
zhihuang1c378ed2017-08-17 21:10:50674 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
Taylor Brandstetter5f0b83b2016-03-18 22:02:07675 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
676 offer->set_rtcp_reduced_size(true);
677 }
Markus Handell755c65d2020-06-23 23:06:10678
679 // Build the vector of header extensions with directions for this
680 // media_description's options.
681 RtpHeaderExtensions extensions;
682 for (auto extension_with_id : rtp_extensions) {
683 for (const auto& extension : media_description_options.header_extensions) {
684 if (extension_with_id.uri == extension.uri) {
685 // TODO(crbug.com/1051821): Configure the extension direction from
686 // the information in the media_description_options extension
Philipp Hanckeb396e2b2023-03-22 16:01:26687 // capability.
Philipp Hancke49e55872023-03-30 11:51:39688 if (extension.direction != RtpTransceiverDirection::kStopped) {
689 extensions.push_back(extension_with_id);
690 }
Markus Handell755c65d2020-06-23 23:06:10691 }
692 }
693 }
694 offer->set_rtp_header_extensions(extensions);
henrike@webrtc.org28e20752013-07-10 00:45:36695
Amit Hilbuchc63ddb22019-01-02 18:13:58696 AddSimulcastToMediaDescription(media_description_options, offer);
697
Harald Alvestrand0d018412021-11-04 13:52:31698 if (secure_policy != SEC_DISABLED) {
699 if (current_cryptos) {
700 AddMediaCryptos(*current_cryptos, offer);
701 }
702 if (offer->cryptos().empty()) {
703 if (!CreateMediaCryptos(crypto_suites, offer)) {
704 return false;
705 }
706 }
707 }
708
709 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
710 return false;
711 }
henrike@webrtc.org28e20752013-07-10 00:45:36712 return true;
713}
Harald Alvestrand5fc28b12019-05-13 11:36:16714template <class C>
715static bool CreateMediaContentOffer(
716 const MediaDescriptionOptions& media_description_options,
717 const MediaSessionOptions& session_options,
718 const std::vector<C>& codecs,
Harald Alvestrand0d018412021-11-04 13:52:31719 const SecurePolicy& secure_policy,
720 const CryptoParamsVec* current_cryptos,
721 const std::vector<std::string>& crypto_suites,
Harald Alvestrand5fc28b12019-05-13 11:36:16722 const RtpHeaderExtensions& rtp_extensions,
723 UniqueRandomIdGenerator* ssrc_generator,
724 StreamParamsVec* current_streams,
Jonas Orelanded99dae2022-03-09 08:28:10725 MediaContentDescriptionImpl<C>* offer,
Jonas Orelande62c2f22022-03-29 09:04:48726 const webrtc::FieldTrialsView& field_trials) {
Harald Alvestrand5fc28b12019-05-13 11:36:16727 offer->AddCodecs(codecs);
728 if (!AddStreamParams(media_description_options.sender_options,
729 session_options.rtcp_cname, ssrc_generator,
Jonas Orelanded99dae2022-03-09 08:28:10730 current_streams, offer, field_trials)) {
Harald Alvestrand5fc28b12019-05-13 11:36:16731 return false;
732 }
733
734 return CreateContentOffer(media_description_options, session_options,
Harald Alvestrand0d018412021-11-04 13:52:31735 secure_policy, current_cryptos, crypto_suites,
Harald Alvestrand5fc28b12019-05-13 11:36:16736 rtp_extensions, ssrc_generator, current_streams,
737 offer);
738}
henrike@webrtc.org28e20752013-07-10 00:45:36739
740template <class C>
Jonas Orelande62c2f22022-03-29 09:04:48741static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
742 const int codec1_id,
743 const std::vector<C>& codecs2,
744 const int codec2_id,
745 const webrtc::FieldTrialsView* field_trials) {
magjedb05fa242016-11-11 12:00:16746 const C* codec1 = FindCodecById(codecs1, codec1_id);
747 const C* codec2 = FindCodecById(codecs2, codec2_id);
Jonas Oreland4476b822022-03-10 14:21:28748 return codec1 != nullptr && codec2 != nullptr &&
749 codec1->Matches(*codec2, field_trials);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34750}
751
752template <class C>
Mirta Dvornicic479a3c02019-06-04 13:38:50753static void NegotiatePacketization(const C& local_codec,
754 const C& remote_codec,
755 C* negotiated_codec) {}
756
757template <>
758void NegotiatePacketization(const VideoCodec& local_codec,
759 const VideoCodec& remote_codec,
760 VideoCodec* negotiated_codec) {
761 negotiated_codec->packetization =
762 VideoCodec::IntersectPacketization(local_codec, remote_codec);
763}
764
765template <class C>
henrike@webrtc.org28e20752013-07-10 00:45:36766static void NegotiateCodecs(const std::vector<C>& local_codecs,
767 const std::vector<C>& offered_codecs,
Florent Castelli2d9d82e2019-04-23 17:25:51768 std::vector<C>* negotiated_codecs,
Jonas Oreland4476b822022-03-10 14:21:28769 bool keep_offer_order,
Jonas Orelande62c2f22022-03-29 09:04:48770 const webrtc::FieldTrialsView* field_trials) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56771 for (const C& ours : local_codecs) {
772 C theirs;
deadbeef67cf2c12016-04-13 17:07:16773 // Note that we intentionally only find one matching codec for each of our
774 // local codecs, in case the remote offer contains duplicate codecs.
Jonas Oreland4476b822022-03-10 14:21:28775 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs,
776 field_trials)) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56777 C negotiated = ours;
Mirta Dvornicic479a3c02019-06-04 13:38:50778 NegotiatePacketization(ours, theirs, &negotiated);
Taylor Brandstetter6ec641b2016-03-05 00:47:56779 negotiated.IntersectFeedbackParams(theirs);
780 if (IsRtxCodec(negotiated)) {
magjedb05fa242016-11-11 12:00:16781 const auto apt_it =
782 theirs.params.find(kCodecParamAssociatedPayloadType);
Taylor Brandstetter6ec641b2016-03-05 00:47:56783 // FindMatchingCodec shouldn't return something with no apt value.
magjedb05fa242016-11-11 12:00:16784 RTC_DCHECK(apt_it != theirs.params.end());
785 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
Philipp Hancke006206d2021-03-24 16:49:02786
787 // We support parsing the declarative rtx-time parameter.
788 const auto rtx_time_it = theirs.params.find(kCodecParamRtxTime);
789 if (rtx_time_it != theirs.params.end()) {
790 negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second);
791 }
Philipp Hancke7145a142021-09-28 05:46:06792 } else if (IsRedCodec(negotiated)) {
793 const auto red_it = theirs.params.find(kCodecParamNotInNameValueFormat);
794 if (red_it != theirs.params.end()) {
795 negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second);
796 }
henrike@webrtc.org28e20752013-07-10 00:45:36797 }
Niels Möller039743e2018-10-23 08:07:25798 if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) {
Johannes Kronc3fcee72021-04-19 07:09:26799 webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs.params,
800 &negotiated.params);
magjedf823ede2016-11-12 17:53:04801 }
Taylor Brandstetter6ec641b2016-03-05 00:47:56802 negotiated.id = theirs.id;
ossu075af922016-06-14 10:29:38803 negotiated.name = theirs.name;
magjedb05fa242016-11-11 12:00:16804 negotiated_codecs->push_back(std::move(negotiated));
henrike@webrtc.org28e20752013-07-10 00:45:36805 }
806 }
Florent Castelli2d9d82e2019-04-23 17:25:51807 if (keep_offer_order) {
808 // RFC3264: Although the answerer MAY list the formats in their desired
809 // order of preference, it is RECOMMENDED that unless there is a
810 // specific reason, the answerer list formats in the same relative order
811 // they were present in the offer.
812 // This can be skipped when the transceiver has any codec preferences.
813 std::unordered_map<int, int> payload_type_preferences;
814 int preference = static_cast<int>(offered_codecs.size() + 1);
815 for (const C& codec : offered_codecs) {
816 payload_type_preferences[codec.id] = preference--;
817 }
818 absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
819 const C& b) {
820 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
821 });
deadbeef67cf2c12016-04-13 17:07:16822 }
henrike@webrtc.org28e20752013-07-10 00:45:36823}
824
Artem Titov880fa812021-07-30 20:30:23825// Finds a codec in `codecs2` that matches `codec_to_match`, which is
Philipp Hancke7145a142021-09-28 05:46:06826// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
Taylor Brandstetter6ec641b2016-03-05 00:47:56827// the codecs themselves and their associated codecs must match.
henrike@webrtc.org28e20752013-07-10 00:45:36828template <class C>
Jonas Orelande62c2f22022-03-29 09:04:48829static bool FindMatchingCodec(const std::vector<C>& codecs1,
830 const std::vector<C>& codecs2,
831 const C& codec_to_match,
832 C* found_codec,
833 const webrtc::FieldTrialsView* field_trials) {
Philipp Hancke7145a142021-09-28 05:46:06834 // `codec_to_match` should be a member of `codecs1`, in order to look up
835 // RED/RTX codecs' associated codecs correctly. If not, that's a programming
836 // error.
Steve Anton64b626b2019-01-29 01:25:26837 RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
838 return &codec == &codec_to_match;
839 }));
Taylor Brandstetter6ec641b2016-03-05 00:47:56840 for (const C& potential_match : codecs2) {
Jonas Oreland4476b822022-03-10 14:21:28841 if (potential_match.Matches(codec_to_match, field_trials)) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56842 if (IsRtxCodec(codec_to_match)) {
magjedb05fa242016-11-11 12:00:16843 int apt_value_1 = 0;
844 int apt_value_2 = 0;
Taylor Brandstetter6ec641b2016-03-05 00:47:56845 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
846 &apt_value_1) ||
847 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
848 &apt_value_2)) {
Mirko Bonadei675513b2017-11-09 10:09:25849 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
Taylor Brandstetter6ec641b2016-03-05 00:47:56850 continue;
851 }
Jonas Oreland4476b822022-03-10 14:21:28852 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, apt_value_2,
853 field_trials)) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56854 continue;
855 }
Philipp Hancke7145a142021-09-28 05:46:06856 } else if (IsRedCodec(codec_to_match)) {
857 auto red_parameters_1 =
858 codec_to_match.params.find(kCodecParamNotInNameValueFormat);
859 auto red_parameters_2 =
860 potential_match.params.find(kCodecParamNotInNameValueFormat);
861 bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end();
862 bool has_parameters_2 =
863 red_parameters_2 != potential_match.params.end();
864 if (has_parameters_1 && has_parameters_2) {
865 // Mixed reference codecs (i.e. 111/112) are not supported.
866 // Different levels of redundancy between offer and answer are
867 // since RED is considered to be declarative.
Niels Möllerf1d822b2022-06-07 11:58:27868 std::vector<absl::string_view> redundant_payloads_1 =
869 rtc::split(red_parameters_1->second, '/');
870 std::vector<absl::string_view> redundant_payloads_2 =
871 rtc::split(red_parameters_2->second, '/');
Philipp Hancke7145a142021-09-28 05:46:06872 if (redundant_payloads_1.size() > 0 &&
873 redundant_payloads_2.size() > 0) {
874 bool consistent = true;
875 for (size_t i = 1; i < redundant_payloads_1.size(); i++) {
876 if (redundant_payloads_1[i] != redundant_payloads_1[0]) {
877 consistent = false;
878 break;
879 }
880 }
881 for (size_t i = 1; i < redundant_payloads_2.size(); i++) {
882 if (redundant_payloads_2[i] != redundant_payloads_2[0]) {
883 consistent = false;
884 break;
885 }
886 }
887 if (!consistent) {
888 continue;
889 }
890
891 int red_value_1;
892 int red_value_2;
893 if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
894 rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
895 if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
Jonas Oreland4476b822022-03-10 14:21:28896 red_value_2, field_trials)) {
Philipp Hancke7145a142021-09-28 05:46:06897 continue;
898 }
899 }
900 }
901 } else if (has_parameters_1 != has_parameters_2) {
902 continue;
903 }
Taylor Brandstetter6ec641b2016-03-05 00:47:56904 }
905 if (found_codec) {
906 *found_codec = potential_match;
henrike@webrtc.org28e20752013-07-10 00:45:36907 }
908 return true;
909 }
910 }
911 return false;
912}
913
Artem Titov880fa812021-07-30 20:30:23914// Find the codec in `codec_list` that `rtx_codec` is associated with.
zhihuang1c378ed2017-08-17 21:10:50915template <class C>
Philipp Hancke7145a142021-09-28 05:46:06916static const C* GetAssociatedCodecForRtx(const std::vector<C>& codec_list,
917 const C& rtx_codec) {
zhihuang1c378ed2017-08-17 21:10:50918 std::string associated_pt_str;
919 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
920 &associated_pt_str)) {
Mirko Bonadei675513b2017-11-09 10:09:25921 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
922 << " is missing an associated payload type.";
zhihuang1c378ed2017-08-17 21:10:50923 return nullptr;
924 }
925
926 int associated_pt;
927 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
Mirko Bonadei675513b2017-11-09 10:09:25928 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
929 << " of RTX codec " << rtx_codec.name
930 << " to an integer.";
zhihuang1c378ed2017-08-17 21:10:50931 return nullptr;
932 }
933
Philipp Hancke7145a142021-09-28 05:46:06934 // Find the associated codec for the RTX codec.
zhihuang1c378ed2017-08-17 21:10:50935 const C* associated_codec = FindCodecById(codec_list, associated_pt);
936 if (!associated_codec) {
Mirko Bonadei675513b2017-11-09 10:09:25937 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
938 << associated_pt << " for RTX codec " << rtx_codec.name
939 << ".";
zhihuang1c378ed2017-08-17 21:10:50940 }
941 return associated_codec;
942}
943
Philipp Hancke7145a142021-09-28 05:46:06944// Find the codec in `codec_list` that `red_codec` is associated with.
945template <class C>
946static const C* GetAssociatedCodecForRed(const std::vector<C>& codec_list,
947 const C& red_codec) {
948 std::string fmtp;
949 if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) {
950 // Normal for video/RED.
Tommi5c5b7b32023-04-18 10:04:58951 if constexpr (std::is_same_v<C, AudioCodec>) {
952 RTC_LOG(LS_WARNING) << "RED codec " << red_codec.name
953 << " is missing an associated payload type.";
954 }
Philipp Hancke7145a142021-09-28 05:46:06955 return nullptr;
956 }
957
Niels Möllerf1d822b2022-06-07 11:58:27958 std::vector<absl::string_view> redundant_payloads = rtc::split(fmtp, '/');
Philipp Hancke7145a142021-09-28 05:46:06959 if (redundant_payloads.size() < 2) {
960 return nullptr;
961 }
962
Niels Möllerf1d822b2022-06-07 11:58:27963 absl::string_view associated_pt_str = redundant_payloads[0];
Philipp Hancke7145a142021-09-28 05:46:06964 int associated_pt;
965 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
966 RTC_LOG(LS_WARNING) << "Couldn't convert first payload type "
967 << associated_pt_str << " of RED codec "
968 << red_codec.name << " to an integer.";
969 return nullptr;
970 }
971
972 // Find the associated codec for the RED codec.
973 const C* associated_codec = FindCodecById(codec_list, associated_pt);
974 if (!associated_codec) {
975 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
976 << associated_pt << " for RED codec " << red_codec.name
977 << ".";
978 }
979 return associated_codec;
980}
981
Artem Titov880fa812021-07-30 20:30:23982// Adds all codecs from `reference_codecs` to `offered_codecs` that don't
983// already exist in `offered_codecs` and ensure the payload types don't
henrike@webrtc.org28e20752013-07-10 00:45:36984// collide.
985template <class C>
zhihuang1c378ed2017-08-17 21:10:50986static void MergeCodecs(const std::vector<C>& reference_codecs,
987 std::vector<C>* offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:28988 UsedPayloadTypes* used_pltypes,
Jonas Orelande62c2f22022-03-29 09:04:48989 const webrtc::FieldTrialsView* field_trials) {
Philipp Hancke7145a142021-09-28 05:46:06990 // Add all new codecs that are not RTX/RED codecs.
991 // The two-pass splitting of the loops means preferring payload types
992 // of actual codecs with respect to collisions.
Taylor Brandstetter6ec641b2016-03-05 00:47:56993 for (const C& reference_codec : reference_codecs) {
Philipp Hancke7145a142021-09-28 05:46:06994 if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) &&
Taylor Brandstetter6ec641b2016-03-05 00:47:56995 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:28996 reference_codec, nullptr, field_trials)) {
Taylor Brandstetter6ec641b2016-03-05 00:47:56997 C codec = reference_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36998 used_pltypes->FindAndSetIdUsed(&codec);
999 offered_codecs->push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:361000 }
1001 }
1002
Philipp Hancke7145a142021-09-28 05:46:061003 // Add all new RTX or RED codecs.
Taylor Brandstetter6ec641b2016-03-05 00:47:561004 for (const C& reference_codec : reference_codecs) {
1005 if (IsRtxCodec(reference_codec) &&
1006 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:281007 reference_codec, nullptr, field_trials)) {
Taylor Brandstetter6ec641b2016-03-05 00:47:561008 C rtx_codec = reference_codec;
olka3c747662017-08-17 13:50:321009 const C* associated_codec =
Philipp Hancke7145a142021-09-28 05:46:061010 GetAssociatedCodecForRtx(reference_codecs, rtx_codec);
olka3c747662017-08-17 13:50:321011 if (!associated_codec) {
olka3c747662017-08-17 13:50:321012 continue;
1013 }
Taylor Brandstetter6ec641b2016-03-05 00:47:561014 // Find a codec in the offered list that matches the reference codec.
1015 // Its payload type may be different than the reference codec.
1016 C matching_codec;
1017 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:281018 *associated_codec, &matching_codec,
1019 field_trials)) {
Mirko Bonadei675513b2017-11-09 10:09:251020 RTC_LOG(LS_WARNING)
1021 << "Couldn't find matching " << associated_codec->name << " codec.";
Taylor Brandstetter6ec641b2016-03-05 00:47:561022 continue;
1023 }
1024
1025 rtx_codec.params[kCodecParamAssociatedPayloadType] =
1026 rtc::ToString(matching_codec.id);
1027 used_pltypes->FindAndSetIdUsed(&rtx_codec);
1028 offered_codecs->push_back(rtx_codec);
Philipp Hancke7145a142021-09-28 05:46:061029 } else if (IsRedCodec(reference_codec) &&
1030 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:281031 reference_codec, nullptr, field_trials)) {
Philipp Hancke7145a142021-09-28 05:46:061032 C red_codec = reference_codec;
1033 const C* associated_codec =
1034 GetAssociatedCodecForRed(reference_codecs, red_codec);
1035 if (associated_codec) {
1036 C matching_codec;
1037 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
Jonas Oreland4476b822022-03-10 14:21:281038 *associated_codec, &matching_codec,
1039 field_trials)) {
Philipp Hancke7145a142021-09-28 05:46:061040 RTC_LOG(LS_WARNING) << "Couldn't find matching "
1041 << associated_codec->name << " codec.";
1042 continue;
1043 }
1044
1045 red_codec.params[kCodecParamNotInNameValueFormat] =
1046 rtc::ToString(matching_codec.id) + "/" +
1047 rtc::ToString(matching_codec.id);
1048 }
1049 used_pltypes->FindAndSetIdUsed(&red_codec);
1050 offered_codecs->push_back(red_codec);
Taylor Brandstetter6ec641b2016-03-05 00:47:561051 }
henrike@webrtc.org28e20752013-07-10 00:45:361052 }
1053}
1054
Taylor Brandstetterf7fcfb72021-09-09 20:39:381055// `codecs` is a full list of codecs with correct payload type mappings, which
1056// don't conflict with mappings of the other media type; `supported_codecs` is
1057// a list filtered for the media section`s direction but with default payload
1058// types.
Florent Castelli2d9d82e2019-04-23 17:25:511059template <typename Codecs>
1060static Codecs MatchCodecPreference(
1061 const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
Taylor Brandstetterf7fcfb72021-09-09 20:39:381062 const Codecs& codecs,
Jonas Oreland4476b822022-03-10 14:21:281063 const Codecs& supported_codecs,
Jonas Orelande62c2f22022-03-29 09:04:481064 const webrtc::FieldTrialsView* field_trials) {
Florent Castelli2d9d82e2019-04-23 17:25:511065 Codecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 17:25:511066 bool want_rtx = false;
Philipp Hancke7145a142021-09-28 05:46:061067 bool want_red = false;
Florent Castelli2d9d82e2019-04-23 17:25:511068
1069 for (const auto& codec_preference : codec_preferences) {
Philipp Hanckec13e7862021-10-19 16:27:281070 if (IsRtxCodec(codec_preference)) {
1071 want_rtx = true;
1072 } else if (IsRedCodec(codec_preference)) {
1073 want_red = true;
1074 }
1075 }
1076 for (const auto& codec_preference : codec_preferences) {
Florent Castelli2d9d82e2019-04-23 17:25:511077 auto found_codec = absl::c_find_if(
Taylor Brandstetterf7fcfb72021-09-09 20:39:381078 supported_codecs,
1079 [&codec_preference](const typename Codecs::value_type& codec) {
Florent Castelli2d9d82e2019-04-23 17:25:511080 webrtc::RtpCodecParameters codec_parameters =
1081 codec.ToCodecParameters();
1082 return codec_parameters.name == codec_preference.name &&
1083 codec_parameters.kind == codec_preference.kind &&
1084 codec_parameters.num_channels ==
1085 codec_preference.num_channels &&
1086 codec_parameters.clock_rate == codec_preference.clock_rate &&
1087 codec_parameters.parameters == codec_preference.parameters;
1088 });
1089
Taylor Brandstetterf7fcfb72021-09-09 20:39:381090 if (found_codec != supported_codecs.end()) {
1091 typename Codecs::value_type found_codec_with_correct_pt;
1092 if (FindMatchingCodec(supported_codecs, codecs, *found_codec,
Jonas Oreland4476b822022-03-10 14:21:281093 &found_codec_with_correct_pt, field_trials)) {
Taylor Brandstetterf7fcfb72021-09-09 20:39:381094 filtered_codecs.push_back(found_codec_with_correct_pt);
Philipp Hanckec13e7862021-10-19 16:27:281095 std::string id = rtc::ToString(found_codec_with_correct_pt.id);
1096 // Search for the matching rtx or red codec.
1097 if (want_red || want_rtx) {
1098 for (const auto& codec : codecs) {
1099 if (IsRtxCodec(codec)) {
1100 const auto apt =
1101 codec.params.find(cricket::kCodecParamAssociatedPayloadType);
1102 if (apt != codec.params.end() && apt->second == id) {
1103 filtered_codecs.push_back(codec);
1104 break;
1105 }
1106 } else if (IsRedCodec(codec)) {
Philipp Hancke60c01cc2021-11-15 13:54:581107 // For RED, do not insert the codec again if it was already
1108 // inserted. audio/red for opus gets enabled by having RED before
1109 // the primary codec.
Philipp Hanckec13e7862021-10-19 16:27:281110 const auto fmtp =
1111 codec.params.find(cricket::kCodecParamNotInNameValueFormat);
1112 if (fmtp != codec.params.end()) {
Niels Möllerf1d822b2022-06-07 11:58:271113 std::vector<absl::string_view> redundant_payloads =
1114 rtc::split(fmtp->second, '/');
Philipp Hanckec13e7862021-10-19 16:27:281115 if (redundant_payloads.size() > 0 &&
1116 redundant_payloads[0] == id) {
Philipp Hancke60c01cc2021-11-15 13:54:581117 if (std::find(filtered_codecs.begin(), filtered_codecs.end(),
1118 codec) == filtered_codecs.end()) {
1119 filtered_codecs.push_back(codec);
1120 }
Philipp Hanckec13e7862021-10-19 16:27:281121 break;
1122 }
1123 }
1124 }
Philipp Hancke7145a142021-09-28 05:46:061125 }
1126 }
Florent Castelli2d9d82e2019-04-23 17:25:511127 }
1128 }
1129 }
1130
1131 return filtered_codecs;
1132}
1133
Stefan Mitic3aa99372022-02-03 12:26:221134// Compute the union of `codecs1` and `codecs2`.
1135template <class C>
Jonas Orelande62c2f22022-03-29 09:04:481136std::vector<C> ComputeCodecsUnion(const std::vector<C>& codecs1,
1137 const std::vector<C>& codecs2,
1138 const webrtc::FieldTrialsView* field_trials) {
Stefan Mitic3aa99372022-02-03 12:26:221139 std::vector<C> all_codecs;
1140 UsedPayloadTypes used_payload_types;
1141 for (const C& codec : codecs1) {
1142 C codec_mutable = codec;
1143 used_payload_types.FindAndSetIdUsed(&codec_mutable);
1144 all_codecs.push_back(codec_mutable);
1145 }
1146
1147 // Use MergeCodecs to merge the second half of our list as it already checks
1148 // and fixes problems with duplicate payload types.
Jonas Oreland4476b822022-03-10 14:21:281149 MergeCodecs<C>(codecs2, &all_codecs, &used_payload_types, field_trials);
Stefan Mitic3aa99372022-02-03 12:26:221150
1151 return all_codecs;
1152}
1153
Artem Titov880fa812021-07-30 20:30:231154// Adds all extensions from `reference_extensions` to `offered_extensions` that
1155// don't already exist in `offered_extensions` and ensure the IDs don't
1156// collide. If an extension is added, it's also added to `regular_extensions` or
1157// `encrypted_extensions`, and if the extension is in `regular_extensions` or
1158// `encrypted_extensions`, its ID is marked as used in `used_ids`.
1159// `offered_extensions` is for either audio or video while `regular_extensions`
1160// and `encrypted_extensions` are used for both audio and video. There could be
zhihuang1c378ed2017-08-17 21:10:501161// overlap between audio extensions and video extensions.
1162static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1163 RtpHeaderExtensions* offered_extensions,
1164 RtpHeaderExtensions* regular_extensions,
1165 RtpHeaderExtensions* encrypted_extensions,
1166 UsedRtpHeaderExtensionIds* used_ids) {
olka3c747662017-08-17 13:50:321167 for (auto reference_extension : reference_extensions) {
Lennart Grahl0d0ed762021-05-17 14:06:371168 if (!webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1169 *offered_extensions, reference_extension.uri,
1170 reference_extension.encrypt)) {
zhihuang1c378ed2017-08-17 21:10:501171 if (reference_extension.encrypt) {
Lennart Grahl0d0ed762021-05-17 14:06:371172 const webrtc::RtpExtension* existing =
1173 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1174 *encrypted_extensions, reference_extension.uri,
1175 reference_extension.encrypt);
1176 if (existing) {
1177 offered_extensions->push_back(*existing);
zhihuang1c378ed2017-08-17 21:10:501178 } else {
1179 used_ids->FindAndSetIdUsed(&reference_extension);
1180 encrypted_extensions->push_back(reference_extension);
1181 offered_extensions->push_back(reference_extension);
1182 }
olka3c747662017-08-17 13:50:321183 } else {
Lennart Grahl0d0ed762021-05-17 14:06:371184 const webrtc::RtpExtension* existing =
1185 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1186 *regular_extensions, reference_extension.uri,
1187 reference_extension.encrypt);
1188 if (existing) {
1189 offered_extensions->push_back(*existing);
zhihuang1c378ed2017-08-17 21:10:501190 } else {
1191 used_ids->FindAndSetIdUsed(&reference_extension);
1192 regular_extensions->push_back(reference_extension);
1193 offered_extensions->push_back(reference_extension);
1194 }
henrike@webrtc.org79047f92014-03-06 23:46:591195 }
henrike@webrtc.org28e20752013-07-10 00:45:361196 }
1197 }
1198}
1199
Lennart Grahl0d0ed762021-05-17 14:06:371200static void AddEncryptedVersionsOfHdrExts(
1201 RtpHeaderExtensions* offered_extensions,
1202 RtpHeaderExtensions* encrypted_extensions,
1203 UsedRtpHeaderExtensionIds* used_ids) {
1204 RtpHeaderExtensions encrypted_extensions_to_add;
1205 for (const auto& extension : *offered_extensions) {
1206 // Skip existing encrypted offered extension
1207 if (extension.encrypt) {
jbauch5869f502017-06-29 19:31:361208 continue;
1209 }
1210
Lennart Grahl0d0ed762021-05-17 14:06:371211 // Skip if we cannot encrypt the extension
1212 if (!webrtc::RtpExtension::IsEncryptionSupported(extension.uri)) {
1213 continue;
jbauch5869f502017-06-29 19:31:361214 }
Lennart Grahl0d0ed762021-05-17 14:06:371215
1216 // Skip if an encrypted extension with that URI already exists in the
1217 // offered extensions.
1218 const bool have_encrypted_extension =
1219 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1220 *offered_extensions, extension.uri, true);
1221 if (have_encrypted_extension) {
1222 continue;
1223 }
1224
1225 // Determine if a shared encrypted extension with that URI already exists.
1226 const webrtc::RtpExtension* shared_encrypted_extension =
1227 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1228 *encrypted_extensions, extension.uri, true);
1229 if (shared_encrypted_extension) {
1230 // Re-use the shared encrypted extension
1231 encrypted_extensions_to_add.push_back(*shared_encrypted_extension);
1232 continue;
1233 }
1234
1235 // None exists. Create a new shared encrypted extension from the
1236 // non-encrypted one.
1237 webrtc::RtpExtension new_encrypted_extension(extension);
1238 new_encrypted_extension.encrypt = true;
1239 used_ids->FindAndSetIdUsed(&new_encrypted_extension);
1240 encrypted_extensions->push_back(new_encrypted_extension);
1241 encrypted_extensions_to_add.push_back(new_encrypted_extension);
jbauch5869f502017-06-29 19:31:361242 }
Lennart Grahl0d0ed762021-05-17 14:06:371243
1244 // Append the additional encrypted extensions to be offered
1245 offered_extensions->insert(offered_extensions->end(),
1246 encrypted_extensions_to_add.begin(),
1247 encrypted_extensions_to_add.end());
1248}
1249
1250// Mostly identical to RtpExtension::FindHeaderExtensionByUri but discards any
1251// encrypted extensions that this implementation cannot encrypt.
1252static const webrtc::RtpExtension* FindHeaderExtensionByUriDiscardUnsupported(
1253 const std::vector<webrtc::RtpExtension>& extensions,
1254 absl::string_view uri,
1255 webrtc::RtpExtension::Filter filter) {
1256 // Note: While it's technically possible to decrypt extensions that we don't
1257 // encrypt, the symmetric API of libsrtp does not allow us to supply
1258 // different IDs for encryption/decryption of header extensions depending on
1259 // whether the packet is inbound or outbound. Thereby, we are limited to
1260 // what we can send in encrypted form.
1261 if (!webrtc::RtpExtension::IsEncryptionSupported(uri)) {
1262 // If there's no encryption support and we only want encrypted extensions,
1263 // there's no point in continuing the search here.
1264 if (filter == webrtc::RtpExtension::kRequireEncryptedExtension) {
1265 return nullptr;
1266 }
1267
1268 // Instruct to only return non-encrypted extensions
1269 filter = webrtc::RtpExtension::Filter::kDiscardEncryptedExtension;
1270 }
1271
1272 return webrtc::RtpExtension::FindHeaderExtensionByUri(extensions, uri,
1273 filter);
jbauch5869f502017-06-29 19:31:361274}
1275
henrike@webrtc.org28e20752013-07-10 00:45:361276static void NegotiateRtpHeaderExtensions(
1277 const RtpHeaderExtensions& local_extensions,
1278 const RtpHeaderExtensions& offered_extensions,
Lennart Grahl0d0ed762021-05-17 14:06:371279 webrtc::RtpExtension::Filter filter,
Johannes Kronce8e8672019-02-22 12:06:441280 RtpHeaderExtensions* negotiated_extensions) {
1281 // TransportSequenceNumberV2 is not offered by default. The special logic for
1282 // the TransportSequenceNumber extensions works as follows:
1283 // Offer Answer
1284 // V1 V1 if in local_extensions.
1285 // V1 and V2 V2 regardless of local_extensions.
1286 // V2 V2 regardless of local_extensions.
1287 const webrtc::RtpExtension* transport_sequence_number_v2_offer =
Lennart Grahl0d0ed762021-05-17 14:06:371288 FindHeaderExtensionByUriDiscardUnsupported(
Johannes Kronce8e8672019-02-22 12:06:441289 offered_extensions,
Lennart Grahl0d0ed762021-05-17 14:06:371290 webrtc::RtpExtension::kTransportSequenceNumberV2Uri, filter);
Johannes Kronce8e8672019-02-22 12:06:441291
Markus Handellc1cbf6b2020-02-17 19:03:571292 bool frame_descriptor_in_local = false;
Danil Chapovalov5f999a72020-02-20 15:39:051293 bool dependency_descriptor_in_local = false;
Minyue Li430e4a02020-03-10 09:59:371294 bool abs_capture_time_in_local = false;
1295
Steve Anton3a66edf2018-09-10 19:57:371296 for (const webrtc::RtpExtension& ours : local_extensions) {
Markus Handellc1cbf6b2020-02-17 19:03:571297 if (ours.uri == webrtc::RtpExtension::kGenericFrameDescriptorUri00)
1298 frame_descriptor_in_local = true;
Danil Chapovalov5f999a72020-02-20 15:39:051299 else if (ours.uri == webrtc::RtpExtension::kDependencyDescriptorUri)
1300 dependency_descriptor_in_local = true;
Minyue Li430e4a02020-03-10 09:59:371301 else if (ours.uri == webrtc::RtpExtension::kAbsoluteCaptureTimeUri)
1302 abs_capture_time_in_local = true;
Lennart Grahl0d0ed762021-05-17 14:06:371303 const webrtc::RtpExtension* theirs =
1304 FindHeaderExtensionByUriDiscardUnsupported(offered_extensions, ours.uri,
1305 filter);
1306 if (theirs) {
Johannes Kronce8e8672019-02-22 12:06:441307 if (transport_sequence_number_v2_offer &&
1308 ours.uri == webrtc::RtpExtension::kTransportSequenceNumberUri) {
1309 // Don't respond to
1310 // http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
1311 // if we get an offer including
Johannes Kron8cc711a2019-03-07 21:36:351312 // http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02
Johannes Kronce8e8672019-02-22 12:06:441313 continue;
1314 } else {
1315 // We respond with their RTP header extension id.
Lennart Grahl0d0ed762021-05-17 14:06:371316 negotiated_extensions->push_back(*theirs);
Johannes Kronce8e8672019-02-22 12:06:441317 }
henrike@webrtc.org28e20752013-07-10 00:45:361318 }
1319 }
Johannes Kronce8e8672019-02-22 12:06:441320
1321 if (transport_sequence_number_v2_offer) {
1322 // Respond that we support kTransportSequenceNumberV2Uri.
1323 negotiated_extensions->push_back(*transport_sequence_number_v2_offer);
1324 }
Markus Handellc1cbf6b2020-02-17 19:03:571325
Danil Chapovalov5f999a72020-02-20 15:39:051326 // Frame descriptors support. If the extension is not present locally, but is
Markus Handellc1cbf6b2020-02-17 19:03:571327 // in the offer, we add it to the list.
Lennart Grahl0d0ed762021-05-17 14:06:371328 if (!dependency_descriptor_in_local) {
1329 const webrtc::RtpExtension* theirs =
1330 FindHeaderExtensionByUriDiscardUnsupported(
1331 offered_extensions, webrtc::RtpExtension::kDependencyDescriptorUri,
1332 filter);
1333 if (theirs) {
1334 negotiated_extensions->push_back(*theirs);
1335 }
Danil Chapovalov5f999a72020-02-20 15:39:051336 }
Lennart Grahl0d0ed762021-05-17 14:06:371337 if (!frame_descriptor_in_local) {
1338 const webrtc::RtpExtension* theirs =
1339 FindHeaderExtensionByUriDiscardUnsupported(
1340 offered_extensions,
1341 webrtc::RtpExtension::kGenericFrameDescriptorUri00, filter);
1342 if (theirs) {
1343 negotiated_extensions->push_back(*theirs);
1344 }
Markus Handellc1cbf6b2020-02-17 19:03:571345 }
Minyue Li430e4a02020-03-10 09:59:371346
1347 // Absolute capture time support. If the extension is not present locally, but
1348 // is in the offer, we add it to the list.
Lennart Grahl0d0ed762021-05-17 14:06:371349 if (!abs_capture_time_in_local) {
1350 const webrtc::RtpExtension* theirs =
1351 FindHeaderExtensionByUriDiscardUnsupported(
1352 offered_extensions, webrtc::RtpExtension::kAbsoluteCaptureTimeUri,
1353 filter);
1354 if (theirs) {
1355 negotiated_extensions->push_back(*theirs);
1356 }
Minyue Li430e4a02020-03-10 09:59:371357 }
henrike@webrtc.org28e20752013-07-10 00:45:361358}
1359
1360static void StripCNCodecs(AudioCodecs* audio_codecs) {
Steve Anton3a66edf2018-09-10 19:57:371361 audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
1362 [](const AudioCodec& codec) {
Philipp Hancke2f3168f2022-05-16 12:41:321363 return IsComfortNoiseCodec(codec);
Steve Anton3a66edf2018-09-10 19:57:371364 }),
1365 audio_codecs->end());
henrike@webrtc.org28e20752013-07-10 00:45:361366}
1367
Harald Alvestrand5fc28b12019-05-13 11:36:161368template <class C>
1369static bool SetCodecsInAnswer(
1370 const MediaContentDescriptionImpl<C>* offer,
1371 const std::vector<C>& local_codecs,
1372 const MediaDescriptionOptions& media_description_options,
1373 const MediaSessionOptions& session_options,
1374 UniqueRandomIdGenerator* ssrc_generator,
1375 StreamParamsVec* current_streams,
Jonas Orelanded99dae2022-03-09 08:28:101376 MediaContentDescriptionImpl<C>* answer,
Jonas Orelande62c2f22022-03-29 09:04:481377 const webrtc::FieldTrialsView& field_trials) {
Harald Alvestrand5fc28b12019-05-13 11:36:161378 std::vector<C> negotiated_codecs;
1379 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
Jonas Oreland4476b822022-03-10 14:21:281380 media_description_options.codec_preferences.empty(),
1381 &field_trials);
Harald Alvestrand5fc28b12019-05-13 11:36:161382 answer->AddCodecs(negotiated_codecs);
1383 answer->set_protocol(offer->protocol());
1384 if (!AddStreamParams(media_description_options.sender_options,
1385 session_options.rtcp_cname, ssrc_generator,
Jonas Orelanded99dae2022-03-09 08:28:101386 current_streams, answer, field_trials)) {
Harald Alvestrand5fc28b12019-05-13 11:36:161387 return false; // Something went seriously wrong.
1388 }
1389 return true;
1390}
1391
Artem Titov880fa812021-07-30 20:30:231392// Create a media content to be answered for the given `sender_options`
zhihuang1c378ed2017-08-17 21:10:501393// according to the given session_options.rtcp_mux, session_options.streams,
Harald Alvestrand0d018412021-11-04 13:52:311394// codecs, crypto, and current_streams. If we don't currently have crypto (in
1395// current_cryptos) and it is enabled (in secure_policy), crypto is created
1396// (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
zhihuang1c378ed2017-08-17 21:10:501397// negotiated with the offer. If the negotiation fails, this method returns
1398// false. The created content is added to the offer.
henrike@webrtc.org28e20752013-07-10 00:45:361399static bool CreateMediaContentAnswer(
Harald Alvestrand5fc28b12019-05-13 11:36:161400 const MediaContentDescription* offer,
zhihuang1c378ed2017-08-17 21:10:501401 const MediaDescriptionOptions& media_description_options,
1402 const MediaSessionOptions& session_options,
Harald Alvestrand0d018412021-11-04 13:52:311403 const SecurePolicy& sdes_policy,
1404 const CryptoParamsVec* current_cryptos,
Markus Handell755c65d2020-06-23 23:06:101405 const RtpHeaderExtensions& local_rtp_extensions,
Amit Hilbuchbcd39d42019-01-26 01:13:561406 UniqueRandomIdGenerator* ssrc_generator,
jbauch5869f502017-06-29 19:31:361407 bool enable_encrypted_rtp_header_extensions,
henrike@webrtc.org28e20752013-07-10 00:45:361408 StreamParamsVec* current_streams,
henrike@webrtc.org28e20752013-07-10 00:45:361409 bool bundle_enabled,
Harald Alvestrand5fc28b12019-05-13 11:36:161410 MediaContentDescription* answer) {
Johannes Kron9581bc42018-10-23 08:17:391411 answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum());
Lennart Grahl0d0ed762021-05-17 14:06:371412 const webrtc::RtpExtension::Filter extensions_filter =
1413 enable_encrypted_rtp_header_extensions
1414 ? webrtc::RtpExtension::Filter::kPreferEncryptedExtension
1415 : webrtc::RtpExtension::Filter::kDiscardEncryptedExtension;
Philipp Hancked4fe3ce2023-03-31 11:33:421416
1417 // Filter local extensions by capabilities and direction.
1418 RtpHeaderExtensions local_rtp_extensions_to_reply_with;
1419 for (auto extension_with_id : local_rtp_extensions) {
1420 for (const auto& extension : media_description_options.header_extensions) {
1421 if (extension_with_id.uri == extension.uri) {
1422 // TODO(crbug.com/1051821): Configure the extension direction from
1423 // the information in the media_description_options extension
1424 // capability. For now, do not include stopped extensions.
1425 // See also crbug.com/webrtc/7477 about the general lack of direction.
1426 if (extension.direction != RtpTransceiverDirection::kStopped) {
1427 local_rtp_extensions_to_reply_with.push_back(extension_with_id);
1428 }
1429 }
1430 }
1431 }
henrike@webrtc.org28e20752013-07-10 00:45:361432 RtpHeaderExtensions negotiated_rtp_extensions;
Philipp Hancked4fe3ce2023-03-31 11:33:421433 NegotiateRtpHeaderExtensions(local_rtp_extensions_to_reply_with,
Lennart Grahl0d0ed762021-05-17 14:06:371434 offer->rtp_header_extensions(),
1435 extensions_filter, &negotiated_rtp_extensions);
henrike@webrtc.org28e20752013-07-10 00:45:361436 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1437
zhihuang1c378ed2017-08-17 21:10:501438 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
Taylor Brandstetter5f0b83b2016-03-18 22:02:071439 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1440 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1441 }
henrike@webrtc.org28e20752013-07-10 00:45:361442
Sebastian Janssone1795f42019-07-24 09:38:031443 answer->set_remote_estimate(offer->remote_estimate());
1444
Harald Alvestrand0d018412021-11-04 13:52:311445 if (sdes_policy != SEC_DISABLED) {
1446 CryptoParams crypto;
1447 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1448 &crypto)) {
1449 if (current_cryptos) {
1450 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1451 }
1452 answer->AddCrypto(crypto);
1453 }
1454 }
1455
1456 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
1457 return false;
1458 }
1459
Amit Hilbuchc63ddb22019-01-02 18:13:581460 AddSimulcastToMediaDescription(media_description_options, answer);
1461
Steve Anton4e70a722017-11-28 22:57:101462 answer->set_direction(NegotiateRtpTransceiverDirection(
1463 offer->direction(), media_description_options.direction));
Bjorn A Mellem8e1343a2019-09-30 22:12:471464
henrike@webrtc.org28e20752013-07-10 00:45:361465 return true;
1466}
1467
1468static bool IsMediaProtocolSupported(MediaType type,
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591469 const std::string& protocol,
1470 bool secure_transport) {
zhihuangcf5b37c2016-05-05 18:44:351471 // Since not all applications serialize and deserialize the media protocol,
Artem Titov880fa812021-07-30 20:30:231472 // we will have to accept `protocol` to be empty.
zhihuangcf5b37c2016-05-05 18:44:351473 if (protocol.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:361474 return true;
1475 }
jiayl@webrtc.org8dcd43c2014-05-29 22:07:591476
zhihuangcf5b37c2016-05-05 18:44:351477 if (type == MEDIA_TYPE_DATA) {
Harald Alvestrand1bffe9e2021-11-02 10:39:591478 // Check for SCTP
zhihuangcf5b37c2016-05-05 18:44:351479 if (secure_transport) {
1480 // Most likely scenarios first.
Harald Alvestrand1bffe9e2021-11-02 10:39:591481 return IsDtlsSctp(protocol);
zhihuangcf5b37c2016-05-05 18:44:351482 } else {
Harald Alvestrand1bffe9e2021-11-02 10:39:591483 return IsPlainSctp(protocol);
zhihuangcf5b37c2016-05-05 18:44:351484 }
1485 }
1486
1487 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1488 // JSEP specifies.
1489 if (secure_transport) {
1490 // Most likely scenarios first.
1491 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1492 } else {
1493 return IsPlainRtp(protocol);
1494 }
henrike@webrtc.org28e20752013-07-10 00:45:361495}
1496
1497static void SetMediaProtocol(bool secure_transport,
1498 MediaContentDescription* desc) {
Harald Alvestrand0d018412021-11-04 13:52:311499 if (!desc->cryptos().empty())
1500 desc->set_protocol(kMediaProtocolSavpf);
1501 else if (secure_transport)
deadbeeff3938292015-07-15 19:20:531502 desc->set_protocol(kMediaProtocolDtlsSavpf);
henrike@webrtc.org28e20752013-07-10 00:45:361503 else
1504 desc->set_protocol(kMediaProtocolAvpf);
1505}
1506
Artem Titov880fa812021-07-30 20:30:231507// Gets the TransportInfo of the given `content_name` from the
1508// `current_description`. If doesn't exist, returns a new one.
mallinath@webrtc.org19f27e62013-10-13 17:18:271509static const TransportDescription* GetTransportDescription(
1510 const std::string& content_name,
1511 const SessionDescription* current_description) {
1512 const TransportDescription* desc = NULL;
1513 if (current_description) {
1514 const TransportInfo* info =
1515 current_description->GetTransportInfoByName(content_name);
1516 if (info) {
1517 desc = &info->description;
1518 }
1519 }
1520 return desc;
1521}
1522
Harald Alvestrand0d018412021-11-04 13:52:311523// Gets the current DTLS state from the transport description.
1524static bool IsDtlsActive(const ContentInfo* content,
1525 const SessionDescription* current_description) {
1526 if (!content) {
1527 return false;
1528 }
1529
1530 size_t msection_index = content - &current_description->contents()[0];
1531
1532 if (current_description->transport_infos().size() <= msection_index) {
1533 return false;
1534 }
1535
1536 return current_description->transport_infos()[msection_index]
1537 .description.secure();
1538}
1539
Steve Anton8ffb9c32017-08-31 22:45:381540void MediaDescriptionOptions::AddAudioSender(
1541 const std::string& track_id,
1542 const std::vector<std::string>& stream_ids) {
zhihuang1c378ed2017-08-17 21:10:501543 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
Amit Hilbuchc63ddb22019-01-02 18:13:581544 AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1);
wu@webrtc.orgcecfd182013-10-30 05:18:121545}
1546
Steve Anton8ffb9c32017-08-31 22:45:381547void MediaDescriptionOptions::AddVideoSender(
1548 const std::string& track_id,
1549 const std::vector<std::string>& stream_ids,
Amit Hilbuchc63ddb22019-01-02 18:13:581550 const std::vector<RidDescription>& rids,
1551 const SimulcastLayerList& simulcast_layers,
Steve Anton8ffb9c32017-08-31 22:45:381552 int num_sim_layers) {
zhihuang1c378ed2017-08-17 21:10:501553 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
Amit Hilbuchc63ddb22019-01-02 18:13:581554 RTC_DCHECK(rids.empty() || num_sim_layers == 0)
1555 << "RIDs are the compliant way to indicate simulcast.";
1556 RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers));
1557 AddSenderInternal(track_id, stream_ids, rids, simulcast_layers,
1558 num_sim_layers);
wu@webrtc.orgcecfd182013-10-30 05:18:121559}
1560
Steve Anton8ffb9c32017-08-31 22:45:381561void MediaDescriptionOptions::AddSenderInternal(
1562 const std::string& track_id,
1563 const std::vector<std::string>& stream_ids,
Amit Hilbuchc63ddb22019-01-02 18:13:581564 const std::vector<RidDescription>& rids,
1565 const SimulcastLayerList& simulcast_layers,
Steve Anton8ffb9c32017-08-31 22:45:381566 int num_sim_layers) {
1567 // TODO(steveanton): Support any number of stream ids.
1568 RTC_CHECK(stream_ids.size() == 1U);
Amit Hilbuchc63ddb22019-01-02 18:13:581569 SenderOptions options;
1570 options.track_id = track_id;
1571 options.stream_ids = stream_ids;
1572 options.simulcast_layers = simulcast_layers;
1573 options.rids = rids;
1574 options.num_sim_layers = num_sim_layers;
1575 sender_options.push_back(options);
henrike@webrtc.org28e20752013-07-10 00:45:361576}
1577
zhihuang1c378ed2017-08-17 21:10:501578bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
Steve Anton64b626b2019-01-29 01:25:261579 return absl::c_any_of(
1580 media_description_options,
1581 [type](const MediaDescriptionOptions& t) { return t.type == type; });
jiayl@webrtc.org742922b2014-10-07 21:32:431582}
1583
henrike@webrtc.org28e20752013-07-10 00:45:361584MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
Amit Hilbuchbcd39d42019-01-26 01:13:561585 const TransportDescriptionFactory* transport_desc_factory,
1586 rtc::UniqueRandomIdGenerator* ssrc_generator)
1587 : ssrc_generator_(ssrc_generator),
Harald Alvestrand8101e7b2022-05-23 14:57:471588 transport_desc_factory_(transport_desc_factory) {}
henrike@webrtc.org28e20752013-07-10 00:45:361589
1590MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
Harald Alvestrandc3fa7c32022-05-22 10:57:011591 cricket::MediaEngineInterface* media_engine,
1592 bool rtx_enabled,
1593 rtc::UniqueRandomIdGenerator* ssrc_generator,
Tomas Gunnarsson5411b172022-01-24 07:45:261594 const TransportDescriptionFactory* transport_desc_factory)
Harald Alvestrandc3fa7c32022-05-22 10:57:011595 : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
1596 if (media_engine) {
1597 audio_send_codecs_ = media_engine->voice().send_codecs();
1598 audio_recv_codecs_ = media_engine->voice().recv_codecs();
1599 video_send_codecs_ = media_engine->video().send_codecs(rtx_enabled);
1600 video_recv_codecs_ = media_engine->video().recv_codecs(rtx_enabled);
1601 }
Artem Titovc6c02ef2022-05-09 08:30:091602 ComputeAudioCodecsIntersectionAndUnion();
1603 ComputeVideoCodecsIntersectionAndUnion();
ossu075af922016-06-14 10:29:381604}
1605
ossudedfd282016-06-14 14:12:391606const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1607 const {
ossu075af922016-06-14 10:29:381608 return audio_sendrecv_codecs_;
1609}
1610
1611const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1612 return audio_send_codecs_;
1613}
1614
1615const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1616 return audio_recv_codecs_;
1617}
1618
1619void MediaSessionDescriptionFactory::set_audio_codecs(
Yves Gerey665174f2018-06-19 13:03:051620 const AudioCodecs& send_codecs,
1621 const AudioCodecs& recv_codecs) {
ossu075af922016-06-14 10:29:381622 audio_send_codecs_ = send_codecs;
1623 audio_recv_codecs_ = recv_codecs;
zhihuang1c378ed2017-08-17 21:10:501624 ComputeAudioCodecsIntersectionAndUnion();
henrike@webrtc.org28e20752013-07-10 00:45:361625}
1626
Johannes Kron3e983682020-03-29 20:17:001627const VideoCodecs& MediaSessionDescriptionFactory::video_sendrecv_codecs()
1628 const {
1629 return video_sendrecv_codecs_;
1630}
1631
1632const VideoCodecs& MediaSessionDescriptionFactory::video_send_codecs() const {
1633 return video_send_codecs_;
1634}
1635
1636const VideoCodecs& MediaSessionDescriptionFactory::video_recv_codecs() const {
1637 return video_recv_codecs_;
1638}
1639
1640void MediaSessionDescriptionFactory::set_video_codecs(
1641 const VideoCodecs& send_codecs,
1642 const VideoCodecs& recv_codecs) {
1643 video_send_codecs_ = send_codecs;
1644 video_recv_codecs_ = recv_codecs;
1645 ComputeVideoCodecsIntersectionAndUnion();
1646}
1647
Florent Castelli80385412019-10-15 13:24:531648static void RemoveUnifiedPlanExtensions(RtpHeaderExtensions* extensions) {
Amit Hilbuch77938e62018-12-21 17:23:381649 RTC_DCHECK(extensions);
Elad Alon157540a2019-02-08 22:37:521650
Florent Castelli80385412019-10-15 13:24:531651 extensions->erase(
1652 std::remove_if(extensions->begin(), extensions->end(),
1653 [](auto extension) {
1654 return extension.uri == webrtc::RtpExtension::kMidUri ||
1655 extension.uri == webrtc::RtpExtension::kRidUri ||
1656 extension.uri ==
1657 webrtc::RtpExtension::kRepairedRidUri;
1658 }),
1659 extensions->end());
Amit Hilbuch77938e62018-12-21 17:23:381660}
1661
1662RtpHeaderExtensions
Markus Handell755c65d2020-06-23 23:06:101663MediaSessionDescriptionFactory::filtered_rtp_header_extensions(
1664 RtpHeaderExtensions extensions) const {
Florent Castelli80385412019-10-15 13:24:531665 if (!is_unified_plan_) {
1666 RemoveUnifiedPlanExtensions(&extensions);
Amit Hilbuch77938e62018-12-21 17:23:381667 }
Amit Hilbuch77938e62018-12-21 17:23:381668 return extensions;
1669}
1670
Steve Anton6fe1fba2018-12-11 18:15:231671std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
zhihuang1c378ed2017-08-17 21:10:501672 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361673 const SessionDescription* current_description) const {
Steve Anton5c72e712018-12-10 22:25:301674 // Must have options for each existing section.
1675 if (current_description) {
1676 RTC_DCHECK_LE(current_description->contents().size(),
1677 session_options.media_description_options.size());
1678 }
henrike@webrtc.org28e20752013-07-10 00:45:361679
Jonas Oreland1cd39fa2018-10-11 05:47:121680 IceCredentialsIterator ice_credentials(
1681 session_options.pooled_ice_credentials);
Steve Anton5c72e712018-12-10 22:25:301682
1683 std::vector<const ContentInfo*> current_active_contents;
1684 if (current_description) {
1685 current_active_contents =
1686 GetActiveContents(*current_description, session_options);
1687 }
1688
1689 StreamParamsVec current_streams =
1690 GetCurrentStreamParams(current_active_contents);
henrike@webrtc.org28e20752013-07-10 00:45:361691
zhihuang1c378ed2017-08-17 21:10:501692 AudioCodecs offer_audio_codecs;
1693 VideoCodecs offer_video_codecs;
Harald Alvestrand7af57c62021-04-16 11:12:141694 GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
1695 &offer_video_codecs);
Markus Handell755c65d2020-06-23 23:06:101696 AudioVideoRtpHeaderExtensions extensions_with_ids =
1697 GetOfferedRtpHeaderExtensionsWithIds(
1698 current_active_contents, session_options.offer_extmap_allow_mixed,
1699 session_options.media_description_options);
henrike@webrtc.org28e20752013-07-10 00:45:361700
Mirko Bonadei317a1f02019-09-17 15:06:181701 auto offer = std::make_unique<SessionDescription>();
jiayl@webrtc.org742922b2014-10-07 21:32:431702
zhihuang1c378ed2017-08-17 21:10:501703 // Iterate through the media description options, matching with existing media
Artem Titov880fa812021-07-30 20:30:231704 // descriptions in `current_description`.
Steve Antondcc3c022017-12-23 00:02:541705 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 21:10:501706 for (const MediaDescriptionOptions& media_description_options :
1707 session_options.media_description_options) {
1708 const ContentInfo* current_content = nullptr;
1709 if (current_description &&
Steve Antondcc3c022017-12-23 00:02:541710 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 21:10:501711 current_content = &current_description->contents()[msection_index];
Steve Antondcc3c022017-12-23 00:02:541712 // Media type must match unless this media section is being recycled.
Steve Anton5c72e712018-12-10 22:25:301713 RTC_DCHECK(current_content->name != media_description_options.mid ||
Steve Antondcc3c022017-12-23 00:02:541714 IsMediaContentOfType(current_content,
zhihuang1c378ed2017-08-17 21:10:501715 media_description_options.type));
1716 }
1717 switch (media_description_options.type) {
1718 case MEDIA_TYPE_AUDIO:
Markus Handell755c65d2020-06-23 23:06:101719 if (!AddAudioContentForOffer(media_description_options, session_options,
1720 current_content, current_description,
1721 extensions_with_ids.audio,
1722 offer_audio_codecs, &current_streams,
1723 offer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501724 return nullptr;
1725 }
1726 break;
1727 case MEDIA_TYPE_VIDEO:
Markus Handell755c65d2020-06-23 23:06:101728 if (!AddVideoContentForOffer(media_description_options, session_options,
1729 current_content, current_description,
1730 extensions_with_ids.video,
1731 offer_video_codecs, &current_streams,
1732 offer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501733 return nullptr;
1734 }
1735 break;
1736 case MEDIA_TYPE_DATA:
1737 if (!AddDataContentForOffer(media_description_options, session_options,
1738 current_content, current_description,
Harald Alvestrand7af57c62021-04-16 11:12:141739 &current_streams, offer.get(),
1740 &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501741 return nullptr;
1742 }
1743 break;
Philipp Hancke4e8c1152020-10-13 10:43:151744 case MEDIA_TYPE_UNSUPPORTED:
1745 if (!AddUnsupportedContentForOffer(
1746 media_description_options, session_options, current_content,
1747 current_description, offer.get(), &ice_credentials)) {
1748 return nullptr;
1749 }
1750 break;
zhihuang1c378ed2017-08-17 21:10:501751 default:
Artem Titovd3251962021-11-15 15:57:071752 RTC_DCHECK_NOTREACHED();
zhihuang1c378ed2017-08-17 21:10:501753 }
1754 ++msection_index;
henrike@webrtc.org28e20752013-07-10 00:45:361755 }
1756
1757 // Bundle the contents together, if we've been asked to do so, and update any
1758 // parameters that need to be tweaked for BUNDLE.
Steve Anton2bed3972019-01-05 01:04:301759 if (session_options.bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:361760 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
zhihuang1c378ed2017-08-17 21:10:501761 for (const ContentInfo& content : offer->contents()) {
Steve Anton2bed3972019-01-05 01:04:301762 if (content.rejected) {
1763 continue;
1764 }
zhihuang1c378ed2017-08-17 21:10:501765 // TODO(deadbeef): There are conditions that make bundling two media
1766 // descriptions together illegal. For example, they use the same payload
1767 // type to represent different codecs, or same IDs for different header
1768 // extensions. We need to detect this and not try to bundle those media
1769 // descriptions together.
1770 offer_bundle.AddContentName(content.name);
henrike@webrtc.org28e20752013-07-10 00:45:361771 }
Steve Anton2bed3972019-01-05 01:04:301772 if (!offer_bundle.content_names().empty()) {
1773 offer->AddGroup(offer_bundle);
1774 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1775 RTC_LOG(LS_ERROR)
1776 << "CreateOffer failed to UpdateTransportInfoForBundle.";
1777 return nullptr;
1778 }
Harald Alvestrand0d018412021-11-04 13:52:311779 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1780 RTC_LOG(LS_ERROR)
1781 << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1782 return nullptr;
1783 }
henrike@webrtc.org28e20752013-07-10 00:45:361784 }
1785 }
Steve Antone831b8c2018-02-01 20:22:161786
1787 // The following determines how to signal MSIDs to ensure compatibility with
1788 // older endpoints (in particular, older Plan B endpoints).
Steve Anton8f66ddb2018-12-11 00:08:051789 if (is_unified_plan_) {
Steve Antone831b8c2018-02-01 20:22:161790 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1791 // Plan answerers will look at a=msid and Plan B answerers will look at the
1792 // a=ssrc MSID line.
1793 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1794 cricket::kMsidSignalingSsrcAttribute);
1795 } else {
1796 // Plan B always signals MSID using a=ssrc lines.
1797 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1798 }
1799
Johannes Kron89f874e2018-11-12 09:25:481800 offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
1801
Steve Anton6fe1fba2018-12-11 18:15:231802 return offer;
henrike@webrtc.org28e20752013-07-10 00:45:361803}
1804
Steve Anton6fe1fba2018-12-11 18:15:231805std::unique_ptr<SessionDescription>
1806MediaSessionDescriptionFactory::CreateAnswer(
zhihuang1c378ed2017-08-17 21:10:501807 const SessionDescription* offer,
1808 const MediaSessionOptions& session_options,
henrike@webrtc.org28e20752013-07-10 00:45:361809 const SessionDescription* current_description) const {
deadbeefb7892532017-02-23 03:35:181810 if (!offer) {
1811 return nullptr;
1812 }
Jonas Oreland1cd39fa2018-10-11 05:47:121813
Steve Anton5c72e712018-12-10 22:25:301814 // Must have options for exactly as many sections as in the offer.
1815 RTC_DCHECK_EQ(offer->contents().size(),
1816 session_options.media_description_options.size());
1817
Jonas Oreland1cd39fa2018-10-11 05:47:121818 IceCredentialsIterator ice_credentials(
1819 session_options.pooled_ice_credentials);
1820
Steve Anton5c72e712018-12-10 22:25:301821 std::vector<const ContentInfo*> current_active_contents;
1822 if (current_description) {
1823 current_active_contents =
1824 GetActiveContents(*current_description, session_options);
1825 }
henrike@webrtc.org28e20752013-07-10 00:45:361826
Steve Anton5c72e712018-12-10 22:25:301827 StreamParamsVec current_streams =
1828 GetCurrentStreamParams(current_active_contents);
Johannes Kron0854eb62018-10-10 20:33:201829
zhihuang1c378ed2017-08-17 21:10:501830 // Get list of all possible codecs that respects existing payload type
1831 // mappings and uses a single payload type space.
1832 //
1833 // Note that these lists may be further filtered for each m= section; this
1834 // step is done just to establish the payload type mappings shared by all
1835 // sections.
1836 AudioCodecs answer_audio_codecs;
1837 VideoCodecs answer_video_codecs;
Steve Anton5c72e712018-12-10 22:25:301838 GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
Harald Alvestrand7af57c62021-04-16 11:12:141839 &answer_video_codecs);
zhihuang1c378ed2017-08-17 21:10:501840
Mirko Bonadei317a1f02019-09-17 15:06:181841 auto answer = std::make_unique<SessionDescription>();
Steve Anton5c72e712018-12-10 22:25:301842
1843 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1844 // group in the answer with the appropriate content names.
Henrik Boströmf8187e02021-04-26 19:04:261845 std::vector<const ContentGroup*> offer_bundles =
1846 offer->GetGroupsByName(GROUP_TYPE_BUNDLE);
1847 // There are as many answer BUNDLE groups as offer BUNDLE groups (even if
Artem Titov880fa812021-07-30 20:30:231848 // rejected, we respond with an empty group). `offer_bundles`,
1849 // `answer_bundles` and `bundle_transports` share the same size and indices.
Henrik Boströmf8187e02021-04-26 19:04:261850 std::vector<ContentGroup> answer_bundles;
1851 std::vector<std::unique_ptr<TransportInfo>> bundle_transports;
1852 answer_bundles.reserve(offer_bundles.size());
1853 bundle_transports.reserve(offer_bundles.size());
1854 for (size_t i = 0; i < offer_bundles.size(); ++i) {
1855 answer_bundles.emplace_back(GROUP_TYPE_BUNDLE);
1856 bundle_transports.emplace_back(nullptr);
1857 }
Steve Anton5c72e712018-12-10 22:25:301858
1859 answer->set_extmap_allow_mixed(offer->extmap_allow_mixed());
1860
zhihuang1c378ed2017-08-17 21:10:501861 // Iterate through the media description options, matching with existing
Artem Titov880fa812021-07-30 20:30:231862 // media descriptions in `current_description`.
Steve Antondcc3c022017-12-23 00:02:541863 size_t msection_index = 0;
zhihuang1c378ed2017-08-17 21:10:501864 for (const MediaDescriptionOptions& media_description_options :
1865 session_options.media_description_options) {
1866 const ContentInfo* offer_content = &offer->contents()[msection_index];
1867 // Media types and MIDs must match between the remote offer and the
1868 // MediaDescriptionOptions.
1869 RTC_DCHECK(
1870 IsMediaContentOfType(offer_content, media_description_options.type));
1871 RTC_DCHECK(media_description_options.mid == offer_content->name);
Henrik Boströmf8187e02021-04-26 19:04:261872 // Get the index of the BUNDLE group that this MID belongs to, if any.
1873 absl::optional<size_t> bundle_index;
1874 for (size_t i = 0; i < offer_bundles.size(); ++i) {
1875 if (offer_bundles[i]->HasContentName(media_description_options.mid)) {
1876 bundle_index = i;
1877 break;
1878 }
1879 }
1880 TransportInfo* bundle_transport =
1881 bundle_index.has_value() ? bundle_transports[bundle_index.value()].get()
1882 : nullptr;
Harald Alvestrand0d018412021-11-04 13:52:311883
zhihuang1c378ed2017-08-17 21:10:501884 const ContentInfo* current_content = nullptr;
1885 if (current_description &&
Steve Antondcc3c022017-12-23 00:02:541886 msection_index < current_description->contents().size()) {
zhihuang1c378ed2017-08-17 21:10:501887 current_content = &current_description->contents()[msection_index];
deadbeefb7892532017-02-23 03:35:181888 }
Markus Handell755c65d2020-06-23 23:06:101889 RtpHeaderExtensions header_extensions = RtpHeaderExtensionsFromCapabilities(
1890 UnstoppedRtpHeaderExtensionCapabilities(
1891 media_description_options.header_extensions));
zhihuang1c378ed2017-08-17 21:10:501892 switch (media_description_options.type) {
1893 case MEDIA_TYPE_AUDIO:
1894 if (!AddAudioContentForAnswer(
1895 media_description_options, session_options, offer_content,
Henrik Boströmf8187e02021-04-26 19:04:261896 offer, current_content, current_description, bundle_transport,
Harald Alvestrand0d018412021-11-04 13:52:311897 answer_audio_codecs, header_extensions, &current_streams,
1898 answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501899 return nullptr;
1900 }
1901 break;
1902 case MEDIA_TYPE_VIDEO:
1903 if (!AddVideoContentForAnswer(
1904 media_description_options, session_options, offer_content,
Henrik Boströmf8187e02021-04-26 19:04:261905 offer, current_content, current_description, bundle_transport,
Harald Alvestrand0d018412021-11-04 13:52:311906 answer_video_codecs, header_extensions, &current_streams,
1907 answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501908 return nullptr;
1909 }
1910 break;
1911 case MEDIA_TYPE_DATA:
Henrik Boströmf8187e02021-04-26 19:04:261912 if (!AddDataContentForAnswer(
1913 media_description_options, session_options, offer_content,
1914 offer, current_content, current_description, bundle_transport,
Harald Alvestrand0d018412021-11-04 13:52:311915 &current_streams, answer.get(), &ice_credentials)) {
zhihuang1c378ed2017-08-17 21:10:501916 return nullptr;
1917 }
1918 break;
Philipp Hancke4e8c1152020-10-13 10:43:151919 case MEDIA_TYPE_UNSUPPORTED:
1920 if (!AddUnsupportedContentForAnswer(
1921 media_description_options, session_options, offer_content,
Henrik Boströmf8187e02021-04-26 19:04:261922 offer, current_content, current_description, bundle_transport,
Harald Alvestrand0d018412021-11-04 13:52:311923 answer.get(), &ice_credentials)) {
Philipp Hancke4e8c1152020-10-13 10:43:151924 return nullptr;
1925 }
1926 break;
zhihuang1c378ed2017-08-17 21:10:501927 default:
Artem Titovd3251962021-11-15 15:57:071928 RTC_DCHECK_NOTREACHED();
zhihuang1c378ed2017-08-17 21:10:501929 }
1930 ++msection_index;
deadbeefb7892532017-02-23 03:35:181931 // See if we can add the newly generated m= section to the BUNDLE group in
1932 // the answer.
1933 ContentInfo& added = answer->contents().back();
Henrik Boströmf8187e02021-04-26 19:04:261934 if (!added.rejected && session_options.bundle_enabled &&
1935 bundle_index.has_value()) {
Artem Titovcfea2182021-08-09 23:22:311936 // The `bundle_index` is for `media_description_options.mid`.
Henrik Boströmf8187e02021-04-26 19:04:261937 RTC_DCHECK_EQ(media_description_options.mid, added.name);
1938 answer_bundles[bundle_index.value()].AddContentName(added.name);
1939 bundle_transports[bundle_index.value()].reset(
deadbeefb7892532017-02-23 03:35:181940 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
henrike@webrtc.org28e20752013-07-10 00:45:361941 }
henrike@webrtc.org28e20752013-07-10 00:45:361942 }
1943
Henrik Boströmf8187e02021-04-26 19:04:261944 // If BUNDLE group(s) were offered, put the same number of BUNDLE groups in
1945 // the answer even if they're empty. RFC5888 says:
Taylor Brandstetter0ab56512018-04-12 17:30:481946 //
1947 // A SIP entity that receives an offer that contains an "a=group" line
1948 // with semantics that are understood MUST return an answer that
1949 // contains an "a=group" line with the same semantics.
Henrik Boströmf8187e02021-04-26 19:04:261950 if (!offer_bundles.empty()) {
1951 for (const ContentGroup& answer_bundle : answer_bundles) {
1952 answer->AddGroup(answer_bundle);
deadbeefb7892532017-02-23 03:35:181953
Henrik Boströmf8187e02021-04-26 19:04:261954 if (answer_bundle.FirstContentName()) {
1955 // Share the same ICE credentials and crypto params across all contents,
1956 // as BUNDLE requires.
1957 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1958 RTC_LOG(LS_ERROR)
1959 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1960 return NULL;
1961 }
Harald Alvestrand0d018412021-11-04 13:52:311962
1963 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1964 RTC_LOG(LS_ERROR)
1965 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1966 return NULL;
1967 }
Henrik Boströmf8187e02021-04-26 19:04:261968 }
henrike@webrtc.org28e20752013-07-10 00:45:361969 }
1970 }
1971
Steve Antone831b8c2018-02-01 20:22:161972 // The following determines how to signal MSIDs to ensure compatibility with
1973 // older endpoints (in particular, older Plan B endpoints).
Steve Anton8f66ddb2018-12-11 00:08:051974 if (is_unified_plan_) {
Steve Antone831b8c2018-02-01 20:22:161975 // Unified Plan needs to look at what the offer included to find the most
1976 // compatible answer.
1977 if (offer->msid_signaling() == 0) {
1978 // We end up here in one of three cases:
1979 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1980 // matter what we pick here.
1981 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1982 // it also doesn't matter what we pick here.
1983 // 3. Media that's either sendonly or inactive from the remote endpoint.
1984 // We don't have any information to say whether the endpoint is Plan B
1985 // or Unified Plan, so be conservative and send both.
1986 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1987 cricket::kMsidSignalingSsrcAttribute);
1988 } else if (offer->msid_signaling() ==
1989 (cricket::kMsidSignalingMediaSection |
1990 cricket::kMsidSignalingSsrcAttribute)) {
1991 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1992 // probably talking to a Unified Plan endpoint so respond with just
1993 // a=msid.
1994 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1995 } else {
1996 // Otherwise, it's clear which method the offerer is using so repeat that
1997 // back to them.
1998 answer->set_msid_signaling(offer->msid_signaling());
1999 }
2000 } else {
2001 // Plan B always signals MSID using a=ssrc lines.
2002 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
2003 }
2004
Steve Anton6fe1fba2018-12-11 18:15:232005 return answer;
henrike@webrtc.org28e20752013-07-10 00:45:362006}
2007
ossu075af922016-06-14 10:29:382008const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
2009 const RtpTransceiverDirection& direction) const {
Steve Anton1d03a752017-11-27 22:30:092010 switch (direction) {
2011 // If stream is inactive - generate list as if sendrecv.
2012 case RtpTransceiverDirection::kSendRecv:
Harald Alvestrand6060df52020-08-11 07:54:022013 case RtpTransceiverDirection::kStopped:
Steve Anton1d03a752017-11-27 22:30:092014 case RtpTransceiverDirection::kInactive:
2015 return audio_sendrecv_codecs_;
2016 case RtpTransceiverDirection::kSendOnly:
2017 return audio_send_codecs_;
2018 case RtpTransceiverDirection::kRecvOnly:
2019 return audio_recv_codecs_;
ossu075af922016-06-14 10:29:382020 }
Karl Wibergc95b9392020-11-07 23:49:372021 RTC_CHECK_NOTREACHED();
ossu075af922016-06-14 10:29:382022}
2023
2024const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
2025 const RtpTransceiverDirection& offer,
2026 const RtpTransceiverDirection& answer) const {
Steve Anton1d03a752017-11-27 22:30:092027 switch (answer) {
2028 // For inactive and sendrecv answers, generate lists as if we were to accept
2029 // the offer's direction. See RFC 3264 Section 6.1.
2030 case RtpTransceiverDirection::kSendRecv:
Harald Alvestrand6060df52020-08-11 07:54:022031 case RtpTransceiverDirection::kStopped:
Steve Anton1d03a752017-11-27 22:30:092032 case RtpTransceiverDirection::kInactive:
2033 return GetAudioCodecsForOffer(
2034 webrtc::RtpTransceiverDirectionReversed(offer));
2035 case RtpTransceiverDirection::kSendOnly:
ossu075af922016-06-14 10:29:382036 return audio_send_codecs_;
Steve Anton1d03a752017-11-27 22:30:092037 case RtpTransceiverDirection::kRecvOnly:
2038 return audio_recv_codecs_;
ossu075af922016-06-14 10:29:382039 }
Karl Wibergc95b9392020-11-07 23:49:372040 RTC_CHECK_NOTREACHED();
ossu075af922016-06-14 10:29:382041}
2042
Johannes Kron3e983682020-03-29 20:17:002043const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForOffer(
2044 const RtpTransceiverDirection& direction) const {
2045 switch (direction) {
2046 // If stream is inactive - generate list as if sendrecv.
2047 case RtpTransceiverDirection::kSendRecv:
Harald Alvestrand6060df52020-08-11 07:54:022048 case RtpTransceiverDirection::kStopped:
Johannes Kron3e983682020-03-29 20:17:002049 case RtpTransceiverDirection::kInactive:
2050 return video_sendrecv_codecs_;
2051 case RtpTransceiverDirection::kSendOnly:
2052 return video_send_codecs_;
2053 case RtpTransceiverDirection::kRecvOnly:
2054 return video_recv_codecs_;
Johannes Kron3e983682020-03-29 20:17:002055 }
Karl Wibergc95b9392020-11-07 23:49:372056 RTC_CHECK_NOTREACHED();
Johannes Kron3e983682020-03-29 20:17:002057}
2058
2059const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForAnswer(
2060 const RtpTransceiverDirection& offer,
2061 const RtpTransceiverDirection& answer) const {
2062 switch (answer) {
2063 // For inactive and sendrecv answers, generate lists as if we were to accept
2064 // the offer's direction. See RFC 3264 Section 6.1.
2065 case RtpTransceiverDirection::kSendRecv:
Harald Alvestrand6060df52020-08-11 07:54:022066 case RtpTransceiverDirection::kStopped:
Johannes Kron3e983682020-03-29 20:17:002067 case RtpTransceiverDirection::kInactive:
2068 return GetVideoCodecsForOffer(
2069 webrtc::RtpTransceiverDirectionReversed(offer));
2070 case RtpTransceiverDirection::kSendOnly:
2071 return video_send_codecs_;
2072 case RtpTransceiverDirection::kRecvOnly:
2073 return video_recv_codecs_;
Johannes Kron3e983682020-03-29 20:17:002074 }
Karl Wibergc95b9392020-11-07 23:49:372075 RTC_CHECK_NOTREACHED();
Johannes Kron3e983682020-03-29 20:17:002076}
2077
Steve Anton5c72e712018-12-10 22:25:302078void MergeCodecsFromDescription(
2079 const std::vector<const ContentInfo*>& current_active_contents,
2080 AudioCodecs* audio_codecs,
2081 VideoCodecs* video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282082 UsedPayloadTypes* used_pltypes,
Jonas Orelande62c2f22022-03-29 09:04:482083 const webrtc::FieldTrialsView* field_trials) {
Steve Anton5c72e712018-12-10 22:25:302084 for (const ContentInfo* content : current_active_contents) {
2085 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
zhihuang1c378ed2017-08-17 21:10:502086 const AudioContentDescription* audio =
Steve Anton5c72e712018-12-10 22:25:302087 content->media_description()->as_audio();
Jonas Oreland4476b822022-03-10 14:21:282088 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes,
2089 field_trials);
Steve Anton5c72e712018-12-10 22:25:302090 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
zhihuang1c378ed2017-08-17 21:10:502091 const VideoContentDescription* video =
Steve Anton5c72e712018-12-10 22:25:302092 content->media_description()->as_video();
Jonas Oreland4476b822022-03-10 14:21:282093 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes,
2094 field_trials);
zhihuang1c378ed2017-08-17 21:10:502095 }
2096 }
2097}
2098
2099// Getting codecs for an offer involves these steps:
2100//
2101// 1. Construct payload type -> codec mappings for current description.
2102// 2. Add any reference codecs that weren't already present
2103// 3. For each individual media description (m= section), filter codecs based
2104// on the directional attribute (happens in another method).
2105void MediaSessionDescriptionFactory::GetCodecsForOffer(
Steve Anton5c72e712018-12-10 22:25:302106 const std::vector<const ContentInfo*>& current_active_contents,
henrike@webrtc.org28e20752013-07-10 00:45:362107 AudioCodecs* audio_codecs,
Harald Alvestrand7af57c62021-04-16 11:12:142108 VideoCodecs* video_codecs) const {
Jonas Orelande62c2f22022-03-29 09:04:482109 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282110 &transport_desc_factory_->trials();
henrike@webrtc.org28e20752013-07-10 00:45:362111 // First - get all codecs from the current description if the media type
Artem Titov880fa812021-07-30 20:30:232112 // is used. Add them to `used_pltypes` so the payload type is not reused if a
zhihuang1c378ed2017-08-17 21:10:502113 // new media type is added.
Steve Anton5c72e712018-12-10 22:25:302114 UsedPayloadTypes used_pltypes;
2115 MergeCodecsFromDescription(current_active_contents, audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282116 video_codecs, &used_pltypes, field_trials);
henrike@webrtc.org28e20752013-07-10 00:45:362117
Steve Anton5c72e712018-12-10 22:25:302118 // Add our codecs that are not in the current description.
Jonas Oreland4476b822022-03-10 14:21:282119 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes,
2120 field_trials);
2121 MergeCodecs<VideoCodec>(all_video_codecs_, video_codecs, &used_pltypes,
2122 field_trials);
zhihuang1c378ed2017-08-17 21:10:502123}
2124
2125// Getting codecs for an answer involves these steps:
2126//
2127// 1. Construct payload type -> codec mappings for current description.
2128// 2. Add any codecs from the offer that weren't already present.
2129// 3. Add any remaining codecs that weren't already present.
2130// 4. For each individual media description (m= section), filter codecs based
2131// on the directional attribute (happens in another method).
2132void MediaSessionDescriptionFactory::GetCodecsForAnswer(
Steve Anton5c72e712018-12-10 22:25:302133 const std::vector<const ContentInfo*>& current_active_contents,
2134 const SessionDescription& remote_offer,
zhihuang1c378ed2017-08-17 21:10:502135 AudioCodecs* audio_codecs,
Harald Alvestrand7af57c62021-04-16 11:12:142136 VideoCodecs* video_codecs) const {
Jonas Orelande62c2f22022-03-29 09:04:482137 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282138 &transport_desc_factory_->trials();
zhihuang1c378ed2017-08-17 21:10:502139 // First - get all codecs from the current description if the media type
Artem Titov880fa812021-07-30 20:30:232140 // is used. Add them to `used_pltypes` so the payload type is not reused if a
zhihuang1c378ed2017-08-17 21:10:502141 // new media type is added.
Steve Anton5c72e712018-12-10 22:25:302142 UsedPayloadTypes used_pltypes;
2143 MergeCodecsFromDescription(current_active_contents, audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282144 video_codecs, &used_pltypes, field_trials);
zhihuang1c378ed2017-08-17 21:10:502145
2146 // Second - filter out codecs that we don't support at all and should ignore.
2147 AudioCodecs filtered_offered_audio_codecs;
2148 VideoCodecs filtered_offered_video_codecs;
Steve Anton5c72e712018-12-10 22:25:302149 for (const ContentInfo& content : remote_offer.contents()) {
zhihuang1c378ed2017-08-17 21:10:502150 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
2151 const AudioContentDescription* audio =
Steve Antonb1c1de12017-12-21 23:14:302152 content.media_description()->as_audio();
zhihuang1c378ed2017-08-17 21:10:502153 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
Jonas Oreland4476b822022-03-10 14:21:282154 if (!FindMatchingCodec<AudioCodec>(
2155 audio->codecs(), filtered_offered_audio_codecs,
2156 offered_audio_codec, nullptr, field_trials) &&
zhihuang1c378ed2017-08-17 21:10:502157 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
Jonas Oreland4476b822022-03-10 14:21:282158 offered_audio_codec, nullptr,
2159 field_trials)) {
zhihuang1c378ed2017-08-17 21:10:502160 filtered_offered_audio_codecs.push_back(offered_audio_codec);
2161 }
2162 }
2163 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
2164 const VideoContentDescription* video =
Steve Antonb1c1de12017-12-21 23:14:302165 content.media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:502166 for (const VideoCodec& offered_video_codec : video->codecs()) {
Jonas Oreland4476b822022-03-10 14:21:282167 if (!FindMatchingCodec<VideoCodec>(
2168 video->codecs(), filtered_offered_video_codecs,
2169 offered_video_codec, nullptr, field_trials) &&
Johannes Kron3e983682020-03-29 20:17:002170 FindMatchingCodec<VideoCodec>(video->codecs(), all_video_codecs_,
Jonas Oreland4476b822022-03-10 14:21:282171 offered_video_codec, nullptr,
2172 field_trials)) {
zhihuang1c378ed2017-08-17 21:10:502173 filtered_offered_video_codecs.push_back(offered_video_codec);
2174 }
2175 }
zhihuang1c378ed2017-08-17 21:10:502176 }
2177 }
2178
Steve Anton5c72e712018-12-10 22:25:302179 // Add codecs that are not in the current description but were in
Artem Titov880fa812021-07-30 20:30:232180 // `remote_offer`.
zhihuang1c378ed2017-08-17 21:10:502181 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282182 &used_pltypes, field_trials);
zhihuang1c378ed2017-08-17 21:10:502183 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282184 &used_pltypes, field_trials);
henrike@webrtc.org28e20752013-07-10 00:45:362185}
2186
Markus Handell755c65d2020-06-23 23:06:102187MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions
2188MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds(
Steve Anton5c72e712018-12-10 22:25:302189 const std::vector<const ContentInfo*>& current_active_contents,
Johannes Kron746dd0d2019-06-20 13:37:522190 bool extmap_allow_mixed,
Markus Handell755c65d2020-06-23 23:06:102191 const std::vector<MediaDescriptionOptions>& media_description_options)
2192 const {
henrike@webrtc.org79047f92014-03-06 23:46:592193 // All header extensions allocated from the same range to avoid potential
2194 // issues when using BUNDLE.
Johannes Kron746dd0d2019-06-20 13:37:522195
2196 // Strictly speaking the SDP attribute extmap_allow_mixed signals that the
2197 // receiver supports an RTP stream where one- and two-byte RTP header
2198 // extensions are mixed. For backwards compatibility reasons it's used in
2199 // WebRTC to signal that two-byte RTP header extensions are supported.
2200 UsedRtpHeaderExtensionIds used_ids(
2201 extmap_allow_mixed ? UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed
2202 : UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly);
jbauch5869f502017-06-29 19:31:362203 RtpHeaderExtensions all_regular_extensions;
2204 RtpHeaderExtensions all_encrypted_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:362205
Markus Handell755c65d2020-06-23 23:06:102206 AudioVideoRtpHeaderExtensions offered_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:362207 // First - get all extensions from the current description if the media type
2208 // is used.
Artem Titov880fa812021-07-30 20:30:232209 // Add them to `used_ids` so the local ids are not reused if a new media
henrike@webrtc.org28e20752013-07-10 00:45:362210 // type is added.
Steve Anton5c72e712018-12-10 22:25:302211 for (const ContentInfo* content : current_active_contents) {
2212 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
2213 const AudioContentDescription* audio =
2214 content->media_description()->as_audio();
Markus Handell755c65d2020-06-23 23:06:102215 MergeRtpHdrExts(audio->rtp_header_extensions(), &offered_extensions.audio,
Steve Anton5c72e712018-12-10 22:25:302216 &all_regular_extensions, &all_encrypted_extensions,
2217 &used_ids);
2218 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
2219 const VideoContentDescription* video =
2220 content->media_description()->as_video();
Markus Handell755c65d2020-06-23 23:06:102221 MergeRtpHdrExts(video->rtp_header_extensions(), &offered_extensions.video,
Steve Anton5c72e712018-12-10 22:25:302222 &all_regular_extensions, &all_encrypted_extensions,
2223 &used_ids);
henrike@webrtc.org28e20752013-07-10 00:45:362224 }
2225 }
2226
Markus Handell755c65d2020-06-23 23:06:102227 // Add all encountered header extensions in the media description options that
2228 // are not in the current description.
Markus Handell6f727da2020-06-12 15:24:542229
Markus Handell755c65d2020-06-23 23:06:102230 for (const auto& entry : media_description_options) {
2231 RtpHeaderExtensions filtered_extensions =
2232 filtered_rtp_header_extensions(UnstoppedOrPresentRtpHeaderExtensions(
2233 entry.header_extensions, all_regular_extensions,
2234 all_encrypted_extensions));
2235 if (entry.type == MEDIA_TYPE_AUDIO)
2236 MergeRtpHdrExts(filtered_extensions, &offered_extensions.audio,
2237 &all_regular_extensions, &all_encrypted_extensions,
2238 &used_ids);
2239 else if (entry.type == MEDIA_TYPE_VIDEO)
2240 MergeRtpHdrExts(filtered_extensions, &offered_extensions.video,
2241 &all_regular_extensions, &all_encrypted_extensions,
2242 &used_ids);
2243 }
jbauch5869f502017-06-29 19:31:362244 // TODO(jbauch): Support adding encrypted header extensions to existing
2245 // sessions.
Steve Anton5c72e712018-12-10 22:25:302246 if (enable_encrypted_rtp_header_extensions_ &&
2247 current_active_contents.empty()) {
Markus Handell755c65d2020-06-23 23:06:102248 AddEncryptedVersionsOfHdrExts(&offered_extensions.audio,
zhihuang1c378ed2017-08-17 21:10:502249 &all_encrypted_extensions, &used_ids);
Markus Handell755c65d2020-06-23 23:06:102250 AddEncryptedVersionsOfHdrExts(&offered_extensions.video,
zhihuang1c378ed2017-08-17 21:10:502251 &all_encrypted_extensions, &used_ids);
jbauch5869f502017-06-29 19:31:362252 }
Markus Handell755c65d2020-06-23 23:06:102253 return offered_extensions;
henrike@webrtc.org28e20752013-07-10 00:45:362254}
2255
2256bool MediaSessionDescriptionFactory::AddTransportOffer(
Yves Gerey665174f2018-06-19 13:03:052257 const std::string& content_name,
2258 const TransportOptions& transport_options,
2259 const SessionDescription* current_desc,
Jonas Oreland1cd39fa2018-10-11 05:47:122260 SessionDescription* offer_desc,
2261 IceCredentialsIterator* ice_credentials) const {
henrike@webrtc.org28e20752013-07-10 00:45:362262 if (!transport_desc_factory_)
Yves Gerey665174f2018-06-19 13:03:052263 return false;
henrike@webrtc.org28e20752013-07-10 00:45:362264 const TransportDescription* current_tdesc =
2265 GetTransportDescription(content_name, current_desc);
kwiberg31022942016-03-11 22:18:212266 std::unique_ptr<TransportDescription> new_tdesc(
Jonas Oreland1cd39fa2018-10-11 05:47:122267 transport_desc_factory_->CreateOffer(transport_options, current_tdesc,
2268 ice_credentials));
Steve Anton06817cd2018-12-18 23:55:302269 if (!new_tdesc) {
Mirko Bonadei675513b2017-11-09 10:09:252270 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
2271 << content_name;
henrike@webrtc.org28e20752013-07-10 00:45:362272 }
Steve Anton06817cd2018-12-18 23:55:302273 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc));
2274 return true;
henrike@webrtc.org28e20752013-07-10 00:45:362275}
2276
Steve Anton1a9d3c32018-12-11 01:18:542277std::unique_ptr<TransportDescription>
2278MediaSessionDescriptionFactory::CreateTransportAnswer(
henrike@webrtc.org28e20752013-07-10 00:45:362279 const std::string& content_name,
2280 const SessionDescription* offer_desc,
2281 const TransportOptions& transport_options,
deadbeefb7892532017-02-23 03:35:182282 const SessionDescription* current_desc,
Jonas Oreland1cd39fa2018-10-11 05:47:122283 bool require_transport_attributes,
2284 IceCredentialsIterator* ice_credentials) const {
henrike@webrtc.org28e20752013-07-10 00:45:362285 if (!transport_desc_factory_)
2286 return NULL;
2287 const TransportDescription* offer_tdesc =
2288 GetTransportDescription(content_name, offer_desc);
2289 const TransportDescription* current_tdesc =
2290 GetTransportDescription(content_name, current_desc);
deadbeefb7892532017-02-23 03:35:182291 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
2292 require_transport_attributes,
Jonas Oreland1cd39fa2018-10-11 05:47:122293 current_tdesc, ice_credentials);
henrike@webrtc.org28e20752013-07-10 00:45:362294}
2295
2296bool MediaSessionDescriptionFactory::AddTransportAnswer(
2297 const std::string& content_name,
2298 const TransportDescription& transport_desc,
2299 SessionDescription* answer_desc) const {
Steve Anton06817cd2018-12-18 23:55:302300 answer_desc->AddTransportInfo(TransportInfo(content_name, transport_desc));
henrike@webrtc.org28e20752013-07-10 00:45:362301 return true;
2302}
2303
Artem Titov880fa812021-07-30 20:30:232304// `audio_codecs` = set of all possible codecs that can be used, with correct
zhihuang1c378ed2017-08-17 21:10:502305// payload type mappings
2306//
Artem Titov880fa812021-07-30 20:30:232307// `supported_audio_codecs` = set of codecs that are supported for the direction
zhihuang1c378ed2017-08-17 21:10:502308// of this m= section
2309//
2310// acd->codecs() = set of previously negotiated codecs for this m= section
2311//
2312// The payload types should come from audio_codecs, but the order should come
2313// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2314// change existing codec priority, and that new codecs are added with the right
2315// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052316bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
zhihuang1c378ed2017-08-17 21:10:502317 const MediaDescriptionOptions& media_description_options,
2318 const MediaSessionOptions& session_options,
2319 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052320 const SessionDescription* current_description,
2321 const RtpHeaderExtensions& audio_rtp_extensions,
2322 const AudioCodecs& audio_codecs,
2323 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 05:47:122324 SessionDescription* desc,
2325 IceCredentialsIterator* ice_credentials) const {
Jonas Orelande62c2f22022-03-29 09:04:482326 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282327 &transport_desc_factory_->trials();
zhihuang1c378ed2017-08-17 21:10:502328 // Filter audio_codecs (which includes all codecs, with correctly remapped
2329 // payload types) based on transceiver direction.
2330 const AudioCodecs& supported_audio_codecs =
2331 GetAudioCodecsForOffer(media_description_options.direction);
2332
2333 AudioCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 17:25:512334
2335 if (!media_description_options.codec_preferences.empty()) {
2336 // Add the codecs from the current transceiver's codec preferences.
2337 // They override any existing codecs from previous negotiations.
Jonas Oreland4476b822022-03-10 14:21:282338 filtered_codecs = MatchCodecPreference(
2339 media_description_options.codec_preferences, audio_codecs,
2340 supported_audio_codecs, field_trials);
Florent Castelli2d9d82e2019-04-23 17:25:512341 } else {
2342 // Add the codecs from current content if it exists and is not rejected nor
2343 // recycled.
2344 if (current_content && !current_content->rejected &&
2345 current_content->name == media_description_options.mid) {
2346 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2347 const AudioContentDescription* acd =
2348 current_content->media_description()->as_audio();
2349 for (const AudioCodec& codec : acd->codecs()) {
2350 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
Jonas Oreland4476b822022-03-10 14:21:282351 nullptr, field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512352 filtered_codecs.push_back(codec);
2353 }
zhihuang1c378ed2017-08-17 21:10:502354 }
2355 }
Florent Castelli2d9d82e2019-04-23 17:25:512356 // Add other supported audio codecs.
2357 AudioCodec found_codec;
2358 for (const AudioCodec& codec : supported_audio_codecs) {
2359 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282360 codec, &found_codec, field_trials) &&
Florent Castelli2d9d82e2019-04-23 17:25:512361 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282362 filtered_codecs, codec, nullptr,
2363 field_trials)) {
Artem Titov880fa812021-07-30 20:30:232364 // Use the `found_codec` from `audio_codecs` because it has the
Florent Castelli2d9d82e2019-04-23 17:25:512365 // correctly mapped payload type.
2366 filtered_codecs.push_back(found_codec);
2367 }
zhihuang1c378ed2017-08-17 21:10:502368 }
2369 }
Philipp Hancke3ac73bd2021-05-11 12:13:062370 if (!session_options.vad_enabled) {
2371 // If application doesn't want CN codecs in offer.
2372 StripCNCodecs(&filtered_codecs);
2373 }
deadbeef44f08192015-12-16 00:20:092374
Harald Alvestrand0d018412021-11-04 13:52:312375 cricket::SecurePolicy sdes_policy =
2376 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2377 : secure();
2378
Johannes Kron3e983682020-03-29 20:17:002379 auto audio = std::make_unique<AudioContentDescription>();
Harald Alvestrand0d018412021-11-04 13:52:312380 std::vector<std::string> crypto_suites;
2381 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
2382 &crypto_suites);
Jonas Orelanded99dae2022-03-09 08:28:102383 if (!CreateMediaContentOffer(
2384 media_description_options, session_options, filtered_codecs,
2385 sdes_policy, GetCryptos(current_content), crypto_suites,
Harald Alvestrand8101e7b2022-05-23 14:57:472386 audio_rtp_extensions, ssrc_generator(), current_streams, audio.get(),
Jonas Orelanded99dae2022-03-09 08:28:102387 transport_desc_factory_->trials())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052388 return false;
2389 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052390
Harald Alvestrand0d018412021-11-04 13:52:312391 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052392 SetMediaProtocol(secure_transport, audio.get());
jiayl@webrtc.org7d4891d2014-09-09 21:43:152393
Steve Anton4e70a722017-11-28 22:57:102394 audio->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:432395
Steve Anton5adfafd2017-12-21 00:34:002396 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
Harald Alvestrand1716d392019-06-03 18:35:452397 media_description_options.stopped, std::move(audio));
zhihuang1c378ed2017-08-17 21:10:502398 if (!AddTransportOffer(media_description_options.mid,
2399 media_description_options.transport_options,
Jonas Oreland1cd39fa2018-10-11 05:47:122400 current_description, desc, ice_credentials)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052401 return false;
2402 }
2403
2404 return true;
2405}
2406
Johannes Kron3e983682020-03-29 20:17:002407// TODO(kron): This function is very similar to AddAudioContentForOffer.
2408// Refactor to reuse shared code.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052409bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
zhihuang1c378ed2017-08-17 21:10:502410 const MediaDescriptionOptions& media_description_options,
2411 const MediaSessionOptions& session_options,
2412 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052413 const SessionDescription* current_description,
2414 const RtpHeaderExtensions& video_rtp_extensions,
2415 const VideoCodecs& video_codecs,
2416 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 05:47:122417 SessionDescription* desc,
2418 IceCredentialsIterator* ice_credentials) const {
Jonas Orelande62c2f22022-03-29 09:04:482419 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282420 &transport_desc_factory_->trials();
Johannes Kron3e983682020-03-29 20:17:002421 // Filter video_codecs (which includes all codecs, with correctly remapped
2422 // payload types) based on transceiver direction.
2423 const VideoCodecs& supported_video_codecs =
2424 GetVideoCodecsForOffer(media_description_options.direction);
zhihuang1c378ed2017-08-17 21:10:502425
2426 VideoCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 17:25:512427
2428 if (!media_description_options.codec_preferences.empty()) {
2429 // Add the codecs from the current transceiver's codec preferences.
2430 // They override any existing codecs from previous negotiations.
Jonas Oreland4476b822022-03-10 14:21:282431 filtered_codecs = MatchCodecPreference(
2432 media_description_options.codec_preferences, video_codecs,
2433 supported_video_codecs, field_trials);
Florent Castelli2d9d82e2019-04-23 17:25:512434 } else {
2435 // Add the codecs from current content if it exists and is not rejected nor
2436 // recycled.
2437 if (current_content && !current_content->rejected &&
2438 current_content->name == media_description_options.mid) {
2439 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2440 const VideoContentDescription* vcd =
2441 current_content->media_description()->as_video();
2442 for (const VideoCodec& codec : vcd->codecs()) {
2443 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
Jonas Oreland4476b822022-03-10 14:21:282444 nullptr, field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512445 filtered_codecs.push_back(codec);
2446 }
zhihuang1c378ed2017-08-17 21:10:502447 }
2448 }
Florent Castelli2d9d82e2019-04-23 17:25:512449 // Add other supported video codecs.
2450 VideoCodec found_codec;
Johannes Kron3e983682020-03-29 20:17:002451 for (const VideoCodec& codec : supported_video_codecs) {
2452 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282453 codec, &found_codec, field_trials) &&
Johannes Kron3e983682020-03-29 20:17:002454 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282455 filtered_codecs, codec, nullptr,
2456 field_trials)) {
Artem Titov880fa812021-07-30 20:30:232457 // Use the `found_codec` from `video_codecs` because it has the
Florent Castelli2d9d82e2019-04-23 17:25:512458 // correctly mapped payload type.
Philipp Hancke16dae202021-11-18 17:27:122459 if (IsRtxCodec(codec)) {
2460 // For RTX we might need to adjust the apt parameter if we got a
2461 // remote offer without RTX for a codec for which we support RTX.
2462 auto referenced_codec =
2463 GetAssociatedCodecForRtx(supported_video_codecs, codec);
2464 RTC_DCHECK(referenced_codec);
2465
2466 // Find the codec we should be referencing and point to it.
2467 VideoCodec changed_referenced_codec;
Jonas Oreland4476b822022-03-10 14:21:282468 if (FindMatchingCodec<VideoCodec>(
2469 supported_video_codecs, filtered_codecs, *referenced_codec,
2470 &changed_referenced_codec, field_trials)) {
Philipp Hancke16dae202021-11-18 17:27:122471 found_codec.SetParam(kCodecParamAssociatedPayloadType,
2472 changed_referenced_codec.id);
2473 }
2474 }
Florent Castelli2d9d82e2019-04-23 17:25:512475 filtered_codecs.push_back(found_codec);
2476 }
zhihuang1c378ed2017-08-17 21:10:502477 }
2478 }
2479
Mirta Dvornicic479a3c02019-06-04 13:38:502480 if (session_options.raw_packetization_for_video) {
2481 for (VideoCodec& codec : filtered_codecs) {
2482 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2483 codec.packetization = kPacketizationParamRaw;
2484 }
2485 }
2486 }
2487
Harald Alvestrand0d018412021-11-04 13:52:312488 cricket::SecurePolicy sdes_policy =
2489 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2490 : secure();
Johannes Kron3e983682020-03-29 20:17:002491 auto video = std::make_unique<VideoContentDescription>();
Harald Alvestrand0d018412021-11-04 13:52:312492 std::vector<std::string> crypto_suites;
2493 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
2494 &crypto_suites);
Jonas Orelanded99dae2022-03-09 08:28:102495 if (!CreateMediaContentOffer(
2496 media_description_options, session_options, filtered_codecs,
2497 sdes_policy, GetCryptos(current_content), crypto_suites,
Harald Alvestrand8101e7b2022-05-23 14:57:472498 video_rtp_extensions, ssrc_generator(), current_streams, video.get(),
Jonas Orelanded99dae2022-03-09 08:28:102499 transport_desc_factory_->trials())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052500 return false;
2501 }
2502
zhihuang1c378ed2017-08-17 21:10:502503 video->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052504
Harald Alvestrand0d018412021-11-04 13:52:312505 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052506 SetMediaProtocol(secure_transport, video.get());
jiayl@webrtc.org742922b2014-10-07 21:32:432507
Steve Anton4e70a722017-11-28 22:57:102508 video->set_direction(media_description_options.direction);
jiayl@webrtc.org742922b2014-10-07 21:32:432509
Steve Anton5adfafd2017-12-21 00:34:002510 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
Harald Alvestrand1716d392019-06-03 18:35:452511 media_description_options.stopped, std::move(video));
zhihuang1c378ed2017-08-17 21:10:502512 if (!AddTransportOffer(media_description_options.mid,
2513 media_description_options.transport_options,
Jonas Oreland1cd39fa2018-10-11 05:47:122514 current_description, desc, ice_credentials)) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052515 return false;
2516 }
Johannes Kron3e983682020-03-29 20:17:002517
jiayl@webrtc.orge7d47a12014-08-05 19:19:052518 return true;
2519}
2520
Harald Alvestrand7af57c62021-04-16 11:12:142521bool MediaSessionDescriptionFactory::AddDataContentForOffer(
Harald Alvestrand5fc28b12019-05-13 11:36:162522 const MediaDescriptionOptions& media_description_options,
2523 const MediaSessionOptions& session_options,
2524 const ContentInfo* current_content,
2525 const SessionDescription* current_description,
2526 StreamParamsVec* current_streams,
2527 SessionDescription* desc,
2528 IceCredentialsIterator* ice_credentials) const {
Johannes Kron3e983682020-03-29 20:17:002529 auto data = std::make_unique<SctpDataContentDescription>();
Harald Alvestrand5fc28b12019-05-13 11:36:162530
Harald Alvestrand0d018412021-11-04 13:52:312531 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
Harald Alvestrand5fc28b12019-05-13 11:36:162532
Harald Alvestrand0d018412021-11-04 13:52:312533 cricket::SecurePolicy sdes_policy =
2534 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2535 : secure();
2536 std::vector<std::string> crypto_suites;
2537 // SDES doesn't make sense for SCTP, so we disable it, and we only
2538 // get SDES crypto suites for RTP-based data channels.
2539 sdes_policy = cricket::SEC_DISABLED;
Harald Alvestrand5fc28b12019-05-13 11:36:162540 // Unlike SetMediaProtocol below, we need to set the protocol
2541 // before we call CreateMediaContentOffer. Otherwise,
2542 // CreateMediaContentOffer won't know this is SCTP and will
2543 // generate SSRCs rather than SIDs.
Guido Urdanetacecf87f2019-05-31 10:17:382544 data->set_protocol(secure_transport ? kMediaProtocolUdpDtlsSctp
Harald Alvestrand5fc28b12019-05-13 11:36:162545 : kMediaProtocolSctp);
Harald Alvestrand4aa11922019-05-14 20:00:012546 data->set_use_sctpmap(session_options.use_obsolete_sctp_sdp);
Harald Alvestrandfbb45bd2019-05-15 06:07:472547 data->set_max_message_size(kSctpSendBufferSize);
2548
Harald Alvestrand5fc28b12019-05-13 11:36:162549 if (!CreateContentOffer(media_description_options, session_options,
Harald Alvestrand0d018412021-11-04 13:52:312550 sdes_policy, GetCryptos(current_content),
Harald Alvestrand8101e7b2022-05-23 14:57:472551 crypto_suites, RtpHeaderExtensions(),
2552 ssrc_generator(), current_streams, data.get())) {
Harald Alvestrand5fc28b12019-05-13 11:36:162553 return false;
2554 }
2555
2556 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
Steve Antonc8ff1602020-02-05 21:53:382557 media_description_options.stopped, std::move(data));
Harald Alvestrand5fc28b12019-05-13 11:36:162558 if (!AddTransportOffer(media_description_options.mid,
2559 media_description_options.transport_options,
2560 current_description, desc, ice_credentials)) {
2561 return false;
2562 }
2563 return true;
2564}
2565
Philipp Hancke4e8c1152020-10-13 10:43:152566bool MediaSessionDescriptionFactory::AddUnsupportedContentForOffer(
2567 const MediaDescriptionOptions& media_description_options,
2568 const MediaSessionOptions& session_options,
2569 const ContentInfo* current_content,
2570 const SessionDescription* current_description,
2571 SessionDescription* desc,
2572 IceCredentialsIterator* ice_credentials) const {
2573 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_UNSUPPORTED));
2574
2575 const UnsupportedContentDescription* current_unsupported_description =
2576 current_content->media_description()->as_unsupported();
2577 auto unsupported = std::make_unique<UnsupportedContentDescription>(
2578 current_unsupported_description->media_type());
2579 unsupported->set_protocol(current_content->media_description()->protocol());
2580 desc->AddContent(media_description_options.mid, MediaProtocolType::kOther,
2581 /*rejected=*/true, std::move(unsupported));
2582
2583 if (!AddTransportOffer(media_description_options.mid,
2584 media_description_options.transport_options,
2585 current_description, desc, ice_credentials)) {
2586 return false;
2587 }
2588 return true;
2589}
2590
Artem Titov880fa812021-07-30 20:30:232591// `audio_codecs` = set of all possible codecs that can be used, with correct
zhihuang1c378ed2017-08-17 21:10:502592// payload type mappings
2593//
Artem Titov880fa812021-07-30 20:30:232594// `supported_audio_codecs` = set of codecs that are supported for the direction
zhihuang1c378ed2017-08-17 21:10:502595// of this m= section
2596//
2597// acd->codecs() = set of previously negotiated codecs for this m= section
2598//
2599// The payload types should come from audio_codecs, but the order should come
2600// from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2601// change existing codec priority, and that new codecs are added with the right
2602// priority.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052603bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502604 const MediaDescriptionOptions& media_description_options,
2605 const MediaSessionOptions& session_options,
2606 const ContentInfo* offer_content,
2607 const SessionDescription* offer_description,
2608 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052609 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182610 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 21:10:502611 const AudioCodecs& audio_codecs,
Philipp Hancked4fe3ce2023-03-31 11:33:422612 const RtpHeaderExtensions& rtp_header_extensions,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052613 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 05:47:122614 SessionDescription* answer,
2615 IceCredentialsIterator* ice_credentials) const {
Jonas Orelande62c2f22022-03-29 09:04:482616 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282617 &transport_desc_factory_->trials();
Taylor Brandstetter80cfb522017-10-13 03:37:382618 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
zhihuang1c378ed2017-08-17 21:10:502619 const AudioContentDescription* offer_audio_description =
Steve Antonb1c1de12017-12-21 23:14:302620 offer_content->media_description()->as_audio();
jiayl@webrtc.orge7d47a12014-08-05 19:19:052621
Steve Anton1a9d3c32018-12-11 01:18:542622 std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 05:47:122623 media_description_options.mid, offer_description,
2624 media_description_options.transport_options, current_description,
Harald Alvestrand0d018412021-11-04 13:52:312625 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052626 if (!audio_transport) {
2627 return false;
2628 }
2629
zhihuang1c378ed2017-08-17 21:10:502630 // Pick codecs based on the requested communications direction in the offer
2631 // and the selected direction in the answer.
2632 // Note these will be filtered one final time in CreateMediaContentAnswer.
2633 auto wants_rtd = media_description_options.direction;
Steve Anton4e70a722017-11-28 22:57:102634 auto offer_rtd = offer_audio_description->direction();
ossu075af922016-06-14 10:29:382635 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
zhihuang1c378ed2017-08-17 21:10:502636 AudioCodecs supported_audio_codecs =
2637 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2638
2639 AudioCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 17:25:512640
2641 if (!media_description_options.codec_preferences.empty()) {
Jonas Oreland4476b822022-03-10 14:21:282642 filtered_codecs = MatchCodecPreference(
2643 media_description_options.codec_preferences, audio_codecs,
2644 supported_audio_codecs, field_trials);
Florent Castelli2d9d82e2019-04-23 17:25:512645 } else {
2646 // Add the codecs from current content if it exists and is not rejected nor
2647 // recycled.
2648 if (current_content && !current_content->rejected &&
2649 current_content->name == media_description_options.mid) {
2650 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2651 const AudioContentDescription* acd =
2652 current_content->media_description()->as_audio();
2653 for (const AudioCodec& codec : acd->codecs()) {
2654 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
Jonas Oreland4476b822022-03-10 14:21:282655 nullptr, field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512656 filtered_codecs.push_back(codec);
2657 }
zhihuang1c378ed2017-08-17 21:10:502658 }
2659 }
Florent Castelli2d9d82e2019-04-23 17:25:512660 // Add other supported audio codecs.
2661 for (const AudioCodec& codec : supported_audio_codecs) {
2662 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282663 codec, nullptr, field_trials) &&
Florent Castelli2d9d82e2019-04-23 17:25:512664 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
Jonas Oreland4476b822022-03-10 14:21:282665 filtered_codecs, codec, nullptr,
2666 field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512667 // We should use the local codec with local parameters and the codec id
Artem Titov880fa812021-07-30 20:30:232668 // would be correctly mapped in `NegotiateCodecs`.
Florent Castelli2d9d82e2019-04-23 17:25:512669 filtered_codecs.push_back(codec);
2670 }
zhihuang1c378ed2017-08-17 21:10:502671 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052672 }
Philipp Hancke3ac73bd2021-05-11 12:13:062673 if (!session_options.vad_enabled) {
2674 // If application doesn't want CN codecs in answer.
2675 StripCNCodecs(&filtered_codecs);
2676 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052677
Philipp Hancke2f3168f2022-05-16 12:41:322678 // Determine if we have media codecs in common.
2679 bool has_common_media_codecs =
2680 std::find_if(filtered_codecs.begin(), filtered_codecs.end(),
2681 [](const AudioCodec& c) {
2682 return !(IsRedCodec(c) || IsComfortNoiseCodec(c));
2683 }) != filtered_codecs.end();
2684
zhihuang1c378ed2017-08-17 21:10:502685 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2686 session_options.bundle_enabled;
Johannes Kron3e983682020-03-29 20:17:002687 auto audio_answer = std::make_unique<AudioContentDescription>();
Harald Alvestrand0d018412021-11-04 13:52:312688 // Do not require or create SDES cryptos if DTLS is used.
2689 cricket::SecurePolicy sdes_policy =
2690 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
Harald Alvestrand5fc28b12019-05-13 11:36:162691 if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
2692 media_description_options, session_options,
Harald Alvestrand8101e7b2022-05-23 14:57:472693 ssrc_generator(), current_streams, audio_answer.get(),
Jonas Orelanded99dae2022-03-09 08:28:102694 transport_desc_factory_->trials())) {
Harald Alvestrand5fc28b12019-05-13 11:36:162695 return false;
2696 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052697 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 21:10:502698 offer_audio_description, media_description_options, session_options,
Harald Alvestrand0d018412021-11-04 13:52:312699 sdes_policy, GetCryptos(current_content),
Philipp Hancked4fe3ce2023-03-31 11:33:422700 filtered_rtp_header_extensions(rtp_header_extensions),
Harald Alvestrand8101e7b2022-05-23 14:57:472701 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
Markus Handell755c65d2020-06-23 23:06:102702 current_streams, bundle_enabled, audio_answer.get())) {
jiayl@webrtc.orge7d47a12014-08-05 19:19:052703 return false; // Fails the session setup.
2704 }
2705
deadbeefb7892532017-02-23 03:35:182706 bool secure = bundle_transport ? bundle_transport->description.secure()
2707 : audio_transport->secure();
zhihuang1c378ed2017-08-17 21:10:502708 bool rejected = media_description_options.stopped ||
Philipp Hancke2f3168f2022-05-16 12:41:322709 offer_content->rejected || !has_common_media_codecs ||
deadbeefb7892532017-02-23 03:35:182710 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2711 audio_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352712 if (!AddTransportAnswer(media_description_options.mid,
2713 *(audio_transport.get()), answer)) {
2714 return false;
2715 }
2716
2717 if (rejected) {
Mirko Bonadei675513b2017-11-09 10:09:252718 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2719 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:052720 }
2721
zhihuang1c378ed2017-08-17 21:10:502722 answer->AddContent(media_description_options.mid, offer_content->type,
Harald Alvestrand1716d392019-06-03 18:35:452723 rejected, std::move(audio_answer));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052724 return true;
2725}
2726
Johannes Kron3e983682020-03-29 20:17:002727// TODO(kron): This function is very similar to AddAudioContentForAnswer.
2728// Refactor to reuse shared code.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052729bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502730 const MediaDescriptionOptions& media_description_options,
2731 const MediaSessionOptions& session_options,
2732 const ContentInfo* offer_content,
2733 const SessionDescription* offer_description,
2734 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052735 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182736 const TransportInfo* bundle_transport,
zhihuang1c378ed2017-08-17 21:10:502737 const VideoCodecs& video_codecs,
Markus Handell755c65d2020-06-23 23:06:102738 const RtpHeaderExtensions& default_video_rtp_header_extensions,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052739 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 05:47:122740 SessionDescription* answer,
2741 IceCredentialsIterator* ice_credentials) const {
Jonas Orelande62c2f22022-03-29 09:04:482742 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282743 &transport_desc_factory_->trials();
Taylor Brandstetter80cfb522017-10-13 03:37:382744 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
zhihuang1c378ed2017-08-17 21:10:502745 const VideoContentDescription* offer_video_description =
Steve Antonb1c1de12017-12-21 23:14:302746 offer_content->media_description()->as_video();
zhihuang1c378ed2017-08-17 21:10:502747
Steve Anton1a9d3c32018-12-11 01:18:542748 std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 05:47:122749 media_description_options.mid, offer_description,
2750 media_description_options.transport_options, current_description,
Harald Alvestrand0d018412021-11-04 13:52:312751 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052752 if (!video_transport) {
2753 return false;
2754 }
2755
Johannes Kron3e983682020-03-29 20:17:002756 // Pick codecs based on the requested communications direction in the offer
2757 // and the selected direction in the answer.
2758 // Note these will be filtered one final time in CreateMediaContentAnswer.
2759 auto wants_rtd = media_description_options.direction;
2760 auto offer_rtd = offer_video_description->direction();
2761 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
2762 VideoCodecs supported_video_codecs =
2763 GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
2764
zhihuang1c378ed2017-08-17 21:10:502765 VideoCodecs filtered_codecs;
Florent Castelli2d9d82e2019-04-23 17:25:512766
2767 if (!media_description_options.codec_preferences.empty()) {
Jonas Oreland4476b822022-03-10 14:21:282768 filtered_codecs = MatchCodecPreference(
2769 media_description_options.codec_preferences, video_codecs,
2770 supported_video_codecs, field_trials);
Florent Castelli2d9d82e2019-04-23 17:25:512771 } else {
2772 // Add the codecs from current content if it exists and is not rejected nor
2773 // recycled.
2774 if (current_content && !current_content->rejected &&
2775 current_content->name == media_description_options.mid) {
2776 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2777 const VideoContentDescription* vcd =
2778 current_content->media_description()->as_video();
2779 for (const VideoCodec& codec : vcd->codecs()) {
2780 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
Jonas Oreland4476b822022-03-10 14:21:282781 nullptr, field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512782 filtered_codecs.push_back(codec);
2783 }
zhihuang1c378ed2017-08-17 21:10:502784 }
2785 }
Stefan Mitic3aa99372022-02-03 12:26:222786
Florent Castelli2d9d82e2019-04-23 17:25:512787 // Add other supported video codecs.
Stefan Mitic3aa99372022-02-03 12:26:222788 VideoCodecs other_video_codecs;
Johannes Kron3e983682020-03-29 20:17:002789 for (const VideoCodec& codec : supported_video_codecs) {
2790 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282791 codec, nullptr, field_trials) &&
Johannes Kron3e983682020-03-29 20:17:002792 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
Jonas Oreland4476b822022-03-10 14:21:282793 filtered_codecs, codec, nullptr,
2794 field_trials)) {
Florent Castelli2d9d82e2019-04-23 17:25:512795 // We should use the local codec with local parameters and the codec id
Artem Titov880fa812021-07-30 20:30:232796 // would be correctly mapped in `NegotiateCodecs`.
Stefan Mitic3aa99372022-02-03 12:26:222797 other_video_codecs.push_back(codec);
Florent Castelli2d9d82e2019-04-23 17:25:512798 }
zhihuang1c378ed2017-08-17 21:10:502799 }
Stefan Mitic3aa99372022-02-03 12:26:222800
2801 // Use ComputeCodecsUnion to avoid having duplicate payload IDs
Jonas Oreland4476b822022-03-10 14:21:282802 filtered_codecs = ComputeCodecsUnion<VideoCodec>(
2803 filtered_codecs, other_video_codecs, field_trials);
zhihuang1c378ed2017-08-17 21:10:502804 }
Philipp Hancke2f3168f2022-05-16 12:41:322805 // Determine if we have media codecs in common.
2806 bool has_common_media_codecs =
2807 std::find_if(
2808 filtered_codecs.begin(), filtered_codecs.end(),
2809 [](const VideoCodec& c) {
2810 return !(IsRedCodec(c) || IsUlpfecCodec(c) || IsFlexfecCodec(c));
2811 }) != filtered_codecs.end();
zhihuang1c378ed2017-08-17 21:10:502812
Mirta Dvornicic479a3c02019-06-04 13:38:502813 if (session_options.raw_packetization_for_video) {
2814 for (VideoCodec& codec : filtered_codecs) {
2815 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2816 codec.packetization = kPacketizationParamRaw;
2817 }
2818 }
2819 }
2820
zhihuang1c378ed2017-08-17 21:10:502821 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2822 session_options.bundle_enabled;
Johannes Kron3e983682020-03-29 20:17:002823 auto video_answer = std::make_unique<VideoContentDescription>();
Harald Alvestrand0d018412021-11-04 13:52:312824 // Do not require or create SDES cryptos if DTLS is used.
2825 cricket::SecurePolicy sdes_policy =
2826 video_transport->secure() ? cricket::SEC_DISABLED : secure();
Harald Alvestrand5fc28b12019-05-13 11:36:162827 if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
2828 media_description_options, session_options,
Harald Alvestrand8101e7b2022-05-23 14:57:472829 ssrc_generator(), current_streams, video_answer.get(),
Jonas Orelanded99dae2022-03-09 08:28:102830 transport_desc_factory_->trials())) {
Harald Alvestrand5fc28b12019-05-13 11:36:162831 return false;
2832 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052833 if (!CreateMediaContentAnswer(
zhihuang1c378ed2017-08-17 21:10:502834 offer_video_description, media_description_options, session_options,
Harald Alvestrand0d018412021-11-04 13:52:312835 sdes_policy, GetCryptos(current_content),
Markus Handell755c65d2020-06-23 23:06:102836 filtered_rtp_header_extensions(default_video_rtp_header_extensions),
Harald Alvestrand8101e7b2022-05-23 14:57:472837 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
Markus Handell755c65d2020-06-23 23:06:102838 current_streams, bundle_enabled, video_answer.get())) {
Philipp Hancke2f3168f2022-05-16 12:41:322839 return false; // Failed the session setup.
jiayl@webrtc.orge7d47a12014-08-05 19:19:052840 }
deadbeefb7892532017-02-23 03:35:182841 bool secure = bundle_transport ? bundle_transport->description.secure()
2842 : video_transport->secure();
zhihuang1c378ed2017-08-17 21:10:502843 bool rejected = media_description_options.stopped ||
Philipp Hancke2f3168f2022-05-16 12:41:322844 offer_content->rejected || !has_common_media_codecs ||
deadbeefb7892532017-02-23 03:35:182845 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2846 video_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352847 if (!AddTransportAnswer(media_description_options.mid,
2848 *(video_transport.get()), answer)) {
2849 return false;
2850 }
2851
jiayl@webrtc.orge7d47a12014-08-05 19:19:052852 if (!rejected) {
zhihuang1c378ed2017-08-17 21:10:502853 video_answer->set_bandwidth(kAutoBandwidth);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052854 } else {
Mirko Bonadei675513b2017-11-09 10:09:252855 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2856 << "' being rejected in answer.";
jiayl@webrtc.orge7d47a12014-08-05 19:19:052857 }
zhihuang1c378ed2017-08-17 21:10:502858 answer->AddContent(media_description_options.mid, offer_content->type,
Harald Alvestrand1716d392019-06-03 18:35:452859 rejected, std::move(video_answer));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052860 return true;
2861}
2862
2863bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
zhihuang1c378ed2017-08-17 21:10:502864 const MediaDescriptionOptions& media_description_options,
2865 const MediaSessionOptions& session_options,
2866 const ContentInfo* offer_content,
2867 const SessionDescription* offer_description,
2868 const ContentInfo* current_content,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052869 const SessionDescription* current_description,
deadbeefb7892532017-02-23 03:35:182870 const TransportInfo* bundle_transport,
jiayl@webrtc.orge7d47a12014-08-05 19:19:052871 StreamParamsVec* current_streams,
Jonas Oreland1cd39fa2018-10-11 05:47:122872 SessionDescription* answer,
2873 IceCredentialsIterator* ice_credentials) const {
Steve Anton1a9d3c32018-12-11 01:18:542874 std::unique_ptr<TransportDescription> data_transport = CreateTransportAnswer(
Jonas Oreland1cd39fa2018-10-11 05:47:122875 media_description_options.mid, offer_description,
2876 media_description_options.transport_options, current_description,
Harald Alvestrand0d018412021-11-04 13:52:312877 bundle_transport != nullptr, ice_credentials);
jiayl@webrtc.orge7d47a12014-08-05 19:19:052878 if (!data_transport) {
2879 return false;
2880 }
jiayl@webrtc.orge7d47a12014-08-05 19:19:052881
Harald Alvestrand0d018412021-11-04 13:52:312882 // Do not require or create SDES cryptos if DTLS is used.
2883 cricket::SecurePolicy sdes_policy =
2884 data_transport->secure() ? cricket::SEC_DISABLED : secure();
zhihuang1c378ed2017-08-17 21:10:502885 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2886 session_options.bundle_enabled;
Taylor Brandstetter80cfb522017-10-13 03:37:382887 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
Harald Alvestrand5fc28b12019-05-13 11:36:162888 std::unique_ptr<MediaContentDescription> data_answer;
2889 if (offer_content->media_description()->as_sctp()) {
2890 // SCTP data content
Mirko Bonadei317a1f02019-09-17 15:06:182891 data_answer = std::make_unique<SctpDataContentDescription>();
Harald Alvestrand5fc28b12019-05-13 11:36:162892 const SctpDataContentDescription* offer_data_description =
2893 offer_content->media_description()->as_sctp();
2894 // Respond with the offerer's proto, whatever it is.
2895 data_answer->as_sctp()->set_protocol(offer_data_description->protocol());
Harald Alvestrandfbb45bd2019-05-15 06:07:472896 // Respond with our max message size or the remote max messsage size,
2897 // whichever is smaller.
Harald Alvestrand8d3d6cf2019-05-16 09:49:172898 // 0 is treated specially - it means "I can accept any size". Since
2899 // we do not implement infinite size messages, reply with
2900 // kSctpSendBufferSize.
2901 if (offer_data_description->max_message_size() == 0) {
2902 data_answer->as_sctp()->set_max_message_size(kSctpSendBufferSize);
2903 } else {
2904 data_answer->as_sctp()->set_max_message_size(std::min(
2905 offer_data_description->max_message_size(), kSctpSendBufferSize));
2906 }
Harald Alvestrand5fc28b12019-05-13 11:36:162907 if (!CreateMediaContentAnswer(
2908 offer_data_description, media_description_options, session_options,
Harald Alvestrand0d018412021-11-04 13:52:312909 sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
Harald Alvestrand8101e7b2022-05-23 14:57:472910 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
Harald Alvestrand0d018412021-11-04 13:52:312911 current_streams, bundle_enabled, data_answer.get())) {
Harald Alvestrand5fc28b12019-05-13 11:36:162912 return false; // Fails the session setup.
2913 }
2914 // Respond with sctpmap if the offer uses sctpmap.
2915 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2916 data_answer->as_sctp()->set_use_sctpmap(offer_uses_sctpmap);
2917 } else {
Artem Titovd3251962021-11-15 15:57:072918 RTC_DCHECK_NOTREACHED() << "Non-SCTP data content found";
Harald Alvestrand5fc28b12019-05-13 11:36:162919 }
Steve Anton46afbf92019-05-10 18:15:182920
deadbeefb7892532017-02-23 03:35:182921 bool secure = bundle_transport ? bundle_transport->description.secure()
2922 : data_transport->secure();
2923
Florent Castelli516e2842021-04-19 13:29:502924 bool rejected = media_description_options.stopped ||
zhihuang1c378ed2017-08-17 21:10:502925 offer_content->rejected ||
deadbeefb7892532017-02-23 03:35:182926 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2927 data_answer->protocol(), secure);
Zhi Huang3518e7b2018-01-30 21:20:352928 if (!AddTransportAnswer(media_description_options.mid,
2929 *(data_transport.get()), answer)) {
2930 return false;
2931 }
2932
zhihuang1c378ed2017-08-17 21:10:502933 answer->AddContent(media_description_options.mid, offer_content->type,
Harald Alvestrand1716d392019-06-03 18:35:452934 rejected, std::move(data_answer));
jiayl@webrtc.orge7d47a12014-08-05 19:19:052935 return true;
2936}
2937
Philipp Hancke4e8c1152020-10-13 10:43:152938bool MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer(
2939 const MediaDescriptionOptions& media_description_options,
2940 const MediaSessionOptions& session_options,
2941 const ContentInfo* offer_content,
2942 const SessionDescription* offer_description,
2943 const ContentInfo* current_content,
2944 const SessionDescription* current_description,
2945 const TransportInfo* bundle_transport,
2946 SessionDescription* answer,
2947 IceCredentialsIterator* ice_credentials) const {
2948 std::unique_ptr<TransportDescription> unsupported_transport =
2949 CreateTransportAnswer(media_description_options.mid, offer_description,
2950 media_description_options.transport_options,
Harald Alvestrand0d018412021-11-04 13:52:312951 current_description, bundle_transport != nullptr,
Philipp Hancke4e8c1152020-10-13 10:43:152952 ice_credentials);
2953 if (!unsupported_transport) {
2954 return false;
2955 }
2956 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_UNSUPPORTED));
2957
2958 const UnsupportedContentDescription* offer_unsupported_description =
2959 offer_content->media_description()->as_unsupported();
2960 std::unique_ptr<MediaContentDescription> unsupported_answer =
2961 std::make_unique<UnsupportedContentDescription>(
2962 offer_unsupported_description->media_type());
2963 unsupported_answer->set_protocol(offer_unsupported_description->protocol());
2964
2965 if (!AddTransportAnswer(media_description_options.mid,
2966 *(unsupported_transport.get()), answer)) {
2967 return false;
2968 }
2969 answer->AddContent(media_description_options.mid, offer_content->type,
2970 /*rejected=*/true, std::move(unsupported_answer));
2971 return true;
2972}
2973
zhihuang1c378ed2017-08-17 21:10:502974void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
Jonas Orelande62c2f22022-03-29 09:04:482975 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:282976 &transport_desc_factory_->trials();
zhihuang1c378ed2017-08-17 21:10:502977 audio_sendrecv_codecs_.clear();
2978 all_audio_codecs_.clear();
2979 // Compute the audio codecs union.
2980 for (const AudioCodec& send : audio_send_codecs_) {
2981 all_audio_codecs_.push_back(send);
2982 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
Jonas Oreland4476b822022-03-10 14:21:282983 send, nullptr, field_trials)) {
zhihuang1c378ed2017-08-17 21:10:502984 // It doesn't make sense to have an RTX codec we support sending but not
2985 // receiving.
2986 RTC_DCHECK(!IsRtxCodec(send));
2987 }
2988 }
2989 for (const AudioCodec& recv : audio_recv_codecs_) {
2990 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
Jonas Oreland4476b822022-03-10 14:21:282991 recv, nullptr, field_trials)) {
zhihuang1c378ed2017-08-17 21:10:502992 all_audio_codecs_.push_back(recv);
2993 }
2994 }
2995 // Use NegotiateCodecs to merge our codec lists, since the operation is
2996 // essentially the same. Put send_codecs as the offered_codecs, which is the
2997 // order we'd like to follow. The reasoning is that encoding is usually more
2998 // expensive than decoding, and prioritizing a codec in the send list probably
2999 // means it's a codec we can handle efficiently.
3000 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
Jonas Oreland4476b822022-03-10 14:21:283001 &audio_sendrecv_codecs_, true, field_trials);
zhihuang1c378ed2017-08-17 21:10:503002}
3003
Johannes Kron3e983682020-03-29 20:17:003004void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() {
Jonas Orelande62c2f22022-03-29 09:04:483005 const webrtc::FieldTrialsView* field_trials =
Jonas Oreland4476b822022-03-10 14:21:283006 &transport_desc_factory_->trials();
Johannes Kron3e983682020-03-29 20:17:003007 video_sendrecv_codecs_.clear();
Johannes Kron3e983682020-03-29 20:17:003008
Stefan Mitic3aa99372022-02-03 12:26:223009 // Use ComputeCodecsUnion to avoid having duplicate payload IDs
3010 all_video_codecs_ =
Jonas Oreland4476b822022-03-10 14:21:283011 ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_, field_trials);
Stefan Mitic3aa99372022-02-03 12:26:223012
Johannes Kron3e983682020-03-29 20:17:003013 // Use NegotiateCodecs to merge our codec lists, since the operation is
3014 // essentially the same. Put send_codecs as the offered_codecs, which is the
3015 // order we'd like to follow. The reasoning is that encoding is usually more
3016 // expensive than decoding, and prioritizing a codec in the send list probably
3017 // means it's a codec we can handle efficiently.
3018 NegotiateCodecs(video_recv_codecs_, video_send_codecs_,
Jonas Oreland4476b822022-03-10 14:21:283019 &video_sendrecv_codecs_, true, field_trials);
Johannes Kron3e983682020-03-29 20:17:003020}
3021
henrike@webrtc.org28e20752013-07-10 00:45:363022bool IsMediaContent(const ContentInfo* content) {
Steve Anton5adfafd2017-12-21 00:34:003023 return (content && (content->type == MediaProtocolType::kRtp ||
3024 content->type == MediaProtocolType::kSctp));
henrike@webrtc.org28e20752013-07-10 00:45:363025}
3026
3027bool IsAudioContent(const ContentInfo* content) {
3028 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
3029}
3030
3031bool IsVideoContent(const ContentInfo* content) {
3032 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
3033}
3034
3035bool IsDataContent(const ContentInfo* content) {
3036 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
3037}
3038
Philipp Hancke4e8c1152020-10-13 10:43:153039bool IsUnsupportedContent(const ContentInfo* content) {
3040 return IsMediaContentOfType(content, MEDIA_TYPE_UNSUPPORTED);
3041}
3042
deadbeef0ed85b22016-02-24 01:24:523043const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
3044 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503045 for (const ContentInfo& content : contents) {
3046 if (IsMediaContentOfType(&content, media_type)) {
3047 return &content;
henrike@webrtc.org28e20752013-07-10 00:45:363048 }
3049 }
deadbeef0ed85b22016-02-24 01:24:523050 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:363051}
3052
3053const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
3054 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
3055}
3056
3057const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
3058 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
3059}
3060
3061const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
3062 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
3063}
3064
Steve Antonad7bffc2018-01-22 18:21:563065const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
3066 MediaType media_type) {
deadbeef0ed85b22016-02-24 01:24:523067 if (sdesc == nullptr) {
3068 return nullptr;
3069 }
henrike@webrtc.org28e20752013-07-10 00:45:363070
3071 return GetFirstMediaContent(sdesc->contents(), media_type);
3072}
3073
3074const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
3075 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
3076}
3077
3078const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
3079 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
3080}
3081
3082const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
3083 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
3084}
3085
3086const MediaContentDescription* GetFirstMediaContentDescription(
Yves Gerey665174f2018-06-19 13:03:053087 const SessionDescription* sdesc,
3088 MediaType media_type) {
henrike@webrtc.org28e20752013-07-10 00:45:363089 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 23:14:303090 return (content ? content->media_description() : nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:363091}
3092
3093const AudioContentDescription* GetFirstAudioContentDescription(
3094 const SessionDescription* sdesc) {
Harald Alvestrand5fc28b12019-05-13 11:36:163095 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
3096 return desc ? desc->as_audio() : nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:363097}
3098
3099const VideoContentDescription* GetFirstVideoContentDescription(
3100 const SessionDescription* sdesc) {
Harald Alvestrand5fc28b12019-05-13 11:36:163101 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
3102 return desc ? desc->as_video() : nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:363103}
3104
Harald Alvestrand5fc28b12019-05-13 11:36:163105const SctpDataContentDescription* GetFirstSctpDataContentDescription(
3106 const SessionDescription* sdesc) {
3107 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
3108 return desc ? desc->as_sctp() : nullptr;
3109}
3110
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503111//
3112// Non-const versions of the above functions.
3113//
3114
Steve Anton36b29d12017-10-30 16:57:423115ContentInfo* GetFirstMediaContent(ContentInfos* contents,
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503116 MediaType media_type) {
Steve Anton36b29d12017-10-30 16:57:423117 for (ContentInfo& content : *contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503118 if (IsMediaContentOfType(&content, media_type)) {
3119 return &content;
3120 }
3121 }
3122 return nullptr;
3123}
3124
Steve Anton36b29d12017-10-30 16:57:423125ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503126 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
3127}
3128
Steve Anton36b29d12017-10-30 16:57:423129ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503130 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
3131}
3132
Steve Anton36b29d12017-10-30 16:57:423133ContentInfo* GetFirstDataContent(ContentInfos* contents) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503134 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
3135}
3136
Steve Antonad7bffc2018-01-22 18:21:563137ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
3138 MediaType media_type) {
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503139 if (sdesc == nullptr) {
3140 return nullptr;
3141 }
3142
Steve Anton36b29d12017-10-30 16:57:423143 return GetFirstMediaContent(&sdesc->contents(), media_type);
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503144}
3145
3146ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
3147 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
3148}
3149
3150ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
3151 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
3152}
3153
3154ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
3155 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
3156}
3157
3158MediaContentDescription* GetFirstMediaContentDescription(
3159 SessionDescription* sdesc,
3160 MediaType media_type) {
3161 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
Steve Antonb1c1de12017-12-21 23:14:303162 return (content ? content->media_description() : nullptr);
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503163}
3164
3165AudioContentDescription* GetFirstAudioContentDescription(
3166 SessionDescription* sdesc) {
Harald Alvestrand5fc28b12019-05-13 11:36:163167 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
3168 return desc ? desc->as_audio() : nullptr;
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503169}
3170
3171VideoContentDescription* GetFirstVideoContentDescription(
3172 SessionDescription* sdesc) {
Harald Alvestrand5fc28b12019-05-13 11:36:163173 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
3174 return desc ? desc->as_video() : nullptr;
Taylor Brandstetterdc4eb8c2016-05-12 15:14:503175}
3176
Harald Alvestrand5fc28b12019-05-13 11:36:163177SctpDataContentDescription* GetFirstSctpDataContentDescription(
3178 SessionDescription* sdesc) {
3179 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
3180 return desc ? desc->as_sctp() : nullptr;
3181}
3182
henrike@webrtc.org28e20752013-07-10 00:45:363183} // namespace cricket