mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
130 lines
3.4 KiB
Go
130 lines
3.4 KiB
Go
package tgsig
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"dev.narayana.im/narayana/telegabber/calls/audio"
|
|
"dev.narayana.im/narayana/telegabber/calls/signaling"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/zelenin/go-tdlib/client"
|
|
)
|
|
|
|
// per-call tg-side state shared by Caller and Callee
|
|
type callBase struct {
|
|
adapter *Adapter
|
|
userID int64
|
|
isVideo bool
|
|
// CallerSide / CalleeSide - lets ntg callbacks fire MediaConnected per-side
|
|
side signaling.Side
|
|
|
|
mu sync.Mutex
|
|
bridge *signaling.Bridge
|
|
callID int32
|
|
}
|
|
|
|
// must run before any callback that re-enters the bridge
|
|
func (b *callBase) Bind(br *signaling.Bridge) {
|
|
b.mu.Lock()
|
|
b.bridge = br
|
|
b.mu.Unlock()
|
|
}
|
|
|
|
func (b *callBase) getBridge() *signaling.Bridge {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.bridge
|
|
}
|
|
|
|
func (b *callBase) getCallID() int32 {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
return b.callID
|
|
}
|
|
|
|
func (b *callBase) setCallID(id int32) {
|
|
b.mu.Lock()
|
|
b.callID = id
|
|
b.mu.Unlock()
|
|
}
|
|
|
|
// audio-layer view of the ntgcalls client; nil for signaling-only tests
|
|
func (b *callBase) NtgClient() audio.NtgClient { return b.adapter.audioNtg }
|
|
|
|
// for P2P calls this is ntgcalls' "chatId" - the remote user_id
|
|
func (b *callBase) ChatID() int64 { return b.userID }
|
|
|
|
// ask tdlib to discard; map removal happens in cleanup from
|
|
// Manager.endCall once tdlib echoes Discarded.
|
|
//
|
|
// Duration matters: with 0 the call shows up as missed on other tdlib
|
|
// clients of the same account.
|
|
func (b *callBase) Terminate(reason signaling.TerminationReason) {
|
|
callID := b.getCallID()
|
|
if callID == 0 {
|
|
// CreateCall never succeeded
|
|
return
|
|
}
|
|
var duration int32
|
|
if br := b.getBridge(); br != nil {
|
|
duration = int32(br.Duration().Seconds())
|
|
}
|
|
b.adapter.withCallContext(func() {
|
|
if _, err := b.adapter.tdlib.DiscardCall(&client.DiscardCallRequest{
|
|
CallId: callID,
|
|
IsDisconnected: reason == signaling.ReasonMediaFailed,
|
|
Duration: duration,
|
|
IsVideo: b.isVideo,
|
|
ConnectionId: 0,
|
|
}); err != nil {
|
|
log.WithFields(log.Fields{
|
|
"call_id": callID,
|
|
"user_id": b.userID,
|
|
"reason": reason,
|
|
"duration": duration,
|
|
}).WithError(err).Error("DiscardCall failed")
|
|
}
|
|
})
|
|
}
|
|
|
|
// drives the ntgcalls P2P setup on tdlib's CallStateReady
|
|
func (b *callBase) onReady(state *client.CallStateReady, isOutgoing bool, entry *log.Entry) {
|
|
b.adapter.withCallContext(func() {
|
|
a := b.adapter
|
|
if err := a.ntg.CreateP2PCall(b.userID); err != nil {
|
|
entry.WithError(err).Error("Failed to create P2P call")
|
|
return
|
|
}
|
|
if err := a.ntg.SkipExchange(b.userID, state.EncryptionKey, isOutgoing); err != nil {
|
|
entry.WithError(err).Error("Failed to skip exchange")
|
|
_ = a.ntg.Stop(b.userID)
|
|
return
|
|
}
|
|
rtcServers := convertCallServers(state.Servers)
|
|
versions := state.Protocol.LibraryVersions
|
|
if err := a.ntg.ConnectP2P(b.userID, rtcServers, versions, state.AllowP2p); err != nil {
|
|
entry.WithError(err).Error("Failed to connect P2P")
|
|
_ = a.ntg.Stop(b.userID)
|
|
return
|
|
}
|
|
entry.Info("P2P connection established")
|
|
})
|
|
}
|
|
|
|
// drops the side from both maps and stops ntgcalls
|
|
func (b *callBase) cleanup() {
|
|
b.adapter.withCallContext(func() {
|
|
a := b.adapter
|
|
callID := b.getCallID()
|
|
if callID != 0 {
|
|
a.mgr.Drop(a.jid, callID)
|
|
}
|
|
a.dropSide(b.userID)
|
|
if err := a.ntg.Stop(b.userID); err != nil {
|
|
log.WithFields(log.Fields{
|
|
"call_id": callID,
|
|
"user_id": b.userID,
|
|
}).WithError(err).Debug("ntgcalls Stop returned error (call may not have started)")
|
|
}
|
|
})
|
|
}
|