Full-featured SIP router with multi-provider trunking, browser softphone via WebRTC, real-time Opus/G.722/PCM transcoding in Rust, RNNoise ML noise suppression, Kokoro neural TTS announcements, and a Lit-based web dashboard with live call monitoring and REST API.
2.2 KiB
Project Notes
Architecture: Hub Model (Call as Centerpiece)
All call logic lives in ts/call/. The Call is the central entity with N legs.
Key Files
ts/call/call-manager.ts— singleton registry, factory methods, SIP routingts/call/call.ts— the hub: owns legs, media forwardingts/call/sip-leg.ts— SIP device/provider connection (wraps SipDialog)ts/call/webrtc-leg.ts— browser WebRTC connection (wraps werift PeerConnection)ts/call/rtp-port-pool.ts— unified RTP port poolts/sipproxy.ts— thin bootstrap wiring everything togetherts/webrtcbridge.ts— browser device registration (signaling only)
WebRTC Browser Call Flow (Critical)
The browser call flow has a specific signaling order that MUST be followed:
POST /api/callwith browser deviceId → CallManager creates Call, saves pending state, notifies browser viawebrtc-incoming- Browser sends
webrtc-offer(with its ownsessionId) → CallManager creates a standalone WebRtcLeg (NOT attached to any call yet) - Browser sends
webrtc-accept(withcallId+sessionId) → CallManager links the standalone WebRtcLeg to the Call, then starts the SIP provider leg
The WebRtcLeg CANNOT be created at call creation time because the browser's session ID is unknown until the webrtc-offer arrives.
WebRTC Audio Return Channel (Critical)
The SIP→browser audio path works through the Call hub:
- Provider sends RTP to SipLeg's socket
- SipLeg's
onRtpReceivedfires → Call hub'sforwardRtp - Call hub calls
webrtcLeg.sendRtp(data)→ which callsforwardToBrowser() forwardToBrowsertranscodes (G.722→Opus) and sends viasender.sendRtp()(WebRTC PeerConnection)
WebRtcLeg.sendRtp() MUST feed into forwardToBrowser() (the WebRTC PeerConnection path), NOT send to a UDP address. This was a bug that caused one-way audio.
The browser→SIP direction works independently: ontrack.onReceiveRtp → forwardToSip() → transcodes → sends directly to provider's media endpoint via UDP.
SIP Protocol Library
ts/sip/ is a zero-dependency SIP protocol library. Do not add transport or timer logic there — it's purely data-level (parse/build/mutate/serialize).