Skip to content
File

Blob: firmware/vendor/str0m/src/config.rs

rust865 lines
1use std::ops::RangeInclusive;
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4 
5use crate::Rtc;
6use crate::config::DtlsCert;
7use crate::crypto::CryptoProvider;
8use crate::crypto::dtls::DtlsVersion;
9use crate::format::CodecConfig;
10use crate::format::Vp9PacketizerMode;
11use crate::ice::IceCreds;
12use crate::io::DATAGRAM_MTU_TARGET;
13use crate::io::DATAGRAM_MTU_TARGET_MAX;
14use crate::io::DATAGRAM_MTU_TARGET_MIN;
15use crate::io::DATAGRAM_MTU_WARN;
16use crate::rtp_::{Bitrate, Extension, ExtensionMap};
17 
18/// Customized config for creating an [`Rtc`] instance.
19///
20/// ```
21/// # #[cfg(feature = "openssl")] {
22/// use std::time::Instant;
23/// use str0m::RtcConfig;
24///
25/// let rtc = RtcConfig::new()
26/// .set_ice_lite(true)
27/// .build(Instant::now());
28/// # }
29/// ```
30///
31/// Configs implement [`Clone`] to help create multiple `Rtc` instances.
32#[derive(Debug, Clone)]
33pub struct RtcConfig {
34 pub(crate) local_ice_credentials: Option<IceCreds>,
35 pub(crate) crypto_provider: Option<Arc<CryptoProvider>>,
36 pub(crate) dtls_cert: Option<DtlsCert>,
37 pub(crate) fingerprint_verification: bool,
38 pub(crate) ice_lite: bool,
39 pub(crate) initial_stun_rto: Option<Duration>,
40 pub(crate) max_stun_rto: Option<Duration>,
41 pub(crate) max_stun_retransmits: Option<usize>,
42 pub(crate) codec_config: CodecConfig,
43 pub(crate) exts: ExtensionMap,
44 pub(crate) stats_interval: Option<Duration>,
45 pub(crate) intervals: RtcpReportIntervals,
46 pub(crate) bwe_config: Option<BweConfig>,
47 pub(crate) reordering_size_audio: usize,
48 pub(crate) reordering_size_video: usize,
49 pub(crate) send_buffer_audio: usize,
50 pub(crate) send_buffer_video: usize,
51 pub(crate) rtp_mode: bool,
52 pub(crate) enable_raw_packets: bool,
53 pub(crate) dtls_version: DtlsVersion,
54 pub(crate) vp9_packetizer_mode: Vp9PacketizerMode,
55 pub(crate) snap_enabled: bool,
56 pub(crate) sctp_receive_limits: Option<crate::channel::SctpReceiveLimits>,
57 pub(crate) mtu: RangeInclusive<usize>,
58}
59 
60#[derive(Debug, Clone)]
61pub(crate) struct BweConfig {
62 pub(crate) initial_bitrate: Bitrate,
63}
64 
65#[derive(Debug, Clone, Copy)]
66pub(crate) struct RtcpReportIntervals {
67 pub(crate) audio: Duration,
68 pub(crate) video: Duration,
69}
70 
71impl RtcpReportIntervals {
72 pub(crate) fn for_audio(self, audio: bool) -> Duration {
73 if audio { self.audio } else { self.video }
74 }
75}
76 
77impl RtcConfig {
78 /// Creates a new default config.
79 pub fn new() -> Self {
80 RtcConfig::default()
81 }
82 
83 /// Get the local ICE credentials, if set.
84 ///
85 /// If not specified, local credentials will be randomly generated when
86 /// building the [`Rtc`] instance.
87 pub fn local_ice_credentials(&self) -> &Option<IceCreds> {
88 &self.local_ice_credentials
89 }
90 
91 /// Explicitly sets local ICE credentials.
92 pub fn set_local_ice_credentials(mut self, local_ice_credentials: IceCreds) -> Self {
93 self.local_ice_credentials = Some(local_ice_credentials);
94 self
95 }
96 
97 /// Set the crypto provider.
98 ///
99 /// This overrides what is set in [`crate::crypto::CryptoProvider::install_process_default()`].
100 pub fn set_crypto_provider(mut self, p: Arc<CryptoProvider>) -> Self {
101 self.crypto_provider = Some(p);
102 self
103 }
104 
105 /// The configured crypto provider, if explicitly set.
106 ///
107 /// Returns `None` if not explicitly set via [`Self::set_crypto_provider()`].
108 /// When `None`, the process default will be checked when building the [`Rtc`] instance.
109 pub fn crypto_provider(&self) -> Option<&Arc<CryptoProvider>> {
110 self.crypto_provider.as_ref()
111 }
112 
113 /// Returns the configured DTLS certificate, if set.
114 ///
115 /// If not set, a certificate will be generated automatically.
116 pub fn dtls_cert(&self) -> Option<&DtlsCert> {
117 self.dtls_cert.as_ref()
118 }
119 
120 /// Set a pregenerated DTLS certificate.
121 ///
122 /// If not set, a certificate will be generated automatically using
123 /// the configured crypto provider.
124 ///
125 /// ```
126 /// # use str0m::RtcConfig;
127 /// # use str0m::crypto;
128 ///
129 /// let provider = crypto::from_feature_flags();
130 /// let cert = provider.dtls_provider.generate_certificate().unwrap();
131 /// let rtc_config = RtcConfig::default()
132 /// .set_dtls_cert(cert);
133 /// ```
134 pub fn set_dtls_cert(mut self, cert: DtlsCert) -> Self {
135 self.dtls_cert = Some(cert);
136 self
137 }
138 
139 /// Toggle ice lite. Ice lite is a mode for WebRTC servers with public IP address.
140 /// An [`Rtc`] instance in ice lite mode will not make STUN binding requests, but only
141 /// answer to requests from the remote peer.
142 ///
143 /// See [ICE RFC][1]
144 ///
145 /// [1]: https://www.rfc-editor.org/rfc/rfc8445#page-13
146 pub fn set_ice_lite(mut self, enabled: bool) -> Self {
147 self.ice_lite = enabled;
148 self
149 }
150 
151 /// Sets the initial STUN retransmission timeout (RTO).
152 ///
153 /// This is the initial wait time before a STUN request is retransmitted.
154 /// The timeout will double with each retry, starting from this value.
155 ///
156 /// Defaults to 250ms.
157 pub fn set_initial_stun_rto(&mut self, rto: Duration) {
158 self.initial_stun_rto = Some(rto);
159 }
160 
161 /// Sets the maximum STUN retransmission timeout for the ICE agent.
162 ///
163 /// This is the upper bound for how long to wait between retransmissions.
164 /// It also controls how often successful bindings are checked.
165 ///
166 /// Defaults to 3000ms.
167 pub fn set_max_stun_rto(&mut self, rto: Duration) {
168 self.max_stun_rto = Some(rto);
169 }
170 
171 /// Sets the maximum number of retransmits for STUN messages.
172 ///
173 /// Defaults to 9.
174 pub fn set_max_stun_retransmits(&mut self, num: usize) {
175 self.max_stun_retransmits = Some(num);
176 }
177 
178 /// Get fingerprint verification mode.
179 ///
180 /// ```
181 /// # use str0m::Rtc;
182 /// let config = Rtc::builder();
183 ///
184 /// // Defaults to true.
185 /// assert!(config.fingerprint_verification());
186 /// ```
187 pub fn fingerprint_verification(&self) -> bool {
188 self.fingerprint_verification
189 }
190 
191 /// Toggle certificate fingerprint verification.
192 ///
193 /// By default the certificate fingerprint is verified.
194 pub fn set_fingerprint_verification(mut self, enabled: bool) -> Self {
195 self.fingerprint_verification = enabled;
196 self
197 }
198 
199 /// Tells whether ice lite is enabled.
200 ///
201 /// ```
202 /// # #[cfg(feature = "openssl")] {
203 /// # use str0m::Rtc;
204 /// let config = Rtc::builder();
205 ///
206 /// // Defaults to false.
207 /// assert_eq!(config.ice_lite(), false);
208 /// # }
209 /// ```
210 pub fn ice_lite(&self) -> bool {
211 self.ice_lite
212 }
213 
214 /// Lower level access to precise configuration of codecs (payload types).
215 pub fn codec_config(&mut self) -> &mut CodecConfig {
216 &mut self.codec_config
217 }
218 
219 /// Clear all configured codecs.
220 ///
221 /// ```
222 /// # #[cfg(feature = "openssl")] {
223 /// # use std::time::Instant;
224 /// # use str0m::RtcConfig;
225 /// // For the session to use only OPUS and VP8.
226 /// let mut rtc = RtcConfig::default()
227 /// .clear_codecs()
228 /// .enable_opus(true)
229 /// .enable_vp8(true)
230 /// .build(Instant::now());
231 /// # }
232 /// ```
233 pub fn clear_codecs(mut self) -> Self {
234 self.codec_config.clear();
235 self
236 }
237 
238 /// Enable opus audio codec.
239 ///
240 /// Enabled by default.
241 pub fn enable_opus(mut self, enabled: bool) -> Self {
242 self.codec_config.enable_opus(enabled);
243 self
244 }
245 
246 /// Enable PCM μ-law audio codec.
247 ///
248 /// This is 14-bit audio compressed to 8-bit as specified by G.711
249 pub fn enable_pcmu(mut self, enabled: bool) -> Self {
250 self.codec_config.enable_pcmu(enabled);
251 self
252 }
253 
254 /// Enable PCM a-law audio codec.
255 ///
256 /// This is 13-bit audio compressed to 8-bit as specified by G.711
257 pub fn enable_pcma(mut self, enabled: bool) -> Self {
258 self.codec_config.enable_pcma(enabled);
259 self
260 }
261 
262 /// Enable G722 audio codec.
263 ///
264 /// This is 16 kHz audio compressed to 64 kbit/s as specified by G.722. Note that
265 /// per RFC 3551 §4.5.2 the RTP clock rate on the wire is 8000 Hz even though the
266 /// codec samples at 16 kHz. str0m treats G722 as a 16 kHz codec in the API and
267 /// handles the 8 kHz RTP timestamp mapping internally. See
268 /// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
269 pub fn enable_g722(mut self, enabled: bool) -> Self {
270 self.codec_config.enable_g722(enabled);
271 self
272 }
273 
274 /// Enable the RFC 3389 Comfort Noise payload at 8000 Hz.
275 ///
276 /// Disabled by default. Higher clock rates require a dynamic payload type
277 /// and can be added through [`RtcConfig::codec_config`].
278 pub fn enable_comfort_noise(mut self, enabled: bool) -> Self {
279 self.codec_config.enable_comfort_noise(enabled);
280 self
281 }
282 
283 /// Enable VP8 video codec.
284 ///
285 /// Enabled by default.
286 pub fn enable_vp8(mut self, enabled: bool) -> Self {
287 self.codec_config.enable_vp8(enabled);
288 self
289 }
290 
291 /// Enable H264 video codec.
292 ///
293 /// Enabled by default.
294 pub fn enable_h264(mut self, enabled: bool) -> Self {
295 self.codec_config.enable_h264(enabled);
296 self
297 }
298 
299 /// Enable H265 video codec.
300 ///
301 /// Enabled by default.
302 pub fn enable_h265(mut self, enabled: bool) -> Self {
303 self.codec_config.enable_h265(enabled);
304 self
305 }
306 
307 /// Enable H266 (VVC) video codec.
308 ///
309 /// Disabled by default: no major browser (Chrome, Safari, Firefox)
310 /// currently supports H.266/VVC, so it is opt-in.
311 pub fn enable_h266(mut self, enabled: bool) -> Self {
312 self.codec_config.enable_h266(enabled);
313 self
314 }
315 
316 /// Enable VP9 video codec.
317 ///
318 /// Enabled by default.
319 pub fn enable_vp9(mut self, enabled: bool) -> Self {
320 self.codec_config.enable_vp9(enabled);
321 self
322 }
323 
324 /// Enable AV1 video codec.
325 ///
326 /// Enabled by default.
327 pub fn enable_av1(mut self, enabled: bool) -> Self {
328 self.codec_config.enable_av1(enabled);
329 self
330 }
331 
332 /// Configure the RTP extension mappings.
333 ///
334 /// The default extension map is
335 ///
336 /// ```
337 /// # use str0m::rtp::{Extension, ExtensionMap};
338 /// let exts = ExtensionMap::standard();
339 ///
340 /// assert_eq!(exts.id_of(Extension::AudioLevel), Some(1));
341 /// assert_eq!(exts.id_of(Extension::AbsoluteSendTime), Some(2));
342 /// assert_eq!(exts.id_of(Extension::TransportSequenceNumber), Some(3));
343 /// assert_eq!(exts.id_of(Extension::RtpMid), Some(4));
344 /// assert_eq!(exts.id_of(Extension::RtpStreamId), Some(10));
345 /// assert_eq!(exts.id_of(Extension::RepairedRtpStreamId), Some(11));
346 /// assert_eq!(exts.id_of(Extension::VideoOrientation), Some(13));
347 /// ```
348 pub fn extension_map(&mut self) -> &mut ExtensionMap {
349 &mut self.exts
350 }
351 
352 /// Set the extension map replacing the existing.
353 pub fn set_extension_map(mut self, exts: ExtensionMap) -> Self {
354 self.exts = exts;
355 self
356 }
357 
358 /// Clear out the standard extension mappings.
359 pub fn clear_extension_map(mut self) -> Self {
360 self.exts.clear();
361 
362 self
363 }
364 
365 /// Set an extension mapping on session level.
366 ///
367 /// The media level will be capped by the extension enabled on session level.
368 ///
369 /// The id must be 1-14 inclusive (1-indexed).
370 pub fn set_extension(mut self, id: u8, ext: Extension) -> Self {
371 self.exts.set(id, ext);
372 self
373 }
374 
375 /// Set the interval between statistics events.
376 ///
377 /// None turns off the stats events.
378 ///
379 /// This includes [`MediaEgressStats`][crate::stats::MediaEgressStats],
380 /// [`MediaIngressStats`][crate::stats::MediaIngressStats]
381 pub fn set_stats_interval(mut self, interval: Option<Duration>) -> Self {
382 self.stats_interval = interval;
383 self
384 }
385 
386 /// The configured statistics interval.
387 ///
388 /// None means statistics are disabled.
389 ///
390 /// ```
391 /// # use str0m::Rtc;
392 /// # use std::time::Duration;
393 /// let config = Rtc::builder();
394 ///
395 /// // Defaults to None.
396 /// assert_eq!(config.stats_interval(), None);
397 /// ```
398 pub fn stats_interval(&self) -> Option<Duration> {
399 self.stats_interval
400 }
401 
402 /// Sets the interval between RTCP sender/receiver reports for audio streams.
403 ///
404 /// Defaults to 5 seconds.
405 ///
406 /// # Panics
407 ///
408 /// Panics if `interval` is zero.
409 pub fn set_rtcp_report_interval_audio(mut self, interval: Duration) -> Self {
410 assert!(!interval.is_zero());
411 self.intervals.audio = interval;
412 self
413 }
414 
415 /// Returns the interval between RTCP sender/receiver reports for audio streams.
416 pub fn rtcp_report_interval_audio(&self) -> Duration {
417 self.intervals.audio
418 }
419 
420 /// Sets the interval between RTCP sender/receiver reports for video streams.
421 ///
422 /// Defaults to 1 second.
423 ///
424 /// # Panics
425 ///
426 /// Panics if `interval` is zero.
427 pub fn set_rtcp_report_interval_video(mut self, interval: Duration) -> Self {
428 assert!(!interval.is_zero());
429 self.intervals.video = interval;
430 self
431 }
432 
433 /// Returns the interval between RTCP sender/receiver reports for video streams.
434 pub fn rtcp_report_interval_video(&self) -> Duration {
435 self.intervals.video
436 }
437 
438 /// Enables estimation of available bandwidth (BWE).
439 ///
440 /// None disables the BWE. This is an estimation of the send bandwidth, not receive.
441 ///
442 /// This includes setting the initial estimate to start with.
443 pub fn enable_bwe(mut self, initial_estimate: Option<Bitrate>) -> Self {
444 match initial_estimate {
445 Some(b) => {
446 let conf = self.bwe_config.get_or_insert(BweConfig::new(b));
447 conf.initial_bitrate = b;
448 }
449 None => {
450 self.bwe_config = None;
451 }
452 }
453 
454 self
455 }
456 
457 /// The initial bitrate as set by [`Self::enable_bwe()`].
458 ///
459 /// ```
460 /// # use str0m::Rtc;
461 /// let config = Rtc::builder();
462 ///
463 /// // Defaults to None - BWE off.
464 /// assert_eq!(config.bwe_initial_bitrate(), None);
465 /// ```
466 pub fn bwe_initial_bitrate(&self) -> Option<Bitrate> {
467 self.bwe_config.as_ref().map(|c| c.initial_bitrate)
468 }
469 
470 /// Sets the number of packets held back for reordering audio packets.
471 ///
472 /// Str0m tries to deliver the frames in order. This number determines how many
473 /// packets to "wait" before releasing media
474 /// [`contiguous: false`][crate::media::MediaData::contiguous].
475 ///
476 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
477 /// packets can arrive out of order.
478 pub fn set_reordering_size_audio(mut self, size: usize) -> Self {
479 self.reordering_size_audio = size;
480 
481 self
482 }
483 
484 /// Returns the setting for audio reordering size.
485 ///
486 /// ```
487 /// # use str0m::Rtc;
488 /// let config = Rtc::builder();
489 ///
490 /// // Defaults to 15.
491 /// assert_eq!(config.reordering_size_audio(), 15);
492 /// ```
493 ///
494 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
495 /// packets can arrive out of order.
496 pub fn reordering_size_audio(&self) -> usize {
497 self.reordering_size_audio
498 }
499 
500 /// Sets the number of packets held back for reordering video packets.
501 ///
502 /// Str0m tries to deliver the frames in order. This number determines how many
503 /// packets to "wait" before releasing media with gaps.
504 ///
505 /// This must be at least as big as the number of packets the biggest keyframe
506 /// can be split over.
507 ///
508 /// WARNING: video is very different to audio. Setting this value too low will result in
509 /// missing video data. The 0 (as described for audio) is not relevant for video.
510 ///
511 /// Default: 30
512 ///
513 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
514 /// packets can arrive out of order.
515 pub fn set_reordering_size_video(mut self, size: usize) -> Self {
516 self.reordering_size_video = size;
517 
518 self
519 }
520 
521 /// Returns the setting for video reordering size.
522 ///
523 /// ```
524 /// # use str0m::Rtc;
525 /// let config = Rtc::builder();
526 ///
527 /// // Defaults to 30.
528 /// assert_eq!(config.reordering_size_video(), 30);
529 /// ```
530 ///
531 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
532 /// packets can arrive out of order.
533 pub fn reordering_size_video(&self) -> usize {
534 self.reordering_size_video
535 }
536 
537 /// Sets the buffer size for outgoing audio packets.
538 ///
539 /// This must be larger than 0. The value configures an internal ring buffer used as a temporary
540 /// holding space between calling [`Writer::write`][crate::media::Writer::write()] and
541 /// [`Rtc::poll_output`].
542 ///
543 /// For audio one call to `write()` typically results in one RTP packet since the entire
544 /// payload fits in one. If you can guarantee that every `write()` is a single RTP packet,
545 /// and is always followed by a `poll_output()`, it might be possible to set this value to 1.
546 /// But that would give no margins for unexpected patterns.
547 ///
548 /// panics if set to 0.
549 pub fn set_send_buffer_audio(mut self, size: usize) -> Self {
550 assert!(size > 0);
551 self.send_buffer_audio = size;
552 self
553 }
554 
555 /// Returns the setting for audio resend size.
556 ///
557 /// ```
558 /// # use str0m::Rtc;
559 /// let config = Rtc::builder();
560 ///
561 /// // Defaults to 50.
562 /// assert_eq!(config.send_buffer_audio(), 50);
563 /// ```
564 pub fn send_buffer_audio(&self) -> usize {
565 self.send_buffer_audio
566 }
567 
568 /// Sets the buffer size for outgoing video packets and resends.
569 ///
570 /// This must be larger than 0. The value configures an internal ring buffer that is both
571 /// used as a temporary holding space between calling
572 /// [`Writer::write`][crate::media::Writer::write()] and [`Rtc::poll_output`] as well as for
573 /// fulfilling resends.
574 ///
575 /// For video, this buffer is used for more than for audio. First, a call to `write()` often
576 /// results in multiple RTP packets since large frames don't fit in one payload. That means
577 /// the buffer must be at least as large to hold all those packets. Second, when the remote
578 /// requests resends (NACK), those are fulfilled from this buffer. Third, for Bandwidth
579 /// Estimation (BWE), when probing for available bandwidth, packets from this buffer are used
580 /// to do "spurious resends", i.e. we do resends for packets that were not asked for.
581 pub fn set_send_buffer_video(mut self, size: usize) -> Self {
582 self.send_buffer_video = size;
583 self
584 }
585 
586 /// Returns the setting for video resend size.
587 ///
588 /// ```
589 /// # use str0m::Rtc;
590 /// let config = Rtc::builder();
591 ///
592 /// // Defaults to 1000.
593 /// assert_eq!(config.send_buffer_video(), 1000);
594 /// ```
595 pub fn send_buffer_video(&self) -> usize {
596 self.send_buffer_video
597 }
598 
599 /// Make the entire Rtc be in RTP mode.
600 ///
601 /// This means all media, read from [`RtpPacket`][crate::rtp::RtpPacket] and written to
602 /// [`StreamTx::write_rtp`][crate::rtp::StreamTx::write_rtp] are RTP packetized.
603 /// It bypasses all internal packetization/depacketization inside str0m.
604 ///
605 /// WARNING: This is a low level API and is not str0m's primary use case.
606 pub fn set_rtp_mode(mut self, enabled: bool) -> Self {
607 self.rtp_mode = enabled;
608 
609 self
610 }
611 
612 /// Checks if RTP mode is set.
613 ///
614 /// ```
615 /// # use str0m::Rtc;
616 /// let config = Rtc::builder();
617 ///
618 /// // Defaults to false.
619 /// assert_eq!(config.rtp_mode(), false);
620 /// ```
621 pub fn rtp_mode(&self) -> bool {
622 self.rtp_mode
623 }
624 
625 /// Enable the [`Event::RawPacket`][crate::Event::RawPacket] event.
626 ///
627 /// This clones data, and is therefore expensive.
628 /// Should not be enabled outside of tests and troubleshooting.
629 pub fn enable_raw_packets(mut self, enabled: bool) -> Self {
630 self.enable_raw_packets = enabled;
631 self
632 }
633 
634 /// Enable SNAP (SCTP Negotiation Acceleration Protocol).
635 ///
636 /// When enabled, the `a=sctp-init` attribute is included in outgoing SDP
637 /// offers, allowing the SCTP association to skip the 4-way handshake
638 /// and go directly to established state. This saves up to two network
639 /// round-trip times when establishing data channels.
640 ///
641 /// If the remote answer does not accept SNAP, str0m logs a warning and
642 /// falls back to the regular SCTP handshake.
643 ///
644 /// When this is `false`, outgoing SDP offers will not include `a=sctp-init`.
645 /// However, incoming offers that contain `a=sctp-init` will still be
646 /// reciprocated in answers, since str0m always supports the extension at
647 /// the protocol level (per §5.4 of draft-hancke-tsvwg-snap).
648 ///
649 /// See [draft-hancke-tsvwg-snap](https://datatracker.ietf.org/doc/draft-hancke-tsvwg-snap/).
650 ///
651 /// Default: `false`
652 pub fn set_snap_enabled(mut self, enabled: bool) -> Self {
653 self.snap_enabled = enabled;
654 self
655 }
656 
657 /// Set hard resource limits for retained inbound SCTP DATA state.
658 ///
659 /// The per-message limit is advertised in SDP and enforced during fragment
660 /// reassembly. Byte and chunk limits also include reset-deferred DATA and
661 /// DATA retained behind missing TSNs. Stream count is independent of the
662 /// numerical IDs. Exceeding a limit closes the SCTP association.
663 ///
664 /// This does not bound total SCTP heap use or control-chunk metadata. Default
665 /// behavior is unchanged when no limits are configured. For direct SNAP,
666 /// create matching `SctpInitData::with_receive_limits` before signaling INIT.
667 pub fn set_sctp_receive_limits(mut self, limits: crate::channel::SctpReceiveLimits) -> Self {
668 self.sctp_receive_limits = Some(limits);
669 self
670 }
671 
672 /// Set which DTLS version to use.
673 ///
674 /// Defaults to [`DtlsVersion::Dtls12`].
675 pub fn set_dtls_version(mut self, version: DtlsVersion) -> Self {
676 self.dtls_version = version;
677 self
678 }
679 
680 /// Get the configured DTLS version.
681 pub fn dtls_version(&self) -> DtlsVersion {
682 self.dtls_version
683 }
684 
685 /// Set the MTU as a `target..=warn` inclusive range.
686 ///
687 /// * `*range.start()` is the **target** MTU: the size str0m aims for when
688 /// fragmenting outgoing packets. Must be within
689 /// `[DATAGRAM_MTU_TARGET_MIN, DATAGRAM_MTU_TARGET_MAX]`.
690 /// * `*range.end()` is the **warn** threshold: packets whose length
691 /// exceeds the target trigger a warning.
692 ///
693 /// Set as `target..=target` to warn packets bigger than target.
694 /// Set as `target..=usize::MAX` to disable warning.
695 ///
696 /// Defaults to `DATAGRAM_MTU_TARGET..=DATAGRAM_MTU_WARN`.
697 pub fn set_mtu(mut self, range: RangeInclusive<usize>) -> Self {
698 let (start, end) = (*range.start(), *range.end());
699 assert!(
700 DATAGRAM_MTU_TARGET_MIN <= start && start <= DATAGRAM_MTU_TARGET_MAX,
701 "mtu target {} out of bounds [{}, {}]",
702 start,
703 DATAGRAM_MTU_TARGET_MIN,
704 DATAGRAM_MTU_TARGET_MAX,
705 );
706 assert!(
707 start <= end,
708 "mtu range start must be <= end, got {}..={}",
709 start,
710 end,
711 );
712 self.mtu = range;
713 self
714 }
715 
716 /// Get the configured target MTU (the value str0m aims for).
717 pub fn mtu(&self) -> usize {
718 *self.mtu.start()
719 }
720 
721 /// Get the configured warn threshold above which outgoing packets
722 /// trigger a warning trace.
723 pub fn mtu_warn(&self) -> usize {
724 *self.mtu.end()
725 }
726 
727 /// Set the VP9 packetizer mode.
728 ///
729 /// VP9 supports two RTP payload descriptor formats:
730 ///
731 /// * [`Vp9PacketizerMode::NonFlexible`] (default) — 5-6 byte header with layer indices
732 /// and scalability structure. Compatible with all major browsers including Safari.
733 /// * [`Vp9PacketizerMode::Flexible`] — minimal 3-byte header. Simpler but may cause
734 /// issues with Safari which drops inter-frames.
735 pub fn set_vp9_packetizer_mode(mut self, mode: Vp9PacketizerMode) -> Self {
736 self.vp9_packetizer_mode = mode;
737 self
738 }
739 
740 /// Returns the configured VP9 packetizer mode.
741 ///
742 /// ```
743 /// # use str0m::Rtc;
744 /// # use str0m::format::Vp9PacketizerMode;
745 /// let config = Rtc::builder();
746 ///
747 /// // Defaults to NonFlexible.
748 /// assert_eq!(config.vp9_packetizer_mode(), Vp9PacketizerMode::NonFlexible);
749 /// ```
750 pub fn vp9_packetizer_mode(&self) -> Vp9PacketizerMode {
751 self.vp9_packetizer_mode
752 }
753 
754 /// Create a [`Rtc`] from the configuration.
755 pub fn build(self, start: Instant) -> Rtc {
756 Rtc::new_from_config(self, start).expect("Failed to create Rtc from config")
757 }
758}
759 
760impl BweConfig {
761 fn new(initial_bitrate: Bitrate) -> Self {
762 Self { initial_bitrate }
763 }
764}
765 
766impl Default for RtcConfig {
767 fn default() -> Self {
768 Self {
769 local_ice_credentials: None,
770 crypto_provider: None,
771 dtls_cert: None,
772 fingerprint_verification: true,
773 ice_lite: false,
774 initial_stun_rto: None,
775 max_stun_rto: None,
776 max_stun_retransmits: None,
777 codec_config: CodecConfig::new_with_defaults(),
778 exts: ExtensionMap::standard(),
779 stats_interval: None,
780 intervals: RtcpReportIntervals {
781 audio: Duration::from_secs(5),
782 video: Duration::from_secs(1),
783 },
784 bwe_config: None,
785 reordering_size_audio: 15,
786 reordering_size_video: 30,
787 send_buffer_audio: 50,
788 send_buffer_video: 1000,
789 rtp_mode: false,
790 enable_raw_packets: false,
791 dtls_version: DtlsVersion::Dtls12,
792 vp9_packetizer_mode: Vp9PacketizerMode::default(),
793 snap_enabled: false,
794 sctp_receive_limits: None,
795 mtu: DATAGRAM_MTU_TARGET..=DATAGRAM_MTU_WARN,
796 }
797 }
798}
799 
800#[cfg(test)]
801mod tests {
802 use super::*;
803 
804 #[test]
805 fn enable_comfort_noise_updates_codec_config() {
806 let mut cfg = RtcConfig::default().enable_comfort_noise(true);
807 
808 assert!(
809 cfg.codec_config()
810 .params()
811 .iter()
812 .any(|p| p.spec().codec == crate::format::Codec::CN)
813 );
814 }
815 
816 #[test]
817 fn default_mtu_is_datagram_mtu_target() {
818 let cfg = RtcConfig::default();
819 assert_eq!(cfg.mtu(), DATAGRAM_MTU_TARGET);
820 assert_eq!(cfg.mtu_warn(), DATAGRAM_MTU_WARN);
821 }
822 
823 #[test]
824 fn set_mtu_round_trips() {
825 let cfg = RtcConfig::default().set_mtu(900..=1400);
826 assert_eq!(cfg.mtu(), 900);
827 assert_eq!(cfg.mtu_warn(), 1400);
828 
829 let cfg = RtcConfig::default().set_mtu(900..=usize::MAX);
830 assert_eq!(cfg.mtu(), 900);
831 assert_eq!(cfg.mtu_warn(), usize::MAX);
832 }
833 
834 #[test]
835 fn set_mtu_accepts_equal_bounds() {
836 let cfg = RtcConfig::default().set_mtu(1200..=1200);
837 assert_eq!(cfg.mtu(), 1200);
838 assert_eq!(cfg.mtu_warn(), 1200);
839 
840 let cfg = RtcConfig::default().set_mtu(DATAGRAM_MTU_TARGET_MIN..=DATAGRAM_MTU_TARGET_MIN);
841 assert_eq!(cfg.mtu(), DATAGRAM_MTU_TARGET_MIN);
842 
843 let cfg = RtcConfig::default().set_mtu(DATAGRAM_MTU_TARGET_MAX..=DATAGRAM_MTU_TARGET_MAX);
844 assert_eq!(cfg.mtu(), DATAGRAM_MTU_TARGET_MAX);
845 }
846 
847 #[test]
848 #[should_panic(expected = "mtu target")]
849 fn set_mtu_panics_on_target_below_min() {
850 let _ = RtcConfig::default().set_mtu((DATAGRAM_MTU_TARGET_MIN - 1)..=usize::MAX);
851 }
852 
853 #[test]
854 #[should_panic(expected = "mtu target")]
855 fn set_mtu_panics_on_target_above_max() {
856 let _ = RtcConfig::default().set_mtu((DATAGRAM_MTU_TARGET_MAX + 1)..=usize::MAX);
857 }
858 
859 #[test]
860 #[should_panic(expected = "mtu range start must be <= end")]
861 fn set_mtu_panics_on_inverted_range() {
862 let _ = RtcConfig::default().set_mtu(1300..=1299);
863 }
864}