Modern WebRTC SFU built in Rust with a browser SDK, demo app, and telemetry UI. Focus: fast forwarding, adaptive quality, and real-world operability.
./docker-dev.shNo Node.js, npm, or Rust required — just Docker. Builds SDK, demo, and server inside the container. Open http://localhost:8080/muxquito/ when ready.
- Highlights
- Quick start
- Demo app
- SDK (browser)
- Server features
- SDK overview
- Debug endpoints
- Env vars
- Development
- Docker
- License
- Simulcast forwarding with per-subscriber allocation. The server tracks q/h/f layers and chooses a layer per subscriber based on bandwidth + congestion, with keyframe-gated switches.
- Adaptive streaming in the SDK. Client-side observers watch element size + visibility and request layer changes with debounce.
- Cascading across nodes. A node can pull tracks from upstream servers (parent/child edges), and relay maps coordinate multi-node subscriptions for scale-out topologies.
- Codec negotiation by default. VP8/VP9/H264/AV1 video, Opus + RED audio, RTX enabled.
- Telemetry + debug. Clients stream RTC stats over a telemetry data channel; server persists and exposes a tail API alongside allocator/stats/rooms/peers endpoints.
./docker-dev.sh # Docker only — no local deps
./dev.sh # local build (Node.js + Rust)| URL | Description |
|---|---|
| http://localhost:8080/muxquito/ | Demo app |
| http://localhost:8080/ui/ | Telemetry UI |
| Parameter | Description |
|---|---|
?room=name or #name |
Join room |
?adaptive=0 |
Disable client-side layer requests |
Features: mic/cam toggle, device select, screen share.
npm install muxquito-webrtc-sdkimport { Room, LocalAudioTrack, LocalVideoTrack, DeviceManager } from 'muxquito-webrtc-sdk';
const room = new Room({ reconnect: true, adaptiveStream: true, telemetry: true });
room.on('connected', () => console.log('connected'));
room.on('participantJoined', (p) => console.log('peer', p.sid));
room.on('trackSubscribed', (track) => {
document.getElementById('videos')?.appendChild(track.attach());
});
await room.connect('ws://localhost:8080/ws', 'demo', 'alice');
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
const audioTrack = new LocalAudioTrack(stream.getAudioTracks()[0]);
const videoTrack = new LocalVideoTrack(stream.getVideoTracks()[0], 'camera');
await room.localParticipant.publishTrack(audioTrack);
await room.localParticipant.publishTrack(videoTrack, { simulcast: true, source: 'camera' });- WebSocket signaling: join/leave, offer/answer/ice
- Rooms + peer tracking
- Simulcast forwarding with per-subscriber layer selection
- Stream allocator (bandwidth estimate + congestion) with keyframe-gated layer switches
- Track metadata + mute propagation
- Audio level parsing → active speaker + audio level updates
- Telemetry data channel ingest (client stats → disk)
- Optional state backend: in-memory default, Redis supported
- Optional cascade edges for multi-node experiments (debug endpoints in test mode)
Roomwith reconnect and adaptive stream supportLocalParticipantpublish/unpublish + codec preferencesRemoteTrack.attach()/detach()DeviceManagerdevice enumeration + change events- Events:
connected,disconnected,reconnecting,reconnected,participantJoined,participantLeft,trackSubscribed,trackUnsubscribed,trackMuted,trackUnmuted,activeSpeakerChanged,audioLevelsChanged,connectionStateChanged,connectionQualityChanged,error
| Endpoint | Method |
|---|---|
/debug/stats |
GET |
/debug/stats/:peer |
GET |
/debug/bwe |
GET |
/debug/bwe/:peer |
POST |
/debug/allocator |
GET |
/debug/layers |
GET |
/debug/layers/:peer |
GET |
/debug/forwarders |
GET |
/debug/server |
GET |
/debug/rooms |
GET |
/debug/peers |
GET |
/debug/telemetry/tail?room=... |
GET |
| Variable | Default | Description |
|---|---|---|
BIND_ADDR |
0.0.0.0:8080 |
Server bind address |
PUBLIC_BASE_PATH |
/muxquito |
Base path for demo app |
STATIC_DIR |
demo/public |
Static files directory |
UI_DIR |
server/static/ui |
Telemetry UI directory |
STUN_SERVERS |
— | Comma-separated STUN servers |
ICE_UDP_MIN |
— | Minimum UDP port for ICE |
ICE_UDP_MAX |
— | Maximum UDP port for ICE |
TLS_CERT_PATH |
— | TLS certificate path |
TLS_KEY_PATH |
— | TLS key path |
TEST_MODE |
— | Enable cascade debug endpoints |
NODE_ID |
— | Node identifier |
NODE_REGION |
— | Node region |
NODE_CAPACITY |
— | Node capacity |
STATE_BACKEND |
in_memory |
in_memory or redis |
REDIS_URL |
— | Redis connection URL |
STATE_USE_DEMAND_AGGREGATE |
— | Demand aggregation |
STATE_PUBLISH_SUBSCRIPTIONS |
— | Publish subscriptions |
ACTIVE_SET_BASE |
— | Active set base |
ACTIVE_SET_MAX |
— | Active set maximum |
ACTIVE_SET_SILENCE_MS |
— | Silence threshold (ms) |
TELEMETRY_DIR |
— | Telemetry storage directory |
CODEC_NEGOTIATION |
— | Enable codec negotiation |
VIDEO_CODECS |
— | Allowed video codecs |
AUDIO_CODECS |
— | Allowed audio codecs |
# bundle demo app
npx --package typescript tsc -p sdk/tsconfig.json
npx esbuild demo/src/main.ts --bundle --outfile=demo/public/main.js --format=esm --external:lucide --alias:muxquito-webrtc-sdk=./sdk/dist/index.js
# SDK tests
cd sdk && npm testdocker build -t muxquito .
docker run --rm --name muxquito -p 8080:8080 -p 10000-10100:10000-10100/udp muxquitoThe container prints the demo, telemetry UI, WS, health, and debug URLs on startup.
# Stop the container
docker stop muxquitoMIT