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.
42 lines
2.2 KiB
Markdown
42 lines
2.2 KiB
Markdown
# 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 routing
|
|
- `ts/call/call.ts` — the hub: owns legs, media forwarding
|
|
- `ts/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 pool
|
|
- `ts/sipproxy.ts` — thin bootstrap wiring everything together
|
|
- `ts/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:
|
|
|
|
1. `POST /api/call` with browser deviceId → CallManager creates Call, saves pending state, notifies browser via `webrtc-incoming`
|
|
2. Browser sends `webrtc-offer` (with its own `sessionId`) → CallManager creates a **standalone** WebRtcLeg (NOT attached to any call yet)
|
|
3. Browser sends `webrtc-accept` (with `callId` + `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:
|
|
|
|
1. Provider sends RTP to SipLeg's socket
|
|
2. SipLeg's `onRtpReceived` fires → Call hub's `forwardRtp`
|
|
3. Call hub calls `webrtcLeg.sendRtp(data)` → which calls `forwardToBrowser()`
|
|
4. `forwardToBrowser` transcodes (G.722→Opus) and sends via `sender.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).
|