qrtc quantum realtime communication GitHub

Architecture

Each peer connection is one task, the driver, on a private tokio runtime. The driver owns everything that mutates the connection, so the rules of the sans-I/O str0m stack hold in exactly one place.

Consumer (plugin, browser engine) NativePeer: blocking handle Driver task (one per peer connection) str0m Rtc: ICE agent, DTLS, SRTP, SCTP, RTP JSEP state, transceivers, data channel queues audio: APM, Opus, jitter buffers, 10 ms playout clock UDP sockets per interface TURN: UDP, TCP, TLS tasks DNS and mDNS lookups commands events (EventSink) every packet in or out passes through the driver
Commands go down through the handle; events come back through the consumer's sink, in order per connection.

The two traits

PeerEngine creates peer connections. Peer is one connection, with methods that mirror RTCPeerConnection: descriptions, candidates, data channels, transceivers, media frames, DTMF and stats. A consumer depends on the traits, so another engine could stand behind them. native::NativeEngine is the one qrtc ships.

The driver

The driver owns the str0m::Rtc, its sockets, the JSEP state and the data channel queues. Every mutation of the Rtc is followed by a drain step. The drain sends queued packets, delivers events and arms the next timeout. Work that may block, such as DNS resolution and mDNS lookups, runs as separate tasks that report back to the driver.

NativePeer is a cheap handle. Each method sends a command to the driver and waits for the answer, which is why every method blocks. Engine events reach the consumer's EventSink from engine threads, in order per peer connection.

Sockets and candidates

The engine binds one UDP socket per usable interface address, and each becomes a host candidate. Loopback is used only when nothing else exists, as in an isolated CI container. Server-reflexive candidates come from STUN Binding requests on those sockets. Relayed candidates come from TURN allocations over UDP, or over a stream to the TURN server when the URL asks for TCP or TLS.

Host candidates leave the engine with a random <uuid>.local name in place of the address, as browsers do, so the page, the signalling server and a remote peer that never connects do not learn the interface addresses. One mDNS responder per engine listens on 224.0.0.251:5353 and ff02::fb:5353 and answers A/AAAA queries for the names of live peer connections, at most one multicast answer per name per second. Remote .local candidates are resolved with a multicast query before they reach the ICE agent; a name registered by this engine resolves without the network. If no mDNS socket can be opened, host candidates carry plain addresses and a warning is logged.

The JSEP layer

str0m generates the SDP. qrtc rewrites only what W3C semantics decide and str0m cannot know:

Setting a local description that differs from the last offer or answer is checked semantically, as Chrome and Safari do. Line order, whitespace, candidates and codec parameters may differ, because matrix-js-sdk re-serialises every local description. Structural edits are refused: setup role, ICE credentials, fingerprints, media sections and directions.

Media paths

Video is encoded and decoded by the consumer, for example with WebCodecs in a web page. The engine moves encoded frames: it packetizes them into RTP, reassembles received frames, and asks for keyframes. Audio is processed in the engine, because echo cancellation needs the far-end signal: the consumer pushes and receives 48 kHz PCM. Audio covers the details.

Where each piece lives

ModuleRole
native/mod.rsNativeEngine, NativePeer and the command channel
native/driver.rsThe per-connection driver
native/jsep.rsSDP section parsing and the rewrites above
native/audio.rsOpus, audio processing, jitter buffers, playout clock
native/net.rsInterface discovery and host sockets
native/stun.rs, turn.rs, turn_stream.rsSTUN/TURN codec, TURN client state machine, TCP and TLS transports
native/mdns.rsmDNS responder for our host candidate names and resolver for remote .local candidates
native/pq.rsPost-quantum and hybrid key exchange groups