telegabber/calls/signaling/xmppsig/base.go
2026-05-27 08:37:47 -07:00

128 lines
2.7 KiB
Go

package xmppsig
import (
"sync"
"dev.narayana.im/narayana/telegabber/calls/signaling"
"dev.narayana.im/narayana/telegabber/xmpp/jingle"
"github.com/pion/webrtc/v4"
)
// per-call state shared by Caller and Callee
type xmppBase struct {
a *Adapter
remote string
// full JID we act as on the wire; falls back to a.cfg.LocalJID
localJID string
// CallerSide/CalleeSide for Bind's MediaConnected wiring
side signaling.Side
mu sync.Mutex
sid string
session *jingle.Session
bridge *signaling.Bridge
pc *webrtc.PeerConnection
remoteSDPSeen bool
onRemoteSDP func(sdp string)
// pion fires OnTrack during SetRemoteDescription, before any handler
// the orchestrator might want to install - so hookTrack runs a permanent
// forwarder at PC construction and buffers into pendingTracks until
// SetTrackHandler arrives
onTrackFn func(t *webrtc.TrackRemote)
pendingTracks []*webrtc.TrackRemote
}
func (b *xmppBase) set(sid string, sess *jingle.Session) {
b.mu.Lock()
b.sid, b.session = sid, sess
b.mu.Unlock()
}
// attaches the bridge and wires pion's connection-state callback
func (b *xmppBase) Bind(br *signaling.Bridge) {
b.mu.Lock()
b.bridge = br
pc := b.pc
side := b.side
b.mu.Unlock()
hookPCState(pc, br, side)
}
func (b *xmppBase) bridgeRef() *signaling.Bridge {
b.mu.Lock()
defer b.mu.Unlock()
return b.bridge
}
func (b *xmppBase) sessionRef() *jingle.Session {
b.mu.Lock()
defer b.mu.Unlock()
return b.session
}
func (b *xmppBase) cleanup() {
b.mu.Lock()
sid := b.sid
b.mu.Unlock()
if sid == "" {
return
}
b.a.cfg.Manager.Unregister(sid)
}
func (b *xmppBase) PeerConnection() *webrtc.PeerConnection {
b.mu.Lock()
defer b.mu.Unlock()
return b.pc
}
func (b *xmppBase) SetOnRemoteSDP(fn func(sdp string)) {
b.mu.Lock()
b.onRemoteSDP = fn
b.mu.Unlock()
}
// must run before the first SetRemoteDescription, or pion's first
// OnTrack event is lost
func (b *xmppBase) hookTrack(pc *webrtc.PeerConnection) {
pc.OnTrack(func(t *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
b.mu.Lock()
fn := b.onTrackFn
if fn == nil {
b.pendingTracks = append(b.pendingTracks, t)
b.mu.Unlock()
return
}
b.mu.Unlock()
fn(t)
})
}
// swaps in the real handler and drains buffered tracks
func (b *xmppBase) SetTrackHandler(fn func(t *webrtc.TrackRemote)) {
b.mu.Lock()
b.onTrackFn = fn
pending := b.pendingTracks
b.pendingTracks = nil
b.mu.Unlock()
for _, t := range pending {
fn(t)
}
}
// fires the orchestrator hook exactly once per call
func (b *xmppBase) onRemoteDescription(sdp string) {
b.mu.Lock()
if b.remoteSDPSeen {
b.mu.Unlock()
return
}
b.remoteSDPSeen = true
hook := b.onRemoteSDP
b.mu.Unlock()
if hook != nil {
hook(sdp)
}
}