Expand description
Odoo’s Selective Forwarding Unit (SFU) for audio/video calls.
o-sfu receives participants’ audio and video and forwards selected streams
to other participants in the same room. Each sender uploads media to the SFU
instead of sending a separate copy to every receiver.
Alice -- audio/video --> SFU --+--> Bob
|
+--> CarolA room groups the participants, published media and subscriptions for a call. One server can host independently authenticated rooms for multiple tenants, such as different Odoo databases. This fits the Odoo saas and Odoo.sh architecture.
§A call through the SFU
The application backend provisions a room and gives participants credentials to join it. Browser clients use WebSocket signaling to join, publish media and choose what to receive. Media travels separately from signaling, over WebRTC.
Odoo backend -- HTTP ------> room creation and access
|
v
Browser <---- WebSocket ---> room membership and media requests
|
v
Browser <---- WebRTC/UDP --> media workers <----> other browsersThis separates decisions about the call from the work of forwarding its media. Signaling changes which streams should flow. Media workers deliver those streams as packets arrive.
§Architecture
Runtime brings together the HTTP/WebSocket servers, rooms and media
workers. The HTTP control API manages rooms through
RoomManager. WebSocket sessions admit
participants through SfuCore and use a
MediaSession for each connection to publish
media and manage subscriptions.
HTTP control API ------------------------------> RoomManager
|
WebSocket session -> SfuCore -> MediaSession |
\ /
+----> Room <----+
|
+<----> Router
|
v
MediaTransport
| | |
v v v
RTC workers / packet loops
(one thread per worker)
| | |
UDP socket UDP socket UDP socket
| | |
v v v
fanout fanout fanout
UDP OUT UDP OUT UDP OUTA Room manages who is present and which media
they publish or receive. The Router keeps
track of the connections needed to deliver that media. Its decisions do not
perform network I/O, so routing behavior can be tested without sockets.
MediaTransport runs the worker
threads and uses str0m for WebRTC. Room policy selects media using
subscriptions, video layout, active speakers and receiver bandwidth. Each
worker forwards incoming media to the selected receivers, shown as fanout
in the diagram.
Rooms start with one worker. config::RoomWorkerPolicy can spread new
joins across additional workers in the same process. Participants still
belong to one room and can receive media from participants on other workers.
§Access and encryption
The backend signs tokens for room creation and disconnect requests with
the server’s AUTH_KEY. Participant tokens are verified with their room’s
signing key. This lets a tenant authorize its participants without giving
them the server key. See auth for token types and verification.
Admission establishes participant identity and room scope. It does not
enforce the per-user permissions claim supplied by the tenant.
Signaling and media need separate transport protection:
- An external reverse proxy terminates HTTPS and WSS. Proxy mode requires
configured trusted proxies and the public edge must overwrite
client-supplied forwarded host and protocol headers. See
configandhttp::resolve_request_origin. str0mencrypts media between each browser and the SFU with DTLS-SRTP. Media UDP must reach the server’s configuredANNOUNCED_IP.
§Browser client
The JavaScript bundle exposes SfuClient, the Odoo-facing API for joining
calls, publishing media and subscribing to other participants. For example,
with a room token supplied by the application:
import { SfuClient } from "/bundle/odoo_sfu.js";
const sfu = new SfuClient();
sfu.connect("https://sfu.example.com/ws", jsonWebToken, {
channelUUID: "mail-channel-uuid",
});connect() returns before authentication and media negotiation finish.
The application observes stateChange and handledError for the result.
The client shares its signaling logic through
o_sfu_protocol::host::ProtocolCore. The browser adapter runs that Rust
logic as WebAssembly and connects it to browser WebSocket and WebRTC APIs.
This keeps protocol decisions separate from browser-specific operations.
§Running and observing the server
run starts the server from configuration. Use Runtime to embed it
with a caller-provided listener and shutdown signal. config controls
network addresses, admission limits and media worker policy at runtime.
The default Cargo feature otel-tracing enables OpenTelemetry tracing.
http::telemetry::metrics exposes Prometheus metrics for monitoring over
time. http::telemetry::diagnostics exposes room, participant and worker
state for investigating a call. These operator endpoints require separate
access control, described in http.
§Where to read next
| Area | Entry point |
|---|---|
| Configuration and server lifecycle | config, Runtime and run |
| Authentication and signaling messages | auth, http and websocket |
| Room behavior and media transport | o_sfu_core |
| Connections and media routing | o_sfu_router |
| Client signaling and Odoo bundle compatibility | o_sfu_protocol |
| Metrics, diagnostics and tracing | o_sfu_telemetry |
| Shared call identifiers | o-sfu-model, including o_sfu_protocol::wire::UserId |
| Protocol constants and types | o_sfu_rfc |
Modules§
- application 🔒
- auth
- config
- core
- http
- HTTP route and payload contracts.
- runtime 🔒
- Wires process services and drains them during shutdown.
- websocket
Structs§
- Runtime
- Process services and lifecycle configuration.
Enums§
- Serve
Error - Failure to serve or fully drain a
Runtime.
Functions§
- run
- Errors