oxpulse_sfu_kit/lib.rs
1//! A reusable multi-client SFU (Selective Forwarding Unit) kit built on top of
2//! [str0m](https://github.com/algesten/str0m).
3//!
4//! str0m is a sans-I/O Rust WebRTC library — you plug in your own networking.
5//! This crate adds the multi-client glue: per-peer state machines, UDP packet
6//! routing, event fanout, and simulcast layer forwarding. It does **not** replace
7//! str0m; it connects multiple str0m [`Rtc`][str0m::Rtc] instances together into
8//! a functioning room.
9//!
10//! # What this gives you
11//!
12//! - **[`Client`]** — per-peer state wrapping a str0m `Rtc` instance.
13//! Handles `poll_output`, incoming datagrams, keyframe requests, and simulcast
14//! layer filtering.
15//! - **[`Registry`]** — room-level packet router. Routes UDP datagrams to the
16//! correct peer via `rtc.accepts()`, drives `poll_all`, and fans out events to
17//! every non-origin peer.
18//! - **[`Propagated`]** — the event enum flowing between the registry and clients.
19//! - **[`SfuConfig`]** — runtime configuration (UDP port, bind address).
20//! - **[`run_udp_loop`]** — a ready-to-use async UDP
21//! receive loop for the simple single-room case.
22//!
23//! # Quick start
24//!
25//! ```no_run
26//! use std::sync::Arc;
27//! use oxpulse_sfu_kit::{Client, Registry, SfuConfig, SfuRtcBuilder, udp_loop};
28//!
29//! #[tokio::main]
30//! async fn main() -> anyhow::Result<()> {
31//! let config = SfuConfig {
32//! udp_port: 3478,
33//! ..SfuConfig::default()
34//! };
35//!
36//! // Shutdown on Ctrl-C.
37//! let shutdown = async { tokio::signal::ctrl_c().await.unwrap() };
38//! udp_loop::run_udp_loop(config, shutdown).await
39//! }
40//! ```
41//!
42//! Clients are inserted into the registry via [`Registry::insert`] after
43//! completing ICE/DTLS signaling (your responsibility — bring your own
44//! WebSocket/HTTP signaling layer).
45//!
46//! # Feature flags
47//!
48//! | Feature | What it enables |
49//! |---------|----------------|
50//! | `active-speaker` | Dominant speaker detection via [`rust-dominant-speaker`](https://crates.io/crates/rust-dominant-speaker). Adds [`Propagated::ActiveSpeakerChanged`] and [`Registry::tick_active_speaker`] / [`Registry::record_audio_level`]. |
51//! | `metrics-prometheus` | Prometheus counters exposed via [`SfuMetrics`]. The library carries the handles; you choose how to expose them (e.g. via axum). |
52//! | `test-utils` | Exposes test seam helpers (`test_seed` module, `Registry::*_for_tests` methods). Gate your own tests on this. |
53//!
54//! # Not included (by design)
55//!
56//! - Signaling (bring your own — WebSocket, HTTP, gRPC)
57//! - TURN server (run coturn or similar alongside)
58//! - End-to-end encryption payload processing (use SFrame; see [`sframe::KeyEpoch`])
59//! - Server-side audio/video mixing (MCU mode)
60//! - WHIP / WHEP ingestion endpoints
61//!
62//! # Examples
63//!
64//! - `examples/basic-sfu.rs` — a complete single-node SFU that binds a UDP port
65//! and handles signaling stubs. Run with `cargo run --example basic-sfu --features active-speaker,metrics-prometheus`.
66//!
67//! # Relationship to str0m
68//!
69//! We build on str0m's `Rtc` state machine. We do not replace it — we connect
70//! multiple instances together for multi-party rooms. All credit for the
71//! underlying protocol work goes to [Martin Algesten](https://github.com/algesten)
72//! and the str0m contributors.
73//!
74//! # Extracted from
75//!
76//! Originally built as part of [OxPulse Chat](https://oxpulse.chat).
77//! Published standalone for the broader Rust WebRTC ecosystem.
78
79#![forbid(unsafe_code)]
80#![cfg_attr(docsrs, feature(doc_cfg))]
81
82#[cfg(feature = "av1-dd")]
83#[cfg_attr(docsrs, doc(cfg(feature = "av1-dd")))]
84pub mod av1;
85pub mod bandwidth;
86#[cfg(any(feature = "pacer", feature = "kalman-bwe", feature = "googcc-bwe"))]
87#[cfg_attr(
88 docsrs,
89 doc(cfg(any(feature = "pacer", feature = "kalman-bwe", feature = "googcc-bwe")))
90)]
91pub mod bwe;
92pub mod cc;
93pub mod client;
94pub mod config;
95pub mod dc;
96pub mod fanout;
97pub mod ids;
98pub mod keyframe;
99pub mod layer_selector;
100pub mod media;
101pub mod metrics;
102pub mod net;
103pub mod origin;
104pub mod propagate;
105pub mod raw;
106pub mod registry;
107pub mod rtc;
108pub mod rtcp_stats;
109pub mod sframe;
110pub mod udp_loop;
111#[cfg(feature = "vfm")]
112#[cfg_attr(docsrs, doc(cfg(feature = "vfm")))]
113pub mod vfm;
114
115#[cfg(feature = "av1-dd")]
116#[cfg_attr(docsrs, doc(cfg(feature = "av1-dd")))]
117pub use av1::Av1DdInfo;
118pub use bandwidth::BandwidthEstimate;
119#[cfg(feature = "kalman-bwe")]
120#[cfg_attr(docsrs, doc(cfg(feature = "kalman-bwe")))]
121pub use bwe::feedback::{TwccFeedback, TwccSample};
122#[cfg(feature = "kalman-bwe")]
123#[cfg_attr(docsrs, doc(cfg(feature = "kalman-bwe")))]
124pub use bwe::kalman::DelayEstimator;
125#[cfg(feature = "kalman-bwe")]
126#[cfg_attr(docsrs, doc(cfg(feature = "kalman-bwe")))]
127pub use bwe::loss::LossEstimator;
128#[cfg(feature = "kalman-bwe")]
129#[cfg_attr(docsrs, doc(cfg(feature = "kalman-bwe")))]
130pub use bwe::subscriber::{ClientHint, PerSubscriber};
131#[cfg(feature = "kalman-bwe")]
132#[cfg_attr(docsrs, doc(cfg(feature = "kalman-bwe")))]
133pub use bwe::BandwidthEstimator;
134#[cfg(feature = "googcc-bwe")]
135#[cfg_attr(docsrs, doc(cfg(feature = "googcc-bwe")))]
136pub use bwe::GoogCcEstimator;
137#[cfg(feature = "pacer")]
138#[cfg_attr(docsrs, doc(cfg(feature = "pacer")))]
139pub use bwe::PacerAction;
140#[cfg(feature = "pacer")]
141#[cfg_attr(docsrs, doc(cfg(feature = "pacer")))]
142pub use bwe::SubscriberPacer;
143#[cfg(feature = "pacer")]
144#[cfg_attr(docsrs, doc(cfg(feature = "pacer")))]
145pub use bwe::{PacerConfig, PacerConfigError};
146pub use cc::{CongestionControl, DefaultGoogCC};
147pub use client::{Client, TrackIn};
148pub use config::SfuConfig;
149pub use dc::ChannelConfig;
150pub use ids::{InvalidRid, SfuMid, SfuPt, SfuRid};
151pub use keyframe::{SfuKeyframeKind, SfuKeyframeRequest};
152pub use layer_selector::{BestFitSelector, LayerSelector};
153pub use media::{SfuMediaKind, SfuMediaPayload};
154pub use metrics::SfuMetrics;
155pub use net::{IncomingDatagram, OutgoingDatagram, SfuProtocol};
156pub use origin::ClientOrigin;
157pub use propagate::{ClientId, Propagated};
158pub use raw::{rtc_config, RawRtc, RawRtcConfig};
159pub use registry::Registry;
160pub use rtc::{SfuRtc, SfuRtcBuilder};
161pub use rtcp_stats::PeerRtcpStats;
162pub use sframe::KeyEpoch;
163pub use udp_loop::{bind, run_udp_loop, serve, serve_socket};
164#[cfg(feature = "vfm")]
165#[cfg_attr(docsrs, doc(cfg(feature = "vfm")))]
166pub use vfm::FrameMarkingInfo;