6.1 KiB
iroh-live: Core API — Live, Call, Subscription, Ticket
Live — Entry Point
The primary entry point for all iroh-live operations. Manages an iroh Endpoint, the MoQ transport (Moq), and optionally a Gossip instance for rooms.
Construction
// Simple: from environment, accept incoming connections
let live = Live::from_env().await?.with_router().spawn();
// With gossip for rooms
let live = Live::from_env().await?.with_router().with_gossip().spawn();
// From an existing endpoint
let live = Live::builder(endpoint).with_router().with_gossip().spawn();
// Manual router mounting (when you have other protocols)
let router = live.register_protocols(Router::builder(endpoint));
let router = router.accept(other_protocol, other_handler);
let router = router.spawn();
Key Methods
| Method | Description |
|---|---|
publish(name, &LocalBroadcast) |
Register a broadcast for all connected peers |
subscribe(remote, name) |
Connect to a peer and subscribe to a broadcast → Subscription |
subscribe_media(remote, name, audio, config) |
Connect, subscribe, decode → (MoqSession, MediaTracks) |
join_room(ticket) |
Join a gossip-based multi-party room → Room |
endpoint() |
Access the underlying iroh Endpoint |
transport() |
Access the Moq transport for advanced operations |
gossip() |
Access the Gossip instance (if enabled) |
shutdown() |
Close all sessions, stop router, close endpoint |
Builder Options
with_router()— Spawns an internalRouterso the endpoint accepts incoming MoQ sessions. Without this, only outbound connections work.with_gossip()— Creates aGossipinstance (required for rooms). Internally mounts on the Router ifwith_routeris also set.gossip(gossip)— Use an externally-managedGossipinstance.
Internal Architecture
Live holds:
endpoint: Endpoint— iroh QUIC endpointmoq: Moq— Internal actor for session/broadcast managementgossip: Option<Gossip>— For room coordinationrouter: Option<Router>— For accepting incoming connections
The from_env() method reads IROH_SECRET for the secret key and generates one if not set. It uses the N0 preset for relay and DNS discovery.
LiveTicket — Connection Sharing
A serializable ticket that contains everything needed to connect to a publisher.
// Create a ticket
let ticket = LiveTicket::new(endpoint.addr(), "my-stream");
// Serialize to URI string (fits in QR codes)
let s = ticket.to_string();
// → "iroh-live:<base64url(postcard(EndpointAddr))>/my-stream"
// Deserialize
let parsed: LiveTicket = s.parse()?;
// With relay URLs for indirect connectivity
let ticket = LiveTicket::new(addr, "stream").with_relay_urls(vec![
"https://relay.example.com".to_string(),
]);
Format: iroh-live:<base64url(postcard(EndpointAddr))>/<name>
Also supports legacy name@base32 format for backward compatibility.
The ticket string is kept short enough for QR codes (< 2000 bytes). It uses postcard (binary) serialization with base64url encoding.
Call — 1:1 Video Call
A convenience wrapper over MoQ primitives for bidirectional calls.
Flow
- One side creates a
LocalBroadcastwith video/audio configured - Dialer:
Call::dial(live, remote_addr, local_broadcast)— connects, publishes "call" broadcast, subscribes to remote's "call" broadcast - Acceptor:
Call::accept(session, local_broadcast)— accepts an incoming session, publishes and subscribes
The broadcast name is always "call" — this is hardcoded (CALL_BROADCAST_NAME).
// Dialer side
let local = LocalBroadcast::new();
local.video().set_source(camera, VideoCodec::H264, [VideoPreset::P720])?;
let call = Call::dial(&live, remote_addr, local).await?;
// Access remote media
let remote_broadcast = call.remote();
let video = remote_broadcast.video()?;
// Wait for call to end
let reason = call.closed().await;
Key Properties
call.local()→&LocalBroadcast(your media)call.remote()→&RemoteBroadcast(peer's media)call.signals()→watch::Receiver<NetworkSignals>(for adaptive bitrate)call.close()— closes with error code 0 and reason "call ended"call.closed()→ waits for close, returnsDisconnectReason(LocalClose, RemoteClose, TransportError)
Auto-wires stats recording and network signal production on the connection.
Subscription — Subscribe Handle
Created by Live::subscribe(). Wraps the MoQ session, remote broadcast, and network signals into a single handle. The constructor auto-wires stats recording and signal production.
let sub = live.subscribe(remote_addr, "stream").await?;
// Access components
sub.session() // &MoqSession
sub.broadcast() // &RemoteBroadcast
sub.signals() // &watch::Receiver<NetworkSignals>
// Convenience methods
let tracks = sub.media(&audio_backend, Default::default()).await?;
let tracks = sub.media_with_decoders::<DefaultDecoders>(&audio_backend, config).await?;
// Decompose
let (session, broadcast, signals) = sub.into_parts();
DisconnectReason
pub enum DisconnectReason {
LocalClose,
RemoteClose,
TransportError,
}
Derived from the QUIC connection's close reason. Used by Call::closed().
util Module
secret_key_from_env()
Loads the iroh secret key from the IROH_SECRET environment variable. Generates a new key if not set, printing the hex-encoded key for reuse.
spawn_signal_producer(conn, shutdown)
Spawns a background task that polls QUIC connection path stats every 200ms and produces NetworkSignals for adaptive rendition selection. Returns a watch::Receiver<NetworkSignals>.
Computes:
- RTT — from
selected_path.rtt() - Loss rate — delta-based (lost packets / (sent + lost) over the interval)
- Available bandwidth — estimated from congestion window:
cwnd * 8 / rtt - Congestion events — monotonically increasing counter
spawn_stats_recorder(conn, net_stats, shutdown)
Records connection stats (RTT, loss rate, bandwidth, path type) into NetStats for debug overlay display. Runs every 200ms.