Group voice and video calls with MatrixRTC and LiveKit
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
Every call in the web client now goes through MatrixRTC with a LiveKit
SFU, the model Element Call and Element X use; the legacy 1:1 m.call.*
calls are gone. In a room of two the other person is rung and can answer
or decline; in a bigger room a banner offers to join the call. Calls show
a grid of participant tiles with mute, camera, screen sharing and leave,
and media is end-to-end encrypted in encrypted rooms.
Server:
- MSC4140 delayed events (send/state with a delay, plus the
delayed_events list/restart/cancel/send API), stored and scheduled, so
a member whose client drops off is taken out of the call.
- POST /user/{userId}/openid/request_token, which lk-jwt-service uses to
hand out LiveKit tokens.
- org.matrix.msc4143.rtc_foci in /.well-known/matrix/client, configured
under client_api.rtc.
- Room presets let members send call membership state.
- Migration 0014 for both SQL dialects.
Deploy: LiveKit and lk-jwt-service in compose (calls.yml) and the helm
chart. Docs: docs/calls.md, configuration and deployment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
325431a5be
commit
76dfc98a86
49 files changed
+3849
-432
No files matched your search
@@ -182,6 +182,39 @@ type ClientAPI struct {
|
||||
// TURN is what GET /voip/turnServer hands clients for voice and video
|
||||
// calls.
|
||||
TURN TURN `yaml:"turn"`
|
||||
|
||||
// RTC is the media backend for group calls (MatrixRTC), advertised in
|
||||
// /.well-known/matrix/client.
|
||||
RTC RTC `yaml:"rtc"`
|
||||
|
||||
// DelayedEvents bounds the delayed events (MSC4140) a client may
|
||||
// schedule. Group calls depend on them: a member's "I left" is
|
||||
// scheduled in advance, so a client that disappears still leaves.
|
||||
DelayedEvents DelayedEvents `yaml:"delayed_events"`
|
||||
}
|
||||
|
||||
// RTC configures group calls. Warp does not carry call media itself:
|
||||
// MatrixRTC clients publish to a LiveKit SFU, and get the token for it from
|
||||
// a LiveKit JWT service (lk-jwt-service) in exchange for an OpenID token
|
||||
// from this server. With no service URL no focus is advertised, and clients
|
||||
// that need one cannot start a group call.
|
||||
type RTC struct {
|
||||
// LiveKitServiceURL is the base URL of the LiveKit JWT service, e.g.
|
||||
// "https://livekit-jwt.example.org". It is advertised as an
|
||||
// org.matrix.msc4143.rtc_foci entry of type livekit.
|
||||
LiveKitServiceURL string `yaml:"livekit_service_url" env:"WARP_RTC_LIVEKIT_SERVICE_URL"`
|
||||
}
|
||||
|
||||
// DelayedEvents configures MSC4140 delayed events.
|
||||
type DelayedEvents struct {
|
||||
// MaxDelay is the longest delay a client may ask for. Zero turns
|
||||
// delayed events off: the delay parameter is refused and the feature
|
||||
// is not advertised.
|
||||
MaxDelay time.Duration `yaml:"max_delay" env:"WARP_DELAYED_EVENTS_MAX_DELAY"`
|
||||
|
||||
// MaxPerUser caps how many delayed events one user may have scheduled
|
||||
// at once.
|
||||
MaxPerUser int `yaml:"max_per_user" env:"WARP_DELAYED_EVENTS_MAX_PER_USER"`
|
||||
}
|
||||
|
||||
// TURN configures the relay servers clients use to set up calls. With no
|
||||
@@ -531,6 +564,10 @@ func Default() *Config {
|
||||
Media: RateLimitBucket{Burst: 100, Interval: 20 * time.Millisecond},
|
||||
},
|
||||
TURN: TURN{UserLifetime: time.Hour},
|
||||
// A MatrixRTC client restarts its delayed leave every few
|
||||
// seconds; a day leaves room for other uses without letting a
|
||||
// forgotten event linger for weeks.
|
||||
DelayedEvents: DelayedEvents{MaxDelay: 24 * time.Hour, MaxPerUser: 100},
|
||||
},
|
||||
Sync: Sync{
|
||||
Presence: false,
|
||||
@@ -714,6 +751,17 @@ func (c *Config) Validate() error {
|
||||
if c.ClientAPI.TURN.SharedSecret != "" && c.ClientAPI.TURN.UserLifetime <= 0 {
|
||||
bad("client_api.turn.user_lifetime: must be positive")
|
||||
}
|
||||
if raw := c.ClientAPI.RTC.LiveKitServiceURL; raw != "" {
|
||||
if u, err := url.Parse(raw); err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
|
||||
bad("client_api.rtc.livekit_service_url: must be an http(s) URL, got %q", raw)
|
||||
}
|
||||
}
|
||||
if c.ClientAPI.DelayedEvents.MaxDelay < 0 {
|
||||
bad("client_api.delayed_events.max_delay: must not be negative")
|
||||
}
|
||||
if c.ClientAPI.DelayedEvents.MaxDelay > 0 && c.ClientAPI.DelayedEvents.MaxPerUser < 1 {
|
||||
bad("client_api.delayed_events.max_per_user: must be at least 1")
|
||||
}
|
||||
if c.Sync.TypingTimeout <= 0 {
|
||||
bad("sync.typing_timeout: must be positive")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user