str0m/lib.rs
1//! <image src="https://user-images.githubusercontent.com/227204/226143511-66fe5264-6ab7-47b9-9551-90ba7e155b96.svg" alt="str0m logo" ></image>
2//!
3//! A Sans I/O WebRTC implementation in Rust.
4//!
5//! This is a [Sans I/O][sansio] implementation meaning the `Rtc` instance itself is not doing any network
6//! talking. Furthermore it has no internal threads or async tasks. All operations are happening from the
7//! calls of the public API.
8//!
9//! This is deliberately not a standard `RTCPeerConnection` API since that isn't a great fit for Rust.
10//! See more details in below section.
11//!
12//! # Join us
13//!
14//! We are discussing str0m things on Zulip. Join us using this [invitation link][zulip]. Or browse the
15//! discussions anonymously at [str0m.zulipchat.com][zulip-anon]
16//!
17//! <image width="300px" src="https://user-images.githubusercontent.com/227204/209446544-f8a8d673-cb1b-4144-a0f2-42307b8d8869.gif" alt="silly clip showing video playing" ></image>
18//!
19//! # Usage
20//!
21//! The [`chat`][x-chat] example shows how to connect multiple browsers
22//! together and act as an SFU (Selective Forwarding Unit). The example
23//! multiplexes all traffic over one server UDP socket and uses two threads
24//! (one for the web server, and one for the SFU loop).
25//!
26//! ## TLS
27//!
28//! For the browser to do WebRTC, all traffic must be under TLS. The
29//! project ships with a self-signed certificate that is used for the
30//! examples. The certificate is for hostname `str0m.test` since TLD .test
31//! should never resolve to a real DNS name.
32//!
33//! ```text
34//! cargo run --example chat
35//! ```
36//!
37//! The log should prompt you to connect a browser to https://10.0.0.103:3000 – this will
38//! most likely cause a security warning that you must get the browser to accept.
39//!
40//! The [`http-post`][x-post] example roughly illustrates how to receive
41//! media data from a browser client. The example is single threaded and
42//! is a bit simpler than the chat. It is a good starting point to understand the API.
43//!
44//! ```text
45//! cargo run --example http-post
46//! ```
47//!
48//! ### Real example
49//!
50//! To see how str0m is used in a real project, check out [BitWHIP][bitwhip] –
51//! a CLI WebRTC Agent written in Rust.
52//!
53//! ## Passive
54//!
55//! For passive connections, i.e. where the media and initial OFFER is
56//! made by a remote peer, we need these steps to open the connection.
57//!
58//! ```no_run
59//! # use str0m::{Rtc, Candidate};
60//! // Instantiate a new Rtc instance.
61//! let mut rtc = Rtc::new();
62//!
63//! // Add some ICE candidate such as a locally bound UDP port.
64//! let addr = "1.2.3.4:5000".parse().unwrap();
65//! let candidate = Candidate::host(addr, "udp").unwrap();
66//! rtc.add_local_candidate(candidate);
67//!
68//! // Accept an incoming offer from the remote peer
69//! // and get the corresponding answer.
70//! let offer = todo!();
71//! let answer = rtc.sdp_api().accept_offer(offer).unwrap();
72//!
73//! // Forward the answer to the remote peer.
74//!
75//! // Go to _run loop_
76//! ```
77//!
78//! ## Active
79//!
80//! Active connections means we are making the inital OFFER and waiting for a
81//! remote ANSWER to start the connection.
82//!
83//! ```no_run
84//! # use str0m::{Rtc, Candidate};
85//! # use str0m::media::{MediaKind, Direction};
86//! // Instantiate a new Rtc instance.
87//! let mut rtc = Rtc::new();
88//!
89//! // Add some ICE candidate such as a locally bound UDP port.
90//! let addr = "1.2.3.4:5000".parse().unwrap();
91//! let candidate = Candidate::host(addr, "udp").unwrap();
92//! rtc.add_local_candidate(candidate);
93//!
94//! // Create a `SdpApi`. The change lets us make multiple changes
95//! // before sending the offer.
96//! let mut change = rtc.sdp_api();
97//!
98//! // Do some change. A valid OFFER needs at least one "m-line" (media).
99//! let mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
100//!
101//! // Get the offer.
102//! let (offer, pending) = change.apply().unwrap();
103//!
104//! // Forward the offer to the remote peer and await the answer.
105//! // How to transfer this is outside the scope for this library.
106//! let answer = todo!();
107//!
108//! // Apply answer.
109//! rtc.sdp_api().accept_answer(pending, answer).unwrap();
110//!
111//! // Go to _run loop_
112//! ```
113//!
114//! ## Run loop
115//!
116//! Driving the state of the `Rtc` forward is a run loop that, regardless of sync or async,
117//! looks like this.
118//!
119//! ```no_run
120//! # use str0m::{Rtc, Output, IceConnectionState, Event, Input};
121//! # use str0m::net::{Receive, Protocol};
122//! # use std::io::ErrorKind;
123//! # use std::net::UdpSocket;
124//! # use std::time::Instant;
125//! # let rtc = Rtc::new();
126//! // Buffer for reading incoming UDP packets.
127//! let mut buf = vec![0; 2000];
128//!
129//! // A UdpSocket we obtained _somehow_.
130//! let socket: UdpSocket = todo!();
131//!
132//! loop {
133//! // Poll output until we get a timeout. The timeout means we
134//! // are either awaiting UDP socket input or the timeout to happen.
135//! let timeout = match rtc.poll_output().unwrap() {
136//! // Stop polling when we get the timeout.
137//! Output::Timeout(v) => v,
138//!
139//! // Transmit this data to the remote peer. Typically via
140//! // a UDP socket. The destination IP comes from the ICE
141//! // agent. It might change during the session.
142//! Output::Transmit(v) => {
143//! socket.send_to(&v.contents, v.destination).unwrap();
144//! continue;
145//! }
146//!
147//! // Events are mainly incoming media data from the remote
148//! // peer, but also data channel data and statistics.
149//! Output::Event(v) => {
150//!
151//! // Abort if we disconnect.
152//! if v == Event::IceConnectionStateChange(IceConnectionState::Disconnected) {
153//! return;
154//! }
155//!
156//! // TODO: handle more cases of v here, such as incoming media data.
157//!
158//! continue;
159//! }
160//! };
161//!
162//! // Duration until timeout.
163//! let duration = timeout - Instant::now();
164//!
165//! // socket.set_read_timeout(Some(0)) is not ok
166//! if duration.is_zero() {
167//! // Drive time forwards in rtc straight away.
168//! rtc.handle_input(Input::Timeout(Instant::now())).unwrap();
169//! continue;
170//! }
171//!
172//! socket.set_read_timeout(Some(duration)).unwrap();
173//!
174//! // Scale up buffer to receive an entire UDP packet.
175//! buf.resize(2000, 0);
176//!
177//! // Try to receive. Because we have a timeout on the socket,
178//! // we will either receive a packet, or timeout.
179//! // This is where having an async loop shines. We can await multiple things to
180//! // happen such as outgoing media data, the timeout and incoming network traffic.
181//! // When using async there is no need to set timeout on the socket.
182//! let input = match socket.recv_from(&mut buf) {
183//! Ok((n, source)) => {
184//! // UDP data received.
185//! buf.truncate(n);
186//! Input::Receive(
187//! Instant::now(),
188//! Receive {
189//! proto: Protocol::Udp,
190//! source,
191//! destination: socket.local_addr().unwrap(),
192//! contents: buf.as_slice().try_into().unwrap(),
193//! },
194//! )
195//! }
196//!
197//! Err(e) => match e.kind() {
198//! // Expected error for set_read_timeout().
199//! // One for windows, one for the rest.
200//! ErrorKind::WouldBlock
201//! | ErrorKind::TimedOut => Input::Timeout(Instant::now()),
202//!
203//! e => {
204//! eprintln!("Error: {:?}", e);
205//! return; // abort
206//! }
207//! },
208//! };
209//!
210//! // Input is either a Timeout or Receive of data. Both drive the state forward.
211//! rtc.handle_input(input).unwrap();
212//! }
213//! ```
214//!
215//! ## Sending media data
216//!
217//! When creating the media, we can decide which codecs to support, and they
218//! are negotiated with the remote side. Each codec corresponds to a
219//! "payload type" (PT). To send media data we need to figure out which PT
220//! to use when sending.
221//!
222//! ```no_run
223//! # use str0m::Rtc;
224//! # use str0m::media::Mid;
225//! # let rtc: Rtc = todo!();
226//! // Obtain mid from Event::MediaAdded
227//! let mid: Mid = todo!();
228//!
229//! // Create a media writer for the mid.
230//! let writer = rtc.writer(mid).unwrap();
231//!
232//! // Get the payload type (pt) for the wanted codec.
233//! let pt = writer.payload_params().nth(0).unwrap().pt();
234//!
235//! // Write the data
236//! let wallclock = todo!(); // Absolute time of the data
237//! let media_time = todo!(); // Media time, in RTP time
238//! let data: &[u8] = todo!(); // Actual data
239//! writer.write(pt, wallclock, media_time, data).unwrap();
240//! ```
241//!
242//! ## Media time, wallclock and local time
243//!
244//! str0m has three main concepts of time. "now", media time and wallclock.
245//!
246//! ### Now
247//!
248//! Some calls in str0m, such as `Rtc::handle_input` takes a `now` argument
249//! that is a `std::time::Instant`. These calls "drive the time forward" in
250//! the internal state. This is used for everything like deciding when
251//! to produce various feedback reports (RTCP) to remote peers, to
252//! bandwidth estimation (BWE) and statistics.
253//!
254//! Str0m has _no internal clock_ calls. I.e. str0m never calls
255//! `Instant::now()` itself. All time is external input. That means it's
256//! possible to construct test cases driving an `Rtc` instance faster
257//! than realtime (see the [integration tests][intg]).
258//!
259//! ### Media time
260//!
261//! Each RTP header has a 32 bit number that str0m calls _media time_.
262//! Media time is in some time base that is dependent on the codec,
263//! however all codecs in str0m use 90_000Hz for video and 48_000Hz
264//! for audio.
265//!
266//! For video the `MediaTime` type is `<timestamp>/90_000` str0m extends
267//! the 32 bit number in the RTP header to 64 bit taking into account
268//! "rollover". 64 bit is such a large number the user doesn't need to
269//! think about rollovers.
270//!
271//! ### Wallclock
272//!
273//! With _wallclock_ str0m means the time a sample of media was produced
274//! at an originating source. I.e. if we are talking into a microphone the
275//! wallclock is the NTP time the sound is sampled.
276//!
277//! We can't know the exact wallclock for media from a remote peer since
278//! not every device is synchronized with NTP. Every sender does
279//! periodically produce a Sender Report (SR) that contains the peer's
280//! idea of its wallclock, however this number can be very wrong compared to
281//! "real" NTP time.
282//!
283//! Furthermore, not all remote devices will have a linear idea of
284//! time passing that exactly matches the local time. A minute on the
285//! remote peer might not be exactly one minute locally.
286//!
287//! These timestamps become important when handling simultaneous audio from
288//! multiple peers.
289//!
290//! When writing media we need to provide str0m with an estimated wallclock.
291//! The simplest strategy is to only trust local time and use arrival time
292//! of the incoming UDP packet. Another simple strategy is to lock some
293//! time T at the first UDP packet, and then offset each wallclock using
294//! `MediaTime`, i.e. for video we could have `T + <media time>/90_000`
295//!
296//! A production worthy SFU probably needs an even more sophisticated
297//! strategy weighing in all possible time sources to get a good estimate
298//! of the remote wallclock for a packet.
299//!
300//! # Crypto backends
301//!
302//! str0m has two crypto backends, `openssl` and `wincrypto`. The default is
303//! `openssl` which works on all platforms (also Windows). Ideally we want a
304//! pure rust version of the crypto code, but WebRTC currently requires
305//! DTLS 1.2 (not the latest version 1.3), and that leaves us only with a
306//! few possible options.
307//!
308//! When compiling for Windows, the `openssl` feature can be removed and
309//! only rely on `wincrypto`. However notice that `str0m` never picks up a
310//! default automatically, you must explicitly configure the crypto backend,
311//! also when removing the `openssl` feature.
312//!
313//! If you are building an application, the easiest is to set the default
314//! for the entire process.
315//!
316//! ```no_run
317//! use str0m::config::CryptoProvider;
318//!
319//! // Will panic if run twice
320//! CryptoProvider::WinCrypto.install_process_default();
321//! ```
322//!
323//! # Project status
324//!
325//! Str0m was originally developed by Martin Algesten of
326//! [Lookback][lookback]. We use str0m for a specific use case: str0m as a
327//! server SFU (as opposed to peer-2-peer). That means we are heavily
328//! testing and developing the parts needed for our use case. Str0m is
329//! intended to be an all-purpose WebRTC library, which means it also
330//! works for peer-2-peer, though that aspect has received less testing.
331//!
332//! Performance is very good, there have been some work the discover and
333//! optimize bottlenecks. Such efforts are of course never ending with
334//! diminishing returns. While there are no glaringly obvious performance
335//! bottlenecks, more work is always welcome – both algorithmically and
336//! allocation/cloning in hot paths etc.
337//!
338//! # Design
339//!
340//! Output from the `Rtc` instance can be grouped into three kinds.
341//!
342//! 1. Events (such as receiving media or data channel data).
343//! 2. Network output. Data to be sent, typically from a UDP socket.
344//! 3. Timeouts. Indicates when the instance next expects a time input.
345//!
346//! Input to the `Rtc` instance is:
347//!
348//! 1. User operations (such as sending media or data channel data).
349//! 2. Network input. Typically read from a UDP socket.
350//! 3. Timeouts. As obtained from the output above.
351//!
352//! The correct use can be seen in the above [Run loop](#run-loop) or in the
353//! examples.
354//!
355//! Sans I/O is a pattern where we turn both network input/output as well
356//! as time passing into external input to the API. This means str0m has
357//! no internal threads, just an enormous state machine that is driven
358//! forward by different kinds of input.
359//!
360//! ## Sample or RTP level?
361//!
362//! Str0m defaults to the "sample level" which treats the RTP as an internal detail. The user
363//! will thus mainly interact with:
364//!
365//! 1. [`Event::MediaData`][evmed] to receive full "samples" (audio frames or video frames).
366//! 2. [`Writer::write`][writer] to write full samples.
367//! 3. [`Writer::request_keyframe`][reqkey] to request keyframes.
368//!
369//! ### Sample level
370//!
371//! All codecs such as h264, vp8, vp9 and opus outputs what we call
372//! "Samples". A sample has a very specific meaning for audio, but this
373//! project uses it in a broader sense, where a sample is either a video
374//! or audio time stamped chunk of encoded data that typically represents
375//! a chunk of audio, or _one single frame for video_.
376//!
377//! Samples are not suitable to use directly in UDP (RTP) packets - for
378//! one they are too big. Samples are therefore further chunked up by
379//! codec specific payloaders into RTP packets.
380//!
381//! ### RTP mode
382//!
383//! Str0m also provides an RTP level API. This would be similar to many other
384//! RTP libraries where the RTP packets themselves are the the API surface
385//! towards the user (when building an SFU one would often talk about "forwarding
386//! RTP packets", while with str0m we can also "forward samples"). Using
387//! this API requires a deeper knowledge of RTP and WebRTC.
388//!
389//! To enable RTP mode
390//!
391//! ```
392//! # #[cfg(feature = "openssl")] {
393//! # use str0m::Rtc;
394//! let rtc = Rtc::builder()
395//! // Enable RTP mode for this Rtc instance.
396//! // This disables `MediaEvent` and the `Writer::write` API.
397//! .set_rtp_mode(true)
398//! .build();
399//! # }
400//! ```
401//!
402//! RTP mode gives us some new API points.
403//!
404//! 1. [`Event::RtpPacket`][rtppak] emitted for every incoming RTP packet. Empty packets for bandwidth
405//! estimation are silently discarded.
406//! 2. [`StreamTx::write_rtp`][wrtrtp] to write outgoing RTP packets.
407//! 3. [`StreamRx::request_keyframe`][reqkey2] to request keyframes from remote.
408//!
409//! ## NIC enumeration and TURN (and STUN)
410//!
411//! The [ICE RFC][ice] talks about "gathering ice candidates". This means
412//! inspecting the local network interfaces and potentially binding UDP
413//! sockets on each usable interface. Since str0m is Sans I/O, this part
414//! is outside the scope of what str0m does. How the user figures out
415//! local IP addresses, via config or via looking up local NICs is not
416//! something str0m cares about.
417//!
418//! TURN is a way of obtaining IP addresses that can be used as fallback
419//! in case direct connections fail. We consider TURN similar to
420//! enumerating local network interfaces – it's a way of obtaining
421//! sockets.
422//!
423//! All discovered candidates, be they local (NIC) or remote sockets
424//! (TURN), are added to str0m and str0m will perform the task of ICE
425//! agent, forming "candidate pairs" and figuring out the best connection
426//! while the actual task of sending the network traffic is left to the
427//! user.
428//!
429//! ## The importance of `&mut self`
430//!
431//! Rust shines when we can eschew locks and heavily rely `&mut` for data
432//! write access. Since str0m has no internal threads, we never have to
433//! deal with shared data. Furthermore the the internals of the library is
434//! organized such that we don't need multiple references to the same
435//! entities. In str0m there are no `Rc`, `Mutex`, `mpsc`, `Arc`(*), or
436//! other locks.
437//!
438//! This means all input to the lib can be modelled as
439//! `handle_something(&mut self, something)`.
440//!
441//! (*) Ok. There is one `Arc` if you use Windows where we also require openssl.
442//!
443//! ## Not a standard WebRTC "Peer Connection" API
444//!
445//! The library deliberately steps away from the "standard" WebRTC API as
446//! seen in JavaScript and/or [webrtc-rs][webrtc-rs] (or [Pion][pion] in Go).
447//! There are few reasons for this.
448//!
449//! First, in the standard API, events are callbacks, which are not a
450//! great fit for Rust. Callbacks require some kind of reference
451//! (ownership?) over the entity the callback is being dispatched
452//! upon. I.e. if in Rust we want `pc.addEventListener(x)`, `x` needs
453//! to be wholly owned by `pc`, or have some shared reference (like
454//! `Arc`). Shared references means shared data, and to get mutable shared
455//! data, we will need some kind of lock. i.e. `Arc<Mutex<EventListener>>`
456//! or similar.
457//!
458//! As an alternative we could turn all events into `mpsc` channels, but
459//! listening to multiple channels is awkward without async.
460//!
461//! Second, in the standard API, entities like `RTCPeerConnection` and
462//! `RTCRtpTransceiver`, are easily clonable and/or long lived
463//! references. I.e. `pc.getTranscievers()` returns objects that can be
464//! retained and owned by the caller. This pattern is fine for garbage
465//! collected or reference counted languages, but not great with Rust.
466//!
467//! ## Panics, Errors and unwraps
468//!
469//! Str0m adheres to [fail-fast][ff]. That means rather than brushing state
470//! bugs under the carpet, it panics. We make a distinction between errors and
471//! bugs.
472//!
473//! * Errors are as a result of incorrect or impossible to understand user input.
474//! * Bugs are broken internal invariants (assumptions).
475//!
476//! If you scan the str0m code you find a few `unwrap()` (or `expect()`). These
477//! will (should) always be accompanied by a code comment that explains why the
478//! unwrap is okay. This is an internal invariant, a state assumption that
479//! str0m is responsible for maintaining.
480//!
481//! We do not believe it's correct to change every `unwrap()`/`expect()` into
482//! `unwrap_or_else()`, `if let Some(x) = x { ... }` etc, because doing so
483//! brushes an actual problem (an incorrect assumption) under the carpet. Trying
484//! to hobble along with an incorrect state would at best result in broken
485//! behavior, at worst a security risk!
486//!
487//! Panics are our friends: *panic means bug*
488//!
489//! And also: str0m should *never* panic on any user input. If you encounter a panic,
490//! please report it!
491//!
492//! ### Catching panics
493//!
494//! Panics should be incredibly rare, or we have a serious problem as a project. For an SFU,
495//! it might not be ideal if str0m encounters a bug and brings the entire server down with it.
496//!
497//! For those who want an extra level of safety, we recommend looking at [`catch_unwind`][catch]
498//! to safely discard a faulty `Rtc` instance. Since `Rtc` has no internal threads, locks or async
499//! tasks, discarding the instance never risk poisoning locks or other issues that can happen
500//! when catching a panic.
501//!
502//! ## FAQ
503//!
504//! ### Features
505//!
506//! Below is a brief comparison of features between libWebRTC and str0m to help you determine
507//! if str0m is suitable for your project.
508//!
509//! | Feature | str0m | libWebRTC |
510//! | ------------------------ | ------------------ | ------------------ |
511//! | Peer Connection API | :x: | :white_check_mark: |
512//! | SDP | :white_check_mark: | :white_check_mark: |
513//! | ICE | :white_check_mark: | :white_check_mark: |
514//! | Data Channels | :white_check_mark: | :white_check_mark: |
515//! | Send/Recv Reports | :white_check_mark: | :white_check_mark: |
516//! | Transport Wide CC | :white_check_mark: | :white_check_mark: |
517//! | Bandwidth Estimation | :white_check_mark: | :white_check_mark: |
518//! | Simulcast | :white_check_mark: | :white_check_mark: |
519//! | NACK | :white_check_mark: | :white_check_mark: |
520//! | Packetize | :white_check_mark: | :white_check_mark: |
521//! | Fixed Depacketize Buffer | :white_check_mark: | :white_check_mark: |
522//! | Adaptive Jitter Buffer | :x: | :white_check_mark: |
523//! | Video/audio capture | :x: | :white_check_mark: |
524//! | Video/audio encode | :x: | :white_check_mark: |
525//! | Video/audio decode | :x: | :white_check_mark: |
526//! | Audio render | :x: | :white_check_mark: |
527//! | Turn | :x: | :white_check_mark: |
528//! | Network interface enum | :x: | :white_check_mark: |
529//!
530//! ### Platform Support
531//!
532//! Platforms str0m is compiled and tested on:
533//!
534//! | Platform | Compiled | Tested |
535//! | -------------------------- | ----------------- | ----------------- |
536//! | `x86_64-pc-windows-msvc` | :white_check_mark:| :white_check_mark:|
537//! | `x86_64-unknown-linux-gnu` | :white_check_mark:| :white_check_mark:|
538//! | `x86_64-apple-darwin` | :white_check_mark:| :white_check_mark:|
539//!
540//! If your platform isn't listed but is supported by Rust, we'd love for you to give str0m a try and
541//! share your experience. We greatly appreciate your feedback!
542//!
543//! ### Does str0m support IPv4, IPv6, UDP and TCP?
544//!
545//! Certainly! str0m fully support IPv4, IPv6, UDP and TCP protocols.
546//!
547//! ### Can I utilize str0m with any Rust async runtime?
548//!
549//! Absolutely! str0m is fully sync, ensuring that it integrates seamlessly with any Rust async
550//! runtime you opt for.
551//!
552//! ### Can I create a client with str0m?
553//!
554//! Of course! You have the freedom to create a client with str0m. However, please note that some
555//! common client features like media encoding, decoding, and capture are not included in str0m. But
556//! don't let that stop you from building amazing applications!
557//!
558//! ### Can I use str0m in a media server?
559//!
560//! Yes! str0m excels as a server component with support for both RTP API and Sample API. You can
561//! easily build that recording server or SFU you dreamt of in Rust!
562//!
563//! ### Can I deploy the chat example into production?
564//!
565//! While the chat example showcases how to use str0m's API, it's not intended for production use or
566//! heavy load. Writing a full-featured SFU or MCU (Multipoint Control Unit) is a significant
567//! undertaking, involving various design decisions based on production requirements.
568//!
569//! ### Discovered a bug? Here's how to share it with us
570//!
571//! We'd love to hear about it! Please submit an issue and consider joining our Zulip community
572//! to discuss further. For a seamless reporting experience, refer to this exemplary
573//! bug report: <https://github.com/algesten/str0m/issues/382>. We appreciate your contribution
574//! to making str0m better!
575//!
576//! ### I am allergic to SDP can you help me?
577//!
578//! Yes use the direct API!
579//!
580//! [sansio]: https://sans-io.readthedocs.io
581//! [quinn]: https://github.com/quinn-rs/quinn
582//! [pion]: https://github.com/pion/webrtc
583//! [webrtc-rs]: https://github.com/webrtc-rs/webrtc
584//! [zulip]: https://str0m.zulipchat.com/join/hsiuva2zx47ujrwgmucjez5o/
585//! [zulip-anon]: https://str0m.zulipchat.com
586//! [ice]: https://www.rfc-editor.org/rfc/rfc8445
587//! [lookback]: https://www.lookback.com
588//! [x-post]: https://github.com/algesten/str0m/blob/main/examples/http-post.rs
589//! [x-chat]: https://github.com/algesten/str0m/blob/main/examples/chat.rs
590//! [intg]: https://github.com/algesten/str0m/blob/main/tests/unidirectional.rs#L12
591//! [ff]: https://en.wikipedia.org/wiki/Fail-fast
592//! [catch]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
593//! [evmed]: https://docs.rs/str0m/*/str0m/enum.Event.html#variant.MediaData
594//! [writer]: https://docs.rs/str0m/*/str0m/media/struct.Writer.html#method.write
595//! [reqkey]: https://docs.rs/str0m/*/str0m/media/struct.Writer.html#method.request_keyframe
596//! [rtppak]: https://docs.rs/str0m/*/str0m/enum.Event.html#variant.RtpPacket
597//! [wrtrtp]: https://docs.rs/str0m/*/str0m/rtp/struct.StreamTx.html#method.write_rtp
598//! [reqkey2]: https://docs.rs/str0m/*/str0m/rtp/struct.StreamRx.html#method.request_keyframe
599//! [bitwhip]: https://github.com/bitwhip/bitwhip
600
601#![forbid(unsafe_code)]
602#![allow(clippy::new_without_default)]
603#![allow(clippy::bool_to_int_with_if)]
604#![allow(clippy::assertions_on_constants)]
605#![allow(clippy::manual_range_contains)]
606#![allow(clippy::get_first)]
607#![allow(clippy::needless_lifetimes)]
608#![allow(clippy::precedence)]
609#![allow(clippy::doc_overindented_list_items)]
610#![allow(clippy::uninlined_format_args)]
611#![allow(mismatched_lifetime_syntaxes)]
612#![deny(missing_docs)]
613
614#[macro_use]
615extern crate tracing;
616
617use bwe::{Bwe, BweKind};
618use change::{DirectApi, SdpApi};
619use rtp::RawPacket;
620use std::fmt;
621use std::net::SocketAddr;
622use std::time::{Duration, Instant};
623use streams::RtpPacket;
624use streams::StreamPaused;
625use util::{InstantExt, Pii};
626
627mod crypto;
628use crypto::CryptoProvider;
629use crypto::Fingerprint;
630
631mod dtls;
632use dtls::{Dtls, DtlsCert, DtlsCertOptions, DtlsEvent};
633
634#[path = "ice/mod.rs"]
635mod ice_;
636use ice_::IceAgent;
637use ice_::IceAgentEvent;
638pub use ice_::{Candidate, CandidateKind, IceConnectionState, IceCreds};
639
640/// Additional configuration.
641pub mod config {
642 pub use super::crypto::{CryptoProvider, DtlsCert, DtlsCertOptions, DtlsPKeyType, Fingerprint};
643}
644
645/// Low level ICE access.
646// The ICE API is not necessary to interact with directly for "regular"
647// use of str0m. This is exported for other libraries that want to
648// reuse str0m's ICE implementation. In the future we might turn this
649// into a separate crate.
650#[doc(hidden)]
651pub mod ice {
652 pub use crate::ice_::IceCreds;
653 pub use crate::ice_::{default_local_preference, LocalPreference};
654 pub use crate::ice_::{IceAgent, IceAgentEvent};
655 pub use crate::io::{StunMessage, StunMessageBuilder, StunPacket, TransId};
656}
657
658mod io;
659use io::DatagramRecvInner;
660
661mod packet;
662
663#[path = "rtp/mod.rs"]
664mod rtp_;
665use rtp_::{Bitrate, DataSize};
666use rtp_::{Extension, ExtensionMap};
667
668/// Low level RTP access.
669pub mod rtp {
670 /// Feedback for RTP.
671 pub mod rtcp {
672 pub use crate::rtp_::{Descriptions, ExtendedReport, Fir, Goodbye, Nack, Pli};
673 pub use crate::rtp_::{Dlrr, NackEntry, ReceptionReport, ReportBlock};
674 pub use crate::rtp_::{FirEntry, ReceiverReport, SenderInfo, SenderReport, Twcc};
675 pub use crate::rtp_::{ReportList, Rrtr, Rtcp, Sdes, SdesType};
676 }
677 use self::rtcp::Rtcp;
678
679 /// Video Layers Allocation RTP Header Extension
680 pub mod vla;
681 pub use crate::rtp_::{Extension, ExtensionMap, ExtensionSerializer};
682 pub use crate::rtp_::{ExtensionValues, UserExtensionValues};
683
684 pub use crate::rtp_::{RtpHeader, SeqNo, Ssrc, VideoOrientation};
685 pub use crate::streams::{RtpPacket, StreamPaused, StreamRx, StreamTx};
686
687 /// Debug output of the unencrypted RTP and RTCP packets.
688 ///
689 /// Enable using [`RtcConfig::enable_raw_packets()`][crate::RtcConfig::enable_raw_packets].
690 /// This clones data, and is therefore expensive.
691 /// Should not be enabled outside of tests and troubleshooting.
692 #[derive(Debug)]
693 pub enum RawPacket {
694 /// Sent RTCP.
695 RtcpTx(Rtcp),
696 /// Incoming RTCP.
697 RtcpRx(Rtcp),
698 /// Sent RTP.
699 RtpTx(RtpHeader, Vec<u8>),
700 /// Incoming RTP.
701 RtpRx(RtpHeader, Vec<u8>),
702 }
703}
704
705pub mod bwe;
706
707mod sctp;
708use sctp::{RtcSctp, SctpEvent};
709
710mod sdp;
711
712pub mod format;
713use format::CodecConfig;
714
715pub mod channel;
716use channel::{Channel, ChannelData, ChannelHandler, ChannelId};
717
718pub mod media;
719use media::{Direction, Media, Mid, Pt, Rid, Writer};
720use media::{KeyframeRequest, KeyframeRequestKind};
721use media::{MediaAdded, MediaChanged, MediaData};
722
723pub mod change;
724
725mod util;
726use util::{already_happened, not_happening, Soonest};
727
728mod session;
729use session::Session;
730
731pub mod stats;
732
733use stats::{CandidatePairStats, CandidateStats, MediaEgressStats, MediaIngressStats};
734use stats::{PeerStats, Stats, StatsEvent, StatsSnapshot};
735
736mod streams;
737
738pub mod error;
739
740/// Network related types to get socket data in/out of [`Rtc`].
741pub mod net {
742 pub use crate::io::{DatagramRecv, DatagramSend, Protocol, Receive, Transmit};
743}
744
745const VERSION: &str = env!("CARGO_PKG_VERSION");
746
747pub use error::RtcError;
748
749/// Instance that does WebRTC. Main struct of the entire library.
750///
751/// ## Usage
752///
753/// ```no_run
754/// # use str0m::{Rtc, Output, Input};
755/// let mut rtc = Rtc::new();
756///
757/// loop {
758/// let timeout = match rtc.poll_output().unwrap() {
759/// Output::Timeout(v) => v,
760/// Output::Transmit(t) => {
761/// // TODO: Send data to remote peer.
762/// continue; // poll again
763/// }
764/// Output::Event(e) => {
765/// // TODO: Handle event.
766/// continue; // poll again
767/// }
768/// };
769///
770/// // TODO: Wait for one of two events, reaching `timeout`
771/// // or receiving network input. Both are encapsulated
772/// // in the Input enum.
773/// let input: Input = todo!();
774///
775/// rtc.handle_input(input).unwrap();
776/// }
777/// ```
778pub struct Rtc {
779 alive: bool,
780 ice: IceAgent,
781 dtls: Dtls,
782 sctp: RtcSctp,
783 chan: ChannelHandler,
784 stats: Option<Stats>,
785 session: Session,
786 remote_fingerprint: Option<Fingerprint>,
787 remote_addrs: Vec<SocketAddr>,
788 send_addr: Option<SendAddr>,
789 need_init_time: bool,
790 last_now: Instant,
791 peer_bytes_rx: u64,
792 peer_bytes_tx: u64,
793 change_counter: usize,
794 last_timeout_reason: Reason,
795 crypto_provider: CryptoProvider,
796 fingerprint_verification: bool,
797}
798
799struct SendAddr {
800 proto: net::Protocol,
801 source: SocketAddr,
802 destination: SocketAddr,
803}
804
805/// Events produced by [`Rtc::poll_output()`].
806#[derive(Debug)]
807#[non_exhaustive]
808#[rustfmt::skip]
809pub enum Event {
810 // =================== ICE related events ===================
811
812 /// Emitted when we got ICE connection and established DTLS.
813 Connected,
814
815 /// ICE connection state changes tells us whether the [`Rtc`] instance is
816 /// connected to the peer or not.
817 IceConnectionStateChange(IceConnectionState),
818
819 // =================== Media related events ==================
820
821 /// Upon detecting the remote side adding new media to the session.
822 ///
823 /// For locally added media, this event never fires. Thus it can be thought of as an
824 /// "SDP only" event. If the direct API is used on both sides, the declaration is local \
825 /// to both sides and the event never fires.
826 ///
827 /// The [`Media`] instance is available via [`Rtc::media()`].
828 MediaAdded(MediaAdded),
829
830 /// Incoming media data sent by the remote peer.
831 MediaData(MediaData),
832
833 /// Changes to the media may be emitted.
834 ///
835 ///. Currently only covers a change of direction.
836 MediaChanged(MediaChanged),
837
838 // =================== Data channel related events ===================
839
840 /// A data channel has opened.
841 ///
842 /// The string is the channel label which is set by the opening peer and can
843 /// be used to identify the purpose of the channel when there are more than one.
844 ///
845 /// The negotiation is to set up an SCTP association via DTLS. Subsequent data
846 /// channels reuse the same association.
847 ///
848 /// Upon this event, the [`Channel`] can be obtained via [`Rtc::channel()`].
849 ///
850 /// For [`SdpApi`]: The first ever data channel results in an SDP
851 /// negotiation, and this events comes at the end of that.
852 ChannelOpen(ChannelId, String),
853
854 /// Incoming data channel data from the remote peer.
855 ChannelData(ChannelData),
856
857 /// A data channel has been closed.
858 ChannelClose(ChannelId),
859
860 /// A data channel's buffered amount has dropped below the configured threshold.
861 ChannelBufferedAmountLow(ChannelId),
862
863 // =================== Statistics and BWE related events ===================
864
865 /// Statistics event for the Rtc instance
866 ///
867 /// Includes both media traffic (rtp payload) as well as all traffic
868 PeerStats(PeerStats),
869
870 /// Aggregated statistics for each media (mid, rid) in the ingress direction
871 MediaIngressStats(MediaIngressStats),
872
873 /// Aggregated statistics for each media (mid, rid) in the egress direction
874 MediaEgressStats(MediaEgressStats),
875
876 /// A new estimate from the bandwidth estimation subsystem.
877 EgressBitrateEstimate(BweKind),
878
879 // =================== RTP related events ===================
880
881 /// Incoming keyframe request for media that we are sending to the remote peer.
882 ///
883 /// The request is either PLI (Picture Loss Indication) or FIR (Full Intra Request).
884 KeyframeRequest(KeyframeRequest),
885
886 /// Whether an incoming encoded stream is paused.
887 ///
888 /// This means the stream has not received any data for some time (default 1.5 seconds).
889 StreamPaused(StreamPaused),
890
891 /// Incoming RTP data.
892 RtpPacket(RtpPacket),
893
894 /// Debug output of incoming and outgoing RTCP/RTP packets.
895 ///
896 /// Enable using [`RtcConfig::enable_raw_packets()`].
897 /// This clones data, and is therefore expensive.
898 /// Should not be enabled outside of tests and troubleshooting.
899 RawPacket(Box<RawPacket>),
900}
901
902impl Event {
903 /// Reference to the [`RawPacket`] if this is indeed an `Event::RawPacket`.
904 pub fn as_raw_packet(&self) -> Option<&RawPacket> {
905 if let Self::RawPacket(boxed) = &self {
906 Some(&**boxed)
907 } else {
908 None
909 }
910 }
911}
912
913/// Input as expected by [`Rtc::handle_input()`]. Either network data or a timeout.
914#[derive(Debug)]
915#[allow(clippy::large_enum_variant)] // We purposely don't want to allocate.
916pub enum Input<'a> {
917 /// A timeout without any network input.
918 Timeout(Instant),
919 /// Network input.
920 Receive(Instant, net::Receive<'a>),
921}
922
923/// Output produced by [`Rtc::poll_output()`]
924#[allow(clippy::large_enum_variant)]
925#[derive(Debug)]
926pub enum Output {
927 /// When the [`Rtc`] instance expects an [`Input::Timeout`].
928 Timeout(Instant),
929
930 /// Network data that is to be sent.
931 Transmit(net::Transmit),
932
933 /// Some event such as media data arriving from the remote peer or connection events.
934 Event(Event),
935}
936
937/// The reason for the next [`Output::Timeout`].
938#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
939pub enum Reason {
940 /// No timeout scheduled.
941 ///
942 /// The timeout value is in the distant future.
943 NotHappening,
944
945 /// The DTLS subsystem.
946 ///
947 /// Only relevant during handshaking.
948 DTLS,
949
950 /// The ICE agent.
951 ///
952 /// Includes checking candidate pairs and various cleanups.
953 Ice,
954
955 /// The SCTP subsystem.
956 ///
957 /// Things like handling retransmissions and keep-alive checks.
958 Sctp,
959
960 /// Data channels.
961 ///
962 /// Scheduled when we need to open allocations using SCTP.
963 Channel,
964
965 /// Stats gathering (if enabled).
966 ///
967 /// Periodic gathering of statistics.
968 Stats,
969
970 /// Regular RTP feedback.
971 ///
972 /// Receiver reports (RR) and sender reports (SR).
973 Feedback,
974
975 /// Sending of RTP NACK.
976 ///
977 /// When missing packets are discovered, a NACK is scheduled.
978 Nack,
979
980 /// Reporting of TWCC (if enabled).
981 ///
982 /// All incoming RTP packets are reported using TWCC. Enabled via SDP if both
983 /// sides support it.
984 Twcc,
985
986 /// RTP streams not receiving data goes into a paused state.
987 ///
988 /// Whenever an RTP receive stream receives data, a new timeout is scheduled.
989 PauseCheck,
990
991 /// Preprocessing of RTP packets to be sent.
992 ///
993 /// Housekeeping task in RTP send streams.
994 SendStream,
995
996 /// Packetizing of media into RTP data (if used).
997 ///
998 /// Written media data needs packetizing. This is not used in RTP mode.
999 Packetize,
1000
1001 /// Paced sending of RTP packets (if BWE is enabled).
1002 ///
1003 /// The pacer ensures bigger RTP chunks, like keyframes, are not sent as a burst,
1004 /// but sent smoothly.
1005 Pacing,
1006
1007 /// Bandwidth estimation update (if enabled).
1008 ///
1009 /// Calculations regarding sender bandwidth using incoming TWCC.
1010 Bwe,
1011}
1012
1013impl Default for Reason {
1014 fn default() -> Self {
1015 Self::NotHappening
1016 }
1017}
1018
1019impl Rtc {
1020 /// Creates a new instance with default settings.
1021 ///
1022 /// To configure the instance, use [`RtcConfig`].
1023 ///
1024 /// ```
1025 /// # #[cfg(feature = "openssl")] {
1026 /// use str0m::Rtc;
1027 ///
1028 /// let rtc = Rtc::new();
1029 /// # }
1030 /// ```
1031 pub fn new() -> Self {
1032 let config = RtcConfig::default();
1033 Self::new_from_config(config)
1034 }
1035
1036 /// Creates a config builder that configures an [`Rtc`] instance.
1037 ///
1038 /// ```
1039 /// # #[cfg(feature = "openssl")] {
1040 /// # use str0m::Rtc;
1041 /// let rtc = Rtc::builder()
1042 /// .set_ice_lite(true)
1043 /// .build();
1044 /// # }
1045 /// ```
1046 pub fn builder() -> RtcConfig {
1047 RtcConfig::new()
1048 }
1049
1050 pub(crate) fn new_from_config(config: RtcConfig) -> Self {
1051 let session = Session::new(&config);
1052
1053 let local_creds = config.local_ice_credentials.unwrap_or_else(IceCreds::new);
1054 let mut ice = IceAgent::with_local_credentials(local_creds);
1055 if config.ice_lite {
1056 ice.set_ice_lite(config.ice_lite);
1057 }
1058
1059 if let Some(initial_stun_rto) = config.initial_stun_rto {
1060 ice.set_initial_stun_rto(initial_stun_rto);
1061 }
1062
1063 if let Some(max_stun_rto) = config.max_stun_rto {
1064 ice.set_max_stun_rto(max_stun_rto);
1065 }
1066
1067 if let Some(max_stun_retransmits) = config.max_stun_retransmits {
1068 ice.set_max_stun_retransmits(max_stun_retransmits);
1069 }
1070
1071 let dtls_cert = match config.dtls_cert_config {
1072 DtlsCertConfig::Options(options) => DtlsCert::new(config.crypto_provider, options),
1073 DtlsCertConfig::PregeneratedCert(cert) => cert,
1074 };
1075
1076 let crypto_provider = dtls_cert.crypto_provider();
1077
1078 Rtc {
1079 alive: true,
1080 ice,
1081 dtls: Dtls::new(dtls_cert).expect("DTLS to init without problem"),
1082 session,
1083 sctp: RtcSctp::new(),
1084 chan: ChannelHandler::default(),
1085 stats: config.stats_interval.map(Stats::new),
1086 remote_fingerprint: None,
1087 remote_addrs: vec![],
1088 send_addr: None,
1089 need_init_time: true,
1090 last_now: already_happened(),
1091 peer_bytes_rx: 0,
1092 peer_bytes_tx: 0,
1093 change_counter: 0,
1094 last_timeout_reason: Reason::NotHappening,
1095 crypto_provider,
1096 fingerprint_verification: config.fingerprint_verification,
1097 }
1098 }
1099
1100 /// Tests if this instance is still working.
1101 ///
1102 /// Certain events will straight away disconnect the `Rtc` instance, such as
1103 /// the DTLS fingerprint from the setup not matching that of the TLS negotiation
1104 /// (since that would potentially indicate a MITM attack!).
1105 ///
1106 /// The instance can be manually disconnected using [`Rtc::disconnect()`].
1107 ///
1108 /// ```
1109 /// # #[cfg(feature = "openssl")] {
1110 /// # use str0m::Rtc;
1111 /// let mut rtc = Rtc::new();
1112 ///
1113 /// assert!(rtc.is_alive());
1114 ///
1115 /// rtc.disconnect();
1116 /// assert!(!rtc.is_alive());
1117 /// # }
1118 /// ```
1119 pub fn is_alive(&self) -> bool {
1120 self.alive
1121 }
1122
1123 /// Force disconnects the instance making [`Rtc::is_alive()`] return `false`.
1124 ///
1125 /// This makes [`Rtc::poll_output`] and [`Rtc::handle_input`] go inert and not
1126 /// produce anymore network output or events.
1127 ///
1128 /// ```
1129 /// # #[cfg(feature = "openssl")] {
1130 /// # use str0m::Rtc;
1131 /// let mut rtc = Rtc::new();
1132 ///
1133 /// rtc.disconnect();
1134 /// assert!(!rtc.is_alive());
1135 /// # }
1136 /// ```
1137 pub fn disconnect(&mut self) {
1138 if self.alive {
1139 debug!("Set alive=false");
1140 self.alive = false;
1141 }
1142 }
1143
1144 /// Add a local ICE candidate. Local candidates are socket addresses the `Rtc` instance
1145 /// use for communicating with the peer.
1146 ///
1147 /// If the candidate is accepted by the `Rtc` instance, it will return `Some` with a reference
1148 /// to it. You should then signal this candidate to the remote peer.
1149 ///
1150 /// This library has no built-in discovery of local network addresses on the host
1151 /// or NATed addresses via a STUN server or TURN server. The user of the library
1152 /// is expected to add new local candidates as they are discovered.
1153 ///
1154 /// In WebRTC lingo, the `Rtc` instance is permanently in a mode of [Trickle Ice][1]. It's
1155 /// however advisable to add at least one local candidate before starting the instance.
1156 ///
1157 /// ```
1158 /// # #[cfg(feature = "openssl")] {
1159 /// # use str0m::{Rtc, Candidate};
1160 /// let mut rtc = Rtc::new();
1161 ///
1162 /// let a = "127.0.0.1:5000".parse().unwrap();
1163 /// let c = Candidate::host(a, "udp").unwrap();
1164 ///
1165 /// rtc.add_local_candidate(c);
1166 /// # }
1167 /// ```
1168 ///
1169 /// [1]: https://www.rfc-editor.org/rfc/rfc8838.txt
1170 pub fn add_local_candidate(&mut self, c: Candidate) -> Option<&Candidate> {
1171 self.ice.add_local_candidate(c)
1172 }
1173
1174 /// Add a remote ICE candidate. Remote candidates are addresses of the peer.
1175 ///
1176 /// For [`SdpApi`]: Remote candidates are typically added via
1177 /// receiving a remote [`SdpOffer`][change::SdpOffer] or [`SdpAnswer`][change::SdpAnswer].
1178 ///
1179 /// However for the case of [Trickle Ice][1], this is the way to add remote candidates
1180 /// that are "trickled" from the other side.
1181 ///
1182 /// ```
1183 /// # #[cfg(feature = "openssl")] {
1184 /// # use str0m::{Rtc, Candidate};
1185 /// let mut rtc = Rtc::new();
1186 ///
1187 /// let a = "1.2.3.4:5000".parse().unwrap();
1188 /// let c = Candidate::host(a, "udp").unwrap();
1189 ///
1190 /// rtc.add_remote_candidate(c);
1191 /// }
1192 /// ```
1193 ///
1194 /// [1]: https://www.rfc-editor.org/rfc/rfc8838.txt
1195 pub fn add_remote_candidate(&mut self, c: Candidate) {
1196 self.ice.add_remote_candidate(c);
1197 }
1198
1199 /// Checks if we are connected.
1200 ///
1201 /// This tests if we have ICE connection, DTLS and the SRTP crypto derived contexts are up.
1202 pub fn is_connected(&self) -> bool {
1203 self.ice.state().is_connected() && self.dtls.is_connected() && self.session.is_connected()
1204 }
1205
1206 /// Make changes to the Rtc session via SDP.
1207 ///
1208 /// ```no_run
1209 /// # use str0m::Rtc;
1210 /// # use str0m::media::{MediaKind, Direction};
1211 /// # use str0m::change::SdpAnswer;
1212 /// let mut rtc = Rtc::new();
1213 ///
1214 /// let mut changes = rtc.sdp_api();
1215 /// let mid_audio = changes.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None);
1216 /// let mid_video = changes.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
1217 ///
1218 /// let (offer, pending) = changes.apply().unwrap();
1219 /// let json = serde_json::to_vec(&offer).unwrap();
1220 ///
1221 /// // Send json OFFER to remote peer. Receive an answer back.
1222 /// let answer: SdpAnswer = todo!();
1223 ///
1224 /// rtc.sdp_api().accept_answer(pending, answer).unwrap();
1225 /// ```
1226 pub fn sdp_api(&mut self) -> SdpApi {
1227 SdpApi::new(self)
1228 }
1229
1230 /// Makes direct changes to the Rtc session.
1231 ///
1232 /// This is a low level API. For "normal" use via SDP, see [`Rtc::sdp_api()`].
1233 pub fn direct_api(&mut self) -> DirectApi {
1234 DirectApi::new(self)
1235 }
1236
1237 /// Send outgoing media data (samples) or request keyframes.
1238 ///
1239 /// Returns `None` if the direction isn't sending (`sendrecv` or `sendonly`).
1240 ///
1241 /// ```no_run
1242 /// # use str0m::Rtc;
1243 /// # use str0m::media::{MediaData, Mid};
1244 /// # use str0m::format::PayloadParams;
1245 /// let mut rtc = Rtc::new();
1246 ///
1247 /// // add candidates, do SDP negotiation
1248 /// let mid: Mid = todo!(); // obtain mid from Event::MediaAdded.
1249 ///
1250 /// // Writer for this mid.
1251 /// let writer = rtc.writer(mid).unwrap();
1252 ///
1253 /// // Get incoming media data from another peer
1254 /// let data: MediaData = todo!();
1255 ///
1256 /// // Match incoming PT to an outgoing PT.
1257 /// let pt = writer.match_params(data.params).unwrap();
1258 ///
1259 /// writer.write(pt, data.network_time, data.time, data.data).unwrap();
1260 /// ```
1261 ///
1262 /// This is a sample level API: For RTP level see [`DirectApi::stream_tx()`]
1263 /// and [`DirectApi::stream_rx()`].
1264 ///
1265 pub fn writer(&mut self, mid: Mid) -> Option<Writer> {
1266 if self.session.rtp_mode {
1267 panic!("In rtp_mode use direct_api().stream_tx().write_rtp()");
1268 }
1269
1270 // This does not catch potential RIDs required to send simulcast, but
1271 // it's a good start. An error might arise later on RID mismatch.
1272 self.session.media_by_mid_mut(mid)?;
1273
1274 Some(Writer::new(&mut self.session, mid))
1275 }
1276
1277 /// Currently configured media.
1278 ///
1279 /// Read only access. Changes are made via [`Rtc::sdp_api()`] or [`Rtc::direct_api()`].
1280 pub fn media(&self, mid: Mid) -> Option<&Media> {
1281 self.session.media_by_mid(mid)
1282 }
1283
1284 fn init_dtls(&mut self, active: bool) -> Result<(), RtcError> {
1285 if self.dtls.is_inited() {
1286 return Ok(());
1287 }
1288
1289 debug!("DTLS setup is: {:?}", active);
1290 self.dtls.set_active(active);
1291
1292 if active {
1293 self.dtls.handle_handshake()?;
1294 }
1295
1296 Ok(())
1297 }
1298
1299 fn init_sctp(&mut self, client: bool) {
1300 // If we got an m=application line, ensure we have negotiated the
1301 // SCTP association with the other side.
1302 if self.sctp.is_inited() {
1303 return;
1304 }
1305
1306 self.sctp.init(client, self.last_now);
1307 }
1308
1309 /// Creates a new Mid that is not in the session already.
1310 pub(crate) fn new_mid(&self) -> Mid {
1311 loop {
1312 let mid = Mid::new();
1313 if !self.session.has_mid(mid) {
1314 break mid;
1315 }
1316 }
1317 }
1318
1319 /// Poll the `Rtc` instance for output. Output can be three things, something to _Transmit_
1320 /// via a UDP socket (maybe via a TURN server). An _Event_, such as receiving media data,
1321 /// or a _Timeout_.
1322 ///
1323 /// The user of the library is expected to continuously call this function and deal with
1324 /// the output until it encounters an [`Output::Timeout`] at which point no further output
1325 /// is produced (if polled again, it will result in just another timeout).
1326 ///
1327 /// After exhausting the `poll_output`, the function will only produce more output again
1328 /// when one of two things happen:
1329 ///
1330 /// 1. The polled timeout is reached.
1331 /// 2. New network input.
1332 ///
1333 /// See [`Rtc`] instance documentation for how this is expected to be used in a loop.
1334 pub fn poll_output(&mut self) -> Result<Output, RtcError> {
1335 let o = self.do_poll_output()?;
1336
1337 match &o {
1338 Output::Event(e) => match e {
1339 Event::ChannelData(_) | Event::MediaData(_) | Event::RtpPacket(_) => {
1340 trace!("{:?}", e)
1341 }
1342 _ => debug!("{:?}", e),
1343 },
1344 Output::Transmit(t) => {
1345 self.peer_bytes_tx += t.contents.len() as u64;
1346 trace!("OUT {:?}", t)
1347 }
1348 Output::Timeout(_t) => {}
1349 }
1350
1351 Ok(o)
1352 }
1353
1354 fn do_poll_output(&mut self) -> Result<Output, RtcError> {
1355 if !self.alive {
1356 self.last_timeout_reason = Reason::NotHappening;
1357 return Ok(Output::Timeout(not_happening()));
1358 }
1359
1360 while let Some(e) = self.ice.poll_event() {
1361 match e {
1362 IceAgentEvent::IceRestart(_) => {
1363 //
1364 }
1365 IceAgentEvent::IceConnectionStateChange(v) => {
1366 return Ok(Output::Event(Event::IceConnectionStateChange(v)))
1367 }
1368 IceAgentEvent::DiscoveredRecv { proto, source } => {
1369 debug!("ICE remote address: {:?}/{:?}", Pii(source), proto);
1370 self.remote_addrs.push(source);
1371 while self.remote_addrs.len() > 20 {
1372 self.remote_addrs.remove(0);
1373 }
1374 }
1375 IceAgentEvent::NominatedSend {
1376 proto,
1377 source,
1378 destination,
1379 } => {
1380 debug!(
1381 "ICE nominated send from: {:?} to: {:?} with protocol {:?}",
1382 Pii(source),
1383 Pii(destination),
1384 proto,
1385 );
1386 self.send_addr = Some(SendAddr {
1387 proto,
1388 source,
1389 destination,
1390 });
1391 }
1392 }
1393 }
1394
1395 let mut dtls_connected = false;
1396
1397 while let Some(e) = self.dtls.poll_event() {
1398 match e {
1399 DtlsEvent::Connected => {
1400 debug!("DTLS connected");
1401 dtls_connected = true;
1402 }
1403 DtlsEvent::SrtpKeyingMaterial(mat, srtp_profile) => {
1404 debug!(
1405 "DTLS set SRTP keying material and profile: {}",
1406 srtp_profile
1407 );
1408 let active = self.dtls.is_active().expect("DTLS must be inited by now");
1409 let srtp_crypto = self.crypto_provider.srtp_crypto();
1410 self.session
1411 .set_keying_material(mat, &srtp_crypto, srtp_profile, active);
1412 }
1413 DtlsEvent::RemoteFingerprint(v1) => {
1414 debug!("DTLS verify remote fingerprint");
1415 if let Some(v2) = &self.remote_fingerprint {
1416 if !self.fingerprint_verification {
1417 debug!("DTLS fingerprint verification disabled");
1418 } else if v1 != *v2 {
1419 self.disconnect();
1420 return Err(RtcError::RemoteSdp("remote fingerprint no match".into()));
1421 }
1422 } else {
1423 self.disconnect();
1424 return Err(RtcError::RemoteSdp("no a=fingerprint before dtls".into()));
1425 }
1426 }
1427 DtlsEvent::Data(v) => {
1428 self.sctp.handle_input(self.last_now, &v);
1429 }
1430 }
1431 }
1432
1433 if dtls_connected {
1434 return Ok(Output::Event(Event::Connected));
1435 }
1436
1437 while let Some(e) = self.sctp.poll() {
1438 match e {
1439 SctpEvent::Transmit { mut packets } => {
1440 if let Some(v) = packets.front() {
1441 if let Err(e) = self.dtls.handle_input(v) {
1442 if e.is_would_block() {
1443 self.sctp.push_back_transmit(packets);
1444 break;
1445 } else {
1446 return Err(e.into());
1447 }
1448 }
1449 packets.pop_front();
1450 // If there are still packets, they are sent on next
1451 // poll_output()
1452 if !packets.is_empty() {
1453 self.sctp.push_back_transmit(packets);
1454 }
1455 break;
1456 }
1457 }
1458 SctpEvent::Open { id, label } => {
1459 self.chan.ensure_channel_id_for(id);
1460 let id = self.chan.channel_id_by_stream_id(id).unwrap();
1461 return Ok(Output::Event(Event::ChannelOpen(id, label)));
1462 }
1463 SctpEvent::Close { id } => {
1464 let Some(id) = self.chan.channel_id_by_stream_id(id) else {
1465 warn!("Drop ChannelClose event for id: {:?}", id);
1466 continue;
1467 };
1468 self.chan.remove_channel(id);
1469 return Ok(Output::Event(Event::ChannelClose(id)));
1470 }
1471 SctpEvent::Data { id, binary, data } => {
1472 let Some(id) = self.chan.channel_id_by_stream_id(id) else {
1473 warn!("Drop ChannelData event for id: {:?}", id);
1474 continue;
1475 };
1476 let cd = ChannelData { id, binary, data };
1477 return Ok(Output::Event(Event::ChannelData(cd)));
1478 }
1479 SctpEvent::BufferedAmountLow { id } => {
1480 let Some(id) = self.chan.channel_id_by_stream_id(id) else {
1481 warn!("Drop BufferedAmountLow for id: {:?}", id);
1482 continue;
1483 };
1484 return Ok(Output::Event(Event::ChannelBufferedAmountLow(id)));
1485 }
1486 }
1487 }
1488
1489 if let Some(ev) = self.session.poll_event() {
1490 return Ok(Output::Event(ev));
1491 }
1492
1493 // Some polling needs to bubble up errors.
1494 if let Some(ev) = self.session.poll_event_fallible()? {
1495 return Ok(Output::Event(ev));
1496 }
1497
1498 if let Some(e) = self.stats.as_mut().and_then(|s| s.poll_output()) {
1499 return Ok(match e {
1500 StatsEvent::Peer(s) => Output::Event(Event::PeerStats(s)),
1501 StatsEvent::MediaIngress(s) => Output::Event(Event::MediaIngressStats(s)),
1502 StatsEvent::MediaEgress(s) => Output::Event(Event::MediaEgressStats(s)),
1503 });
1504 }
1505
1506 if let Some(v) = self.ice.poll_transmit() {
1507 return Ok(Output::Transmit(v));
1508 }
1509
1510 if let Some(send) = &self.send_addr {
1511 // These can only be sent after we got an ICE connection.
1512 let datagram = None
1513 .or_else(|| self.dtls.poll_datagram())
1514 .or_else(|| self.session.poll_datagram(self.last_now));
1515
1516 if let Some(contents) = datagram {
1517 let t = net::Transmit {
1518 proto: send.proto,
1519 source: send.source,
1520 destination: send.destination,
1521 contents,
1522 };
1523 return Ok(Output::Transmit(t));
1524 }
1525 } else {
1526 // Don't allow accumulated feedback to build up indefinitely
1527 self.session.clear_feedback();
1528 }
1529
1530 let stats = self.stats.as_mut();
1531
1532 let time_and_reason = (None, Reason::NotHappening)
1533 .soonest((self.dtls.poll_timeout(self.last_now), Reason::DTLS))
1534 .soonest((self.ice.poll_timeout(), Reason::Ice))
1535 .soonest(self.session.poll_timeout())
1536 .soonest((self.sctp.poll_timeout(), Reason::Sctp))
1537 .soonest((self.chan.poll_timeout(&self.sctp), Reason::Channel))
1538 .soonest((stats.and_then(|s| s.poll_timeout()), Reason::Stats));
1539
1540 // trace!("poll_output timeout reason: {}", time_and_reason.1);
1541
1542 let time = time_and_reason.0.unwrap_or_else(not_happening);
1543 let reason = time_and_reason.1;
1544
1545 // We want to guarantee time doesn't go backwards.
1546 let next = if time < self.last_now {
1547 self.last_now
1548 } else {
1549 time
1550 };
1551
1552 self.last_timeout_reason = reason;
1553
1554 Ok(Output::Timeout(next))
1555 }
1556
1557 /// The reason for the last [`Output::Timeout`]
1558 ///
1559 /// This is updated when calling [`Rtc::poll_output()`] and the next output
1560 /// is a timeout.
1561 ///
1562 /// ```
1563 /// # #[cfg(feature = "openssl")] {
1564 /// # use str0m::{Rtc, Input, Output, Reason};
1565 /// let mut rtc = Rtc::new();
1566 ///
1567 /// let output = rtc.poll_output().unwrap();
1568 ///
1569 /// // Reason updates every time we get an Output::Timeout
1570 /// assert!(matches!(output, Output::Timeout(_)));
1571 ///
1572 /// // If there are no timeouts scheduled, we get NotHappening. The timeout
1573 /// // value itself will be in the distant future.
1574 /// assert_eq!(rtc.last_timeout_reason(), Reason::DTLS);
1575 /// # }
1576 /// ```
1577 pub fn last_timeout_reason(&self) -> Reason {
1578 self.last_timeout_reason
1579 }
1580
1581 /// Check if this `Rtc` instance accepts the given input. This is used for demultiplexing
1582 /// several `Rtc` instances over the same UDP server socket.
1583 ///
1584 /// [`Input::Timeout`] is always accepted. [`Input::Receive`] is tested against the nominated
1585 /// ICE candidate. If that doesn't match and the incoming data is a STUN packet, the accept call
1586 /// is delegated to the ICE agent which recognizes the remote peer from `a=ufrag`/`a=password`
1587 /// credentials negotiated in the SDP. If that also doesn't match, all remote ICE candidates are
1588 /// checked for a match.
1589 ///
1590 /// In a server setup, the server would try to find an `Rtc` instances using [`Rtc::accepts()`].
1591 /// The first found instance would be given the input via [`Rtc::handle_input()`].
1592 ///
1593 /// ```no_run
1594 /// # use str0m::{Rtc, Input};
1595 /// // A vec holding the managed rtc instances. One instance per remote peer.
1596 /// let mut rtcs = vec![Rtc::new(), Rtc::new(), Rtc::new()];
1597 ///
1598 /// // Configure instances with local ice candidates etc.
1599 ///
1600 /// loop {
1601 /// // TODO poll_timeout() and handle the output.
1602 ///
1603 /// let input: Input = todo!(); // read network data from socket.
1604 /// for rtc in &mut rtcs {
1605 /// if rtc.accepts(&input) {
1606 /// rtc.handle_input(input).unwrap();
1607 /// }
1608 /// }
1609 /// }
1610 /// ```
1611 pub fn accepts(&self, input: &Input) -> bool {
1612 let Input::Receive(_, r) = input else {
1613 // always accept the Input::Timeout.
1614 return true;
1615 };
1616
1617 // Fast path: DTLS, RTP, and RTCP traffic coming in from the same socket address
1618 // we've nominated for sending via the ICE agent. This is the typical case
1619 if let Some(send_addr) = &self.send_addr {
1620 if r.source == send_addr.destination {
1621 return true;
1622 }
1623 }
1624
1625 // STUN can use the ufrag/password to identify that a message belongs
1626 // to this Rtc instance.
1627 if let DatagramRecvInner::Stun(v) = &r.contents.inner {
1628 return self.ice.accepts_message(v);
1629 }
1630
1631 // Slow path: Occasionally, traffic comes in on a socket address corresponding
1632 // to a successful candidate pair other than the one we've currently nominated.
1633 // This typically happens at the beginning of the connection
1634 if self.ice.has_viable_remote_candidate(r.source) {
1635 return true;
1636 }
1637
1638 false
1639 }
1640
1641 /// Provide input to this `Rtc` instance. Input is either a [`Input::Timeout`] for some
1642 /// time that was previously obtained from [`Rtc::poll_output()`], or [`Input::Receive`]
1643 /// for network data.
1644 ///
1645 /// Both the timeout and the network data contains a [`std::time::Instant`] which drives
1646 /// time forward in the instance. For network data, the intention is to record the time
1647 /// of receiving the network data as precise as possible. This time is used to calculate
1648 /// things like jitter and bandwidth.
1649 ///
1650 /// It's always okay to call [`Rtc::handle_input()`] with a timeout, also before the
1651 /// time obtained via [`Rtc::poll_output()`].
1652 ///
1653 /// ```no_run
1654 /// # use str0m::{Rtc, Input};
1655 /// # use std::time::Instant;
1656 /// let mut rtc = Rtc::new();
1657 ///
1658 /// loop {
1659 /// let timeout: Instant = todo!(); // rtc.poll_output() until we get a timeout.
1660 ///
1661 /// let input: Input = todo!(); // wait for network data or timeout.
1662 /// rtc.handle_input(input);
1663 /// }
1664 /// ```
1665 pub fn handle_input(&mut self, input: Input) -> Result<(), RtcError> {
1666 if !self.alive {
1667 return Ok(());
1668 }
1669
1670 match input {
1671 Input::Timeout(now) => self.do_handle_timeout(now)?,
1672 Input::Receive(now, r) => {
1673 self.do_handle_receive(now, r)?;
1674 self.do_handle_timeout(now)?;
1675 }
1676 }
1677 Ok(())
1678 }
1679
1680 fn init_time(&mut self, now: Instant) {
1681 // The operation is somewhat expensive, hence we only do it once.
1682 if !self.need_init_time {
1683 return;
1684 }
1685
1686 // We assume this first "now" is a time 0 start point for calculating ntp/unix time offsets.
1687 // This initializes the conversion of Instant -> NTP/Unix time.
1688 let _ = now.to_unix_duration();
1689
1690 self.need_init_time = false;
1691 }
1692
1693 fn do_handle_timeout(&mut self, now: Instant) -> Result<(), RtcError> {
1694 self.init_time(now);
1695
1696 self.last_now = now;
1697 self.ice.handle_timeout(now);
1698 self.sctp.handle_timeout(now);
1699 self.chan.handle_timeout(now, &mut self.sctp);
1700 self.session.handle_timeout(now)?;
1701
1702 if let Some(stats) = &mut self.stats {
1703 if stats.wants_timeout(now) {
1704 let mut snapshot = StatsSnapshot::new(now);
1705 snapshot.peer_rx = self.peer_bytes_rx;
1706 snapshot.peer_tx = self.peer_bytes_tx;
1707 snapshot.selected_candidate_pair =
1708 self.send_addr.as_ref().map(|s| CandidatePairStats {
1709 protocol: s.proto,
1710 local: CandidateStats { addr: s.source },
1711 remote: CandidateStats {
1712 addr: s.destination,
1713 },
1714 });
1715 self.session.visit_stats(now, &mut snapshot);
1716 stats.do_handle_timeout(&mut snapshot);
1717 }
1718 }
1719
1720 Ok(())
1721 }
1722
1723 fn do_handle_receive(&mut self, now: Instant, r: net::Receive) -> Result<(), RtcError> {
1724 self.init_time(now);
1725
1726 trace!("IN {:?}", r);
1727 self.last_now = now;
1728 use DatagramRecvInner::*;
1729
1730 let bytes_rx = match r.contents.inner {
1731 // TODO: stun is already parsed (depacketized) here
1732 Stun(_) => 0,
1733 Dtls(v) | Rtp(v) | Rtcp(v) => v.len(),
1734 };
1735
1736 self.peer_bytes_rx += bytes_rx as u64;
1737
1738 match r.contents.inner {
1739 Stun(stun) => {
1740 let packet = io::StunPacket {
1741 proto: r.proto,
1742 source: r.source,
1743 destination: r.destination,
1744 message: stun,
1745 };
1746 self.ice.handle_packet(now, packet);
1747 }
1748 Dtls(dtls) => self.dtls.handle_receive(dtls)?,
1749 Rtp(rtp) => self.session.handle_rtp_receive(now, rtp),
1750 Rtcp(rtcp) => self.session.handle_rtcp_receive(now, rtcp),
1751 }
1752
1753 Ok(())
1754 }
1755
1756 /// Obtain handle for writing to a data channel.
1757 ///
1758 /// This is first available when a [`ChannelId`] is advertised via [`Event::ChannelOpen`].
1759 /// The function returns `None` also for IDs from [`SdpApi::add_channel()`].
1760 ///
1761 /// Incoming channel data is via the [`Event::ChannelData`] event.
1762 ///
1763 /// ```no_run
1764 /// # use str0m::{Rtc, channel::ChannelId};
1765 /// let mut rtc = Rtc::new();
1766 ///
1767 /// let cid: ChannelId = todo!(); // obtain channel id from Event::ChannelOpen
1768 /// let channel = rtc.channel(cid).unwrap();
1769 /// // TODO write data channel data.
1770 /// ```
1771 pub fn channel(&mut self, id: ChannelId) -> Option<Channel<'_>> {
1772 if !self.alive {
1773 return None;
1774 }
1775
1776 let sctp_stream_id = self.chan.stream_id_by_channel_id(id)?;
1777
1778 if !self.sctp.is_open(sctp_stream_id) {
1779 return None;
1780 }
1781
1782 Some(Channel::new(sctp_stream_id, self))
1783 }
1784
1785 /// Configure the Bandwidth Estimate (BWE) subsystem.
1786 ///
1787 /// Only relevant if BWE was enabled in the [`RtcConfig::enable_bwe()`]
1788 pub fn bwe(&mut self) -> Bwe {
1789 Bwe(self)
1790 }
1791
1792 fn is_correct_change_id(&self, change_id: usize) -> bool {
1793 self.change_counter == change_id + 1
1794 }
1795
1796 fn next_change_id(&mut self) -> usize {
1797 let n = self.change_counter;
1798 self.change_counter += 1;
1799 n
1800 }
1801
1802 /// The codec configs for sending/receiving data.
1803 ///
1804 /// The configurations can be set with [`RtcConfig`] before setting up the session, and they
1805 /// might be further updated by SDP negotiation.
1806 pub fn codec_config(&self) -> &CodecConfig {
1807 &self.session.codec_config
1808 }
1809}
1810
1811/// Configuation for the DTLS certificate used for the Rtc instance. This can be set to
1812/// allow a pregenerated certificate, or options to pass when generating a certificate
1813/// on-the-fly.
1814///
1815/// The default value is DtlsCertConfig::Options(DtlsCertOptions::default())
1816#[derive(Clone, Debug)]
1817pub enum DtlsCertConfig {
1818 /// The options to use for the DTLS certificate generated for this Rtc instance.
1819 Options(DtlsCertOptions),
1820 /// A pregenerated certificate to use for this Rtc instance.
1821 PregeneratedCert(DtlsCert),
1822}
1823
1824impl Default for DtlsCertConfig {
1825 fn default() -> Self {
1826 DtlsCertConfig::Options(DtlsCertOptions::default())
1827 }
1828}
1829
1830/// Customized config for creating an [`Rtc`] instance.
1831///
1832/// ```
1833/// # #[cfg(feature = "openssl")] {
1834/// use str0m::RtcConfig;
1835///
1836/// let rtc = RtcConfig::new()
1837/// .set_ice_lite(true)
1838/// .build();
1839/// # }
1840/// ```
1841///
1842/// Configs implement [`Clone`] to help create multiple `Rtc` instances.
1843#[derive(Debug, Clone)]
1844pub struct RtcConfig {
1845 local_ice_credentials: Option<IceCreds>,
1846 crypto_provider: CryptoProvider,
1847 dtls_cert_config: DtlsCertConfig,
1848 fingerprint_verification: bool,
1849 ice_lite: bool,
1850 initial_stun_rto: Option<Duration>,
1851 max_stun_rto: Option<Duration>,
1852 max_stun_retransmits: Option<usize>,
1853 codec_config: CodecConfig,
1854 exts: ExtensionMap,
1855 stats_interval: Option<Duration>,
1856 /// Whether to use Bandwidth Estimation to discover the egress bandwidth.
1857 bwe_config: Option<BweConfig>,
1858 reordering_size_audio: usize,
1859 reordering_size_video: usize,
1860 send_buffer_audio: usize,
1861 send_buffer_video: usize,
1862 rtp_mode: bool,
1863 enable_raw_packets: bool,
1864}
1865
1866#[derive(Debug, Clone)]
1867struct BweConfig {
1868 initial_bitrate: Bitrate,
1869 enable_loss_controller: bool,
1870}
1871
1872impl RtcConfig {
1873 /// Creates a new default config.
1874 pub fn new() -> Self {
1875 RtcConfig::default()
1876 }
1877
1878 /// Get the local ICE credentials, if set.
1879 ///
1880 /// If not specified, local credentials will be randomly generated when
1881 /// building the [`Rtc`] instance.
1882 pub fn local_ice_credentials(&self) -> &Option<IceCreds> {
1883 &self.local_ice_credentials
1884 }
1885
1886 /// Explicitly sets local ICE credentials.
1887 pub fn set_local_ice_credentials(mut self, local_ice_credentials: IceCreds) -> Self {
1888 self.local_ice_credentials = Some(local_ice_credentials);
1889 self
1890 }
1891
1892 /// Set the crypto provider.
1893 ///
1894 /// This happens implicitly if you use [`RtcConfig::set_dtls_cert_config()`].
1895 ///
1896 /// Panics: If you `set_dtls_cert_config()` followed by a different [`CryptoProvider`].
1897 ///
1898 /// This overrides what is set in [`CryptoProvider::install_process_default()`].
1899 pub fn set_crypto_provider(mut self, p: CryptoProvider) -> Self {
1900 if let DtlsCertConfig::PregeneratedCert(c) = &self.dtls_cert_config {
1901 if p != c.crypto_provider() {
1902 panic!("set_dtls_cert_config() locked crypto provider to: {}", p);
1903 }
1904 } else {
1905 self.crypto_provider = p;
1906 }
1907 self
1908 }
1909
1910 /// The configured crypto provider.
1911 ///
1912 /// Defaults to what's set in [`CryptoProvider::install_process_default()`] followed
1913 /// by a fallback to [`CryptoProvider::OpenSsl`].
1914 pub fn crypto_provider(&self) -> CryptoProvider {
1915 self.crypto_provider
1916 }
1917
1918 /// Returns the configured DTLS certificate configuration.
1919 ///
1920 /// Defaults to a configuration similar to libwebrtc:
1921 /// ```
1922 /// # use str0m::DtlsCertConfig;
1923 /// # use str0m::config::{DtlsCertOptions, DtlsPKeyType};
1924 ///
1925 /// DtlsCertConfig::Options(DtlsCertOptions {
1926 /// common_name: "WebRTC".into(),
1927 /// pkey_type: DtlsPKeyType::EcDsaP256,
1928 /// });
1929 /// ```
1930 pub fn dtls_cert_config(&self) -> &DtlsCertConfig {
1931 &self.dtls_cert_config
1932 }
1933
1934 /// Set the DTLS certificate configuration for certificate generation.
1935 ///
1936 /// Setting this permits you to assign a Pregenerated certificate, or
1937 /// options for certificate generation, such as signing key type, and
1938 /// subject name.
1939 ///
1940 /// If a Pregenerated certificate is set, this locks the `crypto_provider()`
1941 /// setting to the [`CryptoProvider`], for the DTLS certificate.
1942 ///
1943 /// ```
1944 /// # use str0m::{DtlsCertConfig, RtcConfig};
1945 /// # use str0m::config::{DtlsCertOptions, DtlsPKeyType};
1946 ///
1947 /// let dtls_cert_config = DtlsCertConfig::Options(DtlsCertOptions {
1948 /// common_name: "Clark Kent".into(),
1949 /// pkey_type: DtlsPKeyType::EcDsaP256,
1950 /// });
1951 ///
1952 /// let rtc_config = RtcConfig::default()
1953 /// .set_dtls_cert_config(dtls_cert_config);
1954 /// ```
1955 pub fn set_dtls_cert_config(mut self, dtls_cert_config: DtlsCertConfig) -> Self {
1956 if let DtlsCertConfig::PregeneratedCert(ref cert) = dtls_cert_config {
1957 self.crypto_provider = cert.crypto_provider();
1958 }
1959 self.dtls_cert_config = dtls_cert_config;
1960 self
1961 }
1962
1963 /// Toggle ice lite. Ice lite is a mode for WebRTC servers with public IP address.
1964 /// An [`Rtc`] instance in ice lite mode will not make STUN binding requests, but only
1965 /// answer to requests from the remote peer.
1966 ///
1967 /// See [ICE RFC][1]
1968 ///
1969 /// [1]: https://www.rfc-editor.org/rfc/rfc8445#page-13
1970 pub fn set_ice_lite(mut self, enabled: bool) -> Self {
1971 self.ice_lite = enabled;
1972 self
1973 }
1974
1975 /// Sets the initial STUN retransmission timeout (RTO).
1976 ///
1977 /// This is the initial wait time before a STUN request is retransmitted.
1978 /// The timeout will double with each retry, starting from this value.
1979 ///
1980 /// Defaults to 250ms.
1981 pub fn set_initial_stun_rto(&mut self, rto: Duration) {
1982 self.initial_stun_rto = Some(rto);
1983 }
1984
1985 /// Sets the maximum STUN retransmission timeout for the ICE agent.
1986 ///
1987 /// This is the upper bound for how long to wait between retransmissions.
1988 /// It also controls how often successful bindings are checked.
1989 ///
1990 /// Defaults to 3000ms.
1991 pub fn set_max_stun_rto(&mut self, rto: Duration) {
1992 self.max_stun_rto = Some(rto);
1993 }
1994
1995 /// Sets the maximum number of retransmits for STUN messages.
1996 ///
1997 /// Defaults to 9.
1998 pub fn set_max_stun_retransmits(&mut self, num: usize) {
1999 self.max_stun_retransmits = Some(num);
2000 }
2001
2002 /// Get fingerprint verification mode.
2003 ///
2004 /// ```
2005 /// # use str0m::Rtc;
2006 /// let config = Rtc::builder();
2007 ///
2008 /// // Defaults to true.
2009 /// assert!(config.fingerprint_verification());
2010 /// ```
2011 pub fn fingerprint_verification(&self) -> bool {
2012 self.fingerprint_verification
2013 }
2014
2015 /// Toggle certificate fingerprint verification.
2016 ///
2017 /// By default the certificate fingerprint is verified.
2018 pub fn set_fingerprint_verification(mut self, enabled: bool) -> Self {
2019 self.fingerprint_verification = enabled;
2020 self
2021 }
2022
2023 /// Tells whether ice lite is enabled.
2024 ///
2025 /// ```
2026 /// # #[cfg(feature = "openssl")] {
2027 /// # use str0m::Rtc;
2028 /// let config = Rtc::builder();
2029 ///
2030 /// // Defaults to false.
2031 /// assert_eq!(config.ice_lite(), false);
2032 /// # }
2033 /// ```
2034 pub fn ice_lite(&self) -> bool {
2035 self.ice_lite
2036 }
2037
2038 /// Lower level access to precise configuration of codecs (payload types).
2039 pub fn codec_config(&mut self) -> &mut CodecConfig {
2040 &mut self.codec_config
2041 }
2042
2043 /// Clear all configured codecs.
2044 ///
2045 /// ```
2046 /// # #[cfg(feature = "openssl")] {
2047 /// # use str0m::RtcConfig;
2048 /// // For the session to use only OPUS and VP8.
2049 /// let mut rtc = RtcConfig::default()
2050 /// .clear_codecs()
2051 /// .enable_opus(true)
2052 /// .enable_vp8(true)
2053 /// .build();
2054 /// # }
2055 /// ```
2056 pub fn clear_codecs(mut self) -> Self {
2057 self.codec_config.clear();
2058 self
2059 }
2060
2061 /// Enable opus audio codec.
2062 ///
2063 /// Enabled by default.
2064 pub fn enable_opus(mut self, enabled: bool) -> Self {
2065 self.codec_config.enable_opus(enabled);
2066 self
2067 }
2068
2069 /// Enable PCM μ-law audio codec.
2070 ///
2071 /// This is 14-bit audio compressed to 8-bit as specified by G.711
2072 pub fn enable_pcmu(mut self, enabled: bool) -> Self {
2073 self.codec_config.enable_pcmu(enabled);
2074 self
2075 }
2076
2077 /// Enable PCM a-law audio codec.
2078 ///
2079 /// This is 13-bit audio compressed to 8-bit as specified by G.711
2080 pub fn enable_pcma(mut self, enabled: bool) -> Self {
2081 self.codec_config.enable_pcma(enabled);
2082 self
2083 }
2084
2085 /// Enable VP8 video codec.
2086 ///
2087 /// Enabled by default.
2088 pub fn enable_vp8(mut self, enabled: bool) -> Self {
2089 self.codec_config.enable_vp8(enabled);
2090 self
2091 }
2092
2093 /// Enable H264 video codec.
2094 ///
2095 /// Enabled by default.
2096 pub fn enable_h264(mut self, enabled: bool) -> Self {
2097 self.codec_config.enable_h264(enabled);
2098 self
2099 }
2100
2101 // TODO: AV1 depacketizer/packetizer.
2102 //
2103 // /// Enable AV1 video codec.
2104 // ///
2105 // /// Enabled by default.
2106 // pub fn enable_av1(mut self) -> Self {
2107 // self.codec_config.add_default_av1();
2108 // self
2109 // }
2110
2111 /// Enable VP9 video codec.
2112 ///
2113 /// Enabled by default.
2114 pub fn enable_vp9(mut self, enabled: bool) -> Self {
2115 self.codec_config.enable_vp9(enabled);
2116 self
2117 }
2118
2119 /// Configure the RTP extension mappings.
2120 ///
2121 /// The default extension map is
2122 ///
2123 /// ```
2124 /// # use str0m::rtp::{Extension, ExtensionMap};
2125 /// let exts = ExtensionMap::standard();
2126 ///
2127 /// assert_eq!(exts.id_of(Extension::AudioLevel), Some(1));
2128 /// assert_eq!(exts.id_of(Extension::AbsoluteSendTime), Some(2));
2129 /// assert_eq!(exts.id_of(Extension::TransportSequenceNumber), Some(3));
2130 /// assert_eq!(exts.id_of(Extension::RtpMid), Some(4));
2131 /// assert_eq!(exts.id_of(Extension::RtpStreamId), Some(10));
2132 /// assert_eq!(exts.id_of(Extension::RepairedRtpStreamId), Some(11));
2133 /// assert_eq!(exts.id_of(Extension::VideoOrientation), Some(13));
2134 /// ```
2135 pub fn extension_map(&mut self) -> &mut ExtensionMap {
2136 &mut self.exts
2137 }
2138
2139 /// Set the extension map replacing the existing.
2140 pub fn set_extension_map(mut self, exts: ExtensionMap) -> Self {
2141 self.exts = exts;
2142 self
2143 }
2144
2145 /// Clear out the standard extension mappings.
2146 pub fn clear_extension_map(mut self) -> Self {
2147 self.exts.clear();
2148
2149 self
2150 }
2151
2152 /// Set an extension mapping on session level.
2153 ///
2154 /// The media level will be capped by the extension enabled on session level.
2155 ///
2156 /// The id must be 1-14 inclusive (1-indexed).
2157 pub fn set_extension(mut self, id: u8, ext: Extension) -> Self {
2158 self.exts.set(id, ext);
2159 self
2160 }
2161
2162 /// Set the interval between statistics events.
2163 ///
2164 /// None turns off the stats events.
2165 ///
2166 /// This includes [`MediaEgressStats`], [`MediaIngressStats`], [`MediaEgressStats`]
2167 pub fn set_stats_interval(mut self, interval: Option<Duration>) -> Self {
2168 self.stats_interval = interval;
2169 self
2170 }
2171
2172 /// The configured statistics interval.
2173 ///
2174 /// None means statistics are disabled.
2175 ///
2176 /// ```
2177 /// # use str0m::Rtc;
2178 /// # use std::time::Duration;
2179 /// let config = Rtc::builder();
2180 ///
2181 /// // Defaults to None.
2182 /// assert_eq!(config.stats_interval(), None);
2183 /// ```
2184 pub fn stats_interval(&self) -> Option<Duration> {
2185 self.stats_interval
2186 }
2187
2188 /// Enables estimation of available bandwidth (BWE).
2189 ///
2190 /// None disables the BWE. This is an estimation of the send bandwidth, not receive.
2191 ///
2192 /// This includes setting the initial estimate to start with.
2193 pub fn enable_bwe(mut self, initial_estimate: Option<Bitrate>) -> Self {
2194 match initial_estimate {
2195 Some(b) => {
2196 let conf = self.bwe_config.get_or_insert(BweConfig::new(b));
2197 conf.initial_bitrate = b;
2198 }
2199 None => {
2200 self.bwe_config = None;
2201 }
2202 }
2203
2204 self
2205 }
2206
2207 /// Enable the experimental loss based BWE subsystem.
2208 /// Defaults to disabled for now, will be enabled by default in the future.
2209 pub fn enable_experimental_loss_based_bwe(mut self, enabled: bool) -> Self {
2210 if let Some(c) = &mut self.bwe_config {
2211 c.enable_loss_controller = enabled;
2212 }
2213
2214 self
2215 }
2216
2217 /// The initial bitrate as set by [`Self::enable_bwe()`].
2218 ///
2219 /// ```
2220 /// # use str0m::Rtc;
2221 /// let config = Rtc::builder();
2222 ///
2223 /// // Defaults to None - BWE off.
2224 /// assert_eq!(config.bwe_initial_bitrate(), None);
2225 /// ```
2226 pub fn bwe_initial_bitrate(&self) -> Option<Bitrate> {
2227 self.bwe_config.as_ref().map(|c| c.initial_bitrate)
2228 }
2229
2230 /// Sets the number of packets held back for reordering audio packets.
2231 ///
2232 /// Str0m tries to deliver the samples in order. This number determines how many
2233 /// packets to "wait" before releasing media
2234 /// [`contiguous: false`][crate::media::MediaData::contiguous].
2235 ///
2236 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
2237 /// packets can arrive out of order.
2238 pub fn set_reordering_size_audio(mut self, size: usize) -> Self {
2239 self.reordering_size_audio = size;
2240
2241 self
2242 }
2243
2244 /// Returns the setting for audio reordering size.
2245 ///
2246 /// ```
2247 /// # use str0m::Rtc;
2248 /// let config = Rtc::builder();
2249 ///
2250 /// // Defaults to 15.
2251 /// assert_eq!(config.reordering_size_audio(), 15);
2252 /// ```
2253 ///
2254 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
2255 /// packets can arrive out of order.
2256 pub fn reordering_size_audio(&self) -> usize {
2257 self.reordering_size_audio
2258 }
2259
2260 /// Sets the number of packets held back for reordering video packets.
2261 ///
2262 /// Str0m tries to deliver the samples in order. This number determines how many
2263 /// packets to "wait" before releasing media with gaps.
2264 ///
2265 /// This must be at least as big as the number of packets the biggest keyframe can be split over.
2266 ///
2267 /// WARNING: video is very different to audio. Setting this value too low will result in
2268 /// missing video data. The 0 (as described for audio) is not relevant for video.
2269 ///
2270 /// Default: 30
2271 ///
2272 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
2273 /// packets can arrive out of order.
2274 pub fn set_reordering_size_video(mut self, size: usize) -> Self {
2275 self.reordering_size_video = size;
2276
2277 self
2278 }
2279
2280 /// Returns the setting for video reordering size.
2281 ///
2282 /// ```
2283 /// # use str0m::Rtc;
2284 /// let config = Rtc::builder();
2285 ///
2286 /// // Defaults to 30.
2287 /// assert_eq!(config.reordering_size_video(), 30);
2288 /// ```
2289 ///
2290 /// This setting is ignored in [RTP mode][`RtcConfig::set_rtp_mode()`] where RTP
2291 /// packets can arrive out of order.
2292 pub fn reordering_size_video(&self) -> usize {
2293 self.reordering_size_video
2294 }
2295
2296 /// Sets the buffer size for outgoing audio packets.
2297 ///
2298 /// This must be larger than 0. The value configures an internal ring buffer used as a temporary
2299 /// holding space between calling [`Writer::write`][crate::media::Writer::write()] and
2300 /// [`Rtc::poll_output`].
2301 ///
2302 /// For audio one call to `write()` typically results in one RTP packet since the entire payload
2303 /// fits in one. If you can guarantee that every `write()` is a single RTP packet, and is always
2304 /// followed by a `poll_output()`, it might be possible to set this value to 1. But that would give
2305 /// no margins for unexpected patterns.
2306 ///
2307 /// panics if set to 0.
2308 pub fn set_send_buffer_audio(mut self, size: usize) -> Self {
2309 assert!(size > 0);
2310 self.send_buffer_audio = size;
2311 self
2312 }
2313
2314 /// Returns the setting for audio resend size.
2315 ///
2316 /// ```
2317 /// # use str0m::Rtc;
2318 /// let config = Rtc::builder();
2319 ///
2320 /// // Defaults to 50.
2321 /// assert_eq!(config.send_buffer_audio(), 50);
2322 /// ```
2323 pub fn send_buffer_audio(&self) -> usize {
2324 self.send_buffer_audio
2325 }
2326
2327 /// Sets the buffer size for outgoing video packets and resends.
2328 ///
2329 /// This must be larger than 0. The value configures an internal ring buffer that is both
2330 /// used as a temporary holding space between calling [`Writer::write`][crate::media::Writer::write()]
2331 /// and [`Rtc::poll_output`] as well as for fulfilling resends.
2332 ///
2333 /// For video, this buffer is used for more than for audio. First, a call to `write()` often
2334 /// results in multiple RTP packets since large frames don't fit in one payload. That means the buffer
2335 /// must be at least as large to hold all those packets. Second, when the remote requests resends (NACK),
2336 /// those are fulfilled from this buffer. Third, for Bandwidth Estimation (BWE), when probing for
2337 /// available bandwidth, packets from this buffer are used to do "spurious resends", i.e. we do resends
2338 /// for packets that were not asked for.
2339 pub fn set_send_buffer_video(mut self, size: usize) -> Self {
2340 self.send_buffer_video = size;
2341 self
2342 }
2343
2344 /// Returns the setting for video resend size.
2345 ///
2346 /// ```
2347 /// # use str0m::Rtc;
2348 /// let config = Rtc::builder();
2349 ///
2350 /// // Defaults to 1000.
2351 /// assert_eq!(config.send_buffer_video(), 1000);
2352 /// ```
2353 pub fn send_buffer_video(&self) -> usize {
2354 self.send_buffer_video
2355 }
2356
2357 /// Make the entire Rtc be in RTP mode.
2358 ///
2359 /// This means all media, read from [`RtpPacket`] and written to
2360 /// [`StreamTx::write_rtp`][crate::rtp::StreamTx::write_rtp] are RTP packetized.
2361 /// It bypasses all internal packetization/depacketization inside str0m.
2362 ///
2363 /// WARNING: This is a low level API and is not str0m's primary use case.
2364 pub fn set_rtp_mode(mut self, enabled: bool) -> Self {
2365 self.rtp_mode = enabled;
2366
2367 self
2368 }
2369
2370 /// Checks if RTP mode is set.
2371 ///
2372 /// ```
2373 /// # use str0m::Rtc;
2374 /// let config = Rtc::builder();
2375 ///
2376 /// // Defaults to false.
2377 /// assert_eq!(config.rtp_mode(), false);
2378 /// ```
2379 pub fn rtp_mode(&self) -> bool {
2380 self.rtp_mode
2381 }
2382
2383 /// Enable the [`Event::RawPacket`] event.
2384 ///
2385 /// This clones data, and is therefore expensive.
2386 /// Should not be enabled outside of tests and troubleshooting.
2387 pub fn enable_raw_packets(mut self, enabled: bool) -> Self {
2388 self.enable_raw_packets = enabled;
2389 self
2390 }
2391
2392 /// Create a [`Rtc`] from the configuration.
2393 pub fn build(self) -> Rtc {
2394 Rtc::new_from_config(self)
2395 }
2396}
2397
2398impl BweConfig {
2399 fn new(initial_bitrate: Bitrate) -> Self {
2400 Self {
2401 initial_bitrate,
2402 enable_loss_controller: false,
2403 }
2404 }
2405}
2406
2407impl Default for RtcConfig {
2408 fn default() -> Self {
2409 Self {
2410 local_ice_credentials: None,
2411 crypto_provider: CryptoProvider::process_default().unwrap_or(CryptoProvider::OpenSsl),
2412 dtls_cert_config: Default::default(),
2413 fingerprint_verification: true,
2414 ice_lite: false,
2415 initial_stun_rto: None,
2416 max_stun_rto: None,
2417 max_stun_retransmits: None,
2418 codec_config: CodecConfig::new_with_defaults(),
2419 exts: ExtensionMap::standard(),
2420 stats_interval: None,
2421 bwe_config: None,
2422 reordering_size_audio: 15,
2423 reordering_size_video: 30,
2424 send_buffer_audio: 50,
2425 send_buffer_video: 1000,
2426 rtp_mode: false,
2427 enable_raw_packets: false,
2428 }
2429 }
2430}
2431
2432impl PartialEq for Event {
2433 fn eq(&self, other: &Self) -> bool {
2434 match (self, other) {
2435 (Self::IceConnectionStateChange(l0), Self::IceConnectionStateChange(r0)) => l0 == r0,
2436 (Self::MediaAdded(m0), Self::MediaAdded(m1)) => m0 == m1,
2437 (Self::MediaData(m1), Self::MediaData(m2)) => m1 == m2,
2438 (Self::ChannelOpen(l0, l1), Self::ChannelOpen(r0, r1)) => l0 == r0 && l1 == r1,
2439 (Self::ChannelData(l0), Self::ChannelData(r0)) => l0 == r0,
2440 (Self::ChannelClose(l0), Self::ChannelClose(r0)) => l0 == r0,
2441 _ => false,
2442 }
2443 }
2444}
2445
2446impl Eq for Event {}
2447
2448impl fmt::Debug for Rtc {
2449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2450 f.debug_struct("Rtc").finish()
2451 }
2452}
2453
2454/// Log a CSV like stat to stdout.
2455///
2456/// ```ignore
2457/// log_stat!("MY_STAT", 1, "hello", 3);
2458/// ```
2459///
2460/// will result in the following being printed
2461///
2462/// ```text
2463/// MY_STAT 1, hello, 3, {unix_timestamp_ms}
2464/// ````
2465///
2466/// These logs can be easily grepped for, parsed and graphed, or otherwise analyzed.
2467///
2468/// This macro turns into a NO-OP if the `_internal_dont_use_log_stats` feature is not enabled
2469macro_rules! log_stat {
2470 ($name:expr, $($arg:expr),+) => {
2471 #[cfg(feature = "_internal_dont_use_log_stats")]
2472 {
2473 use std::time::SystemTime;
2474 use std::io::{self, Write};
2475
2476 let now = SystemTime::now();
2477 let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
2478 let unix_time_ms = since_epoch.as_millis();
2479 let mut lock = io::stdout().lock();
2480 write!(lock, "{} ", $name).expect("Failed to write to stdout");
2481
2482 $(
2483 write!(lock, "{},", $arg).expect("Failed to write to stdout");
2484 )+
2485 writeln!(lock, "{}", unix_time_ms).expect("Failed to write to stdout");
2486 }
2487 };
2488}
2489pub(crate) use log_stat;
2490
2491#[cfg(test)]
2492#[doc(hidden)]
2493pub fn init_crypto_default() {
2494 crate::config::CryptoProvider::from_feature_flags().__test_install_process_default();
2495}
2496
2497#[cfg(test)]
2498mod test {
2499 use std::panic::UnwindSafe;
2500
2501 use super::*;
2502
2503 #[test]
2504 fn rtc_is_send() {
2505 fn is_send<T: Send>(_t: T) {}
2506 fn is_sync<T: Sync>(_t: T) {}
2507 is_send(Rtc::new());
2508 is_sync(Rtc::new());
2509 }
2510
2511 #[test]
2512 fn rtc_is_unwind_safe() {
2513 fn is_unwind_safe<T: UnwindSafe>(_t: T) {}
2514 is_unwind_safe(Rtc::new());
2515 }
2516
2517 #[test]
2518 fn event_is_reasonably_sized() {
2519 let n = std::mem::size_of::<Event>();
2520 assert!(n < 450);
2521 }
2522}
2523
2524#[cfg(feature = "_internal_test_exports")]
2525#[allow(missing_docs)]
2526pub mod _internal_test_exports;