diff --git a/go.sum b/go.sum index 38c820b..2d39532 100644 --- a/go.sum +++ b/go.sum @@ -231,24 +231,6 @@ github.com/xtaci/smux v1.5.57/go.mod h1:IGQ9QYrBphmb/4aTnLEcJby0TNr3NV+OslIOMrX8 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zarazaex69/gr v0.1.4 h1:7u9JRGCFqAfMKk3cG2VR9uiZRSHdCdv25ySwzK4LTOY= github.com/zarazaex69/gr v0.1.4/go.mod h1:hAk5j/s2QFlvr3bJkjPwakZENsMBx6VOUN1GwYzYoRw= -github.com/zarazaex69/j v0.0.0-20260525065402-ab14a8750587 h1:BBlIf0+5czVJKS/ti5D/UGq24qw9CLiDDev4PFB33SU= -github.com/zarazaex69/j v0.0.0-20260525065402-ab14a8750587/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525070842-7db7b32d7255 h1:SGRiNJpCvIHUrAQxPKLu55EC5iZdt1sK8t8TSmXIJ/Y= -github.com/zarazaex69/j v0.0.0-20260525070842-7db7b32d7255/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525072612-27cb999da9fc h1:LQ9YzrfcuXf4BHAgotAKBQtmW3eCegs60+YcKON9gr4= -github.com/zarazaex69/j v0.0.0-20260525072612-27cb999da9fc/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525073820-97bd0a207728 h1:KC1+P2RytHIjaEPbqdCRF3C142Edj2KJj/1O0t57pV8= -github.com/zarazaex69/j v0.0.0-20260525073820-97bd0a207728/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525074211-5b001c9a66a0 h1:KXxeiwgdxR0T0zUwdaQ+VPanFEU/abij4tc91S14xto= -github.com/zarazaex69/j v0.0.0-20260525074211-5b001c9a66a0/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525075808-d35d82587a5d h1:C5zn+QFxj5u7sdA0HOWprfjAqioPq7f61TpGeH0LCZU= -github.com/zarazaex69/j v0.0.0-20260525075808-d35d82587a5d/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525080536-4baed2627868 h1:sVQZjngeHUkhYUo4hy53BjFDGZzWhv814fN5BuaXyQc= -github.com/zarazaex69/j v0.0.0-20260525080536-4baed2627868/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525082259-730a4c448bcf h1:8Rin8fYYKH3BqLS1VUxktTCchZy3SXkZC/0B0F0qPhw= -github.com/zarazaex69/j v0.0.0-20260525082259-730a4c448bcf/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= -github.com/zarazaex69/j v0.0.0-20260525084838-37e184b41e37 h1:H7nRrEvDYOgOnZC+mA7UC65NPhbSRqKbg/ieri31rPc= -github.com/zarazaex69/j v0.0.0-20260525084838-37e184b41e37/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= github.com/zarazaex69/j v0.0.0-20260525090009-ce2ca552a46b h1:Qod8qBMKesKg+gIqL3f1yqh/v00qG/dnTEMqTkz7pTU= github.com/zarazaex69/j v0.0.0-20260525090009-ce2ca552a46b/go.mod h1:7/ypJTenOIPx23fpo5uF7l4u+rxZqg9cFbTL/N77Ktc= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= diff --git a/internal/engine/jitsi/jitsi.go b/internal/engine/jitsi/jitsi.go index a199413..0981b92 100644 --- a/internal/engine/jitsi/jitsi.go +++ b/internal/engine/jitsi/jitsi.go @@ -32,6 +32,7 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/engine" "github.com/openlibrecommunity/olcrtc/internal/logger" pioninterceptor "github.com/pion/interceptor" + "github.com/pion/rtcp" "github.com/pion/webrtc/v4" "github.com/zarazaex69/j" ) @@ -541,6 +542,17 @@ func (s *Session) negotiatePC(ctx context.Context, jSess *j.Session, sctpBridge s.pcMu.Lock() s.pc = pc s.pcMu.Unlock() + + // Start an RTCP keepalive. JVB tracks endpoint liveness via + // lastIncomingActivityInstant = max(lastRtpReceived, lastIceConsent). + // In a TURN-relay-only path, ICE consent updates can fail to reach + // JVB's lastIceActivityInstant tracker. Periodic RTCP RR packets + // guarantee lastRtpReceived is fresh and the endpoint is not expired + // after the default 1-minute inactivity timeout, which causes JVB to + // shut down the DTLS session and emit close_notify. + s.wg.Add(1) + go s.rtcpKeepalive(pc) + return nil } @@ -550,6 +562,34 @@ type negotiator interface { HandleSourceAdd(stanza string) error } +// rtcpKeepalive sends an empty RTCP Receiver Report every 5 seconds so JVB +// updates its lastRtpPacketReceivedInstant tracker for our endpoint. JVB's +// shouldExpire() check fires every minute and tears down the DTLS session +// (causing the observed CloseNotify alert) when no activity has been seen in +// more than the configured inactivityTimeout (default 1 minute). Even an +// empty RR keeps the timestamp fresh - JVB does not require the report to +// reference any specific SSRC. +func (s *Session) rtcpKeepalive(pc *webrtc.PeerConnection) { + defer s.wg.Done() + const interval = 5 * time.Second + ticker := time.NewTicker(interval) + defer ticker.Stop() + pkts := []rtcp.Packet{&rtcp.ReceiverReport{}} + for { + select { + case <-s.done: + return + case <-ticker.C: + if err := pc.WriteRTCP(pkts); err != nil { + if s.closed.Load() { + return + } + logger.Debugf("jitsi: rtcp keepalive write: %v", err) + } + } + } +} + // trickleDrainLoop reads the XMPP stanza channel and feeds any // transport-info ICE candidates into the PeerConnection. It also drains // non-jingle stanzas so the channel never fills and blocks the read loop.