Zippytal-Node/webrtcCallManager.go
2021-12-08 15:58:40 +01:00

846 lines
25 KiB
Go

package localserver
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
sync "sync"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/pion/rtcp"
"github.com/pion/webrtc/v3"
)
const (
NAME = "name"
ID = "ID"
SDP = "sdp"
CANDIDATE = "webrtc_candidate"
SQUAD_ID = "squadId"
FROM = "from"
TO = "to"
STOP_CALL = "stop_call"
LIST_HOSTED_SQUADS_BY_HOST = "list_hosted_squads_by_host"
)
type Squad struct {
ID string
Members []string
}
type WebRTCCallManager struct {
stream GrpcManager_LinkClient
middlewares []WebrtcCallEventManager
ID string
LocalSD map[string]*webrtc.SessionDescription
RTCPeerConnections map[string]*RTCPeerConnection
AudioTransceiver map[string][]*PeerSender
VideoTransceiver map[string][]*PeerSender
DataChannels map[string]*DataChannel
PendingCandidates map[string][]*webrtc.ICECandidate
RemoteTracks map[string][]*RemoteTrack
Squads map[string]*Squad
SquadMapMux *sync.RWMutex
CandidateChannel chan *IncomingCandidate
CandidateMux *sync.RWMutex
RemoteTracksMux *sync.RWMutex
RTCPeerConnectionMapMux *sync.RWMutex
DataChannelMapMux *sync.RWMutex
LocalSDMapMux *sync.RWMutex
AudioSenderMux *sync.RWMutex
VideoSenderMux *sync.RWMutex
}
type IncomingCandidate struct {
For string
Candidate *webrtc.ICECandidateInit
}
type RTCPeerConnection struct {
*webrtc.PeerConnection
makingOffer bool
negotiate func(string, string)
makingOfferLock *sync.Mutex
}
type DataChannel struct {
DataChannel *webrtc.DataChannel
bufferedAmountLowThresholdReached <-chan struct{}
l *int32
}
type PeerSender struct {
ID string
Transceiver *webrtc.RTPTransceiver
Sender *webrtc.RTPSender
}
type CallEvent struct {
EventId string `json:"eventId"`
From string `json:"from"`
Data []byte `json:"data"`
Payload map[string]interface{} `json:"payload"`
}
type RemoteTrack struct {
ID string
Track *webrtc.TrackLocalStaticRTP
rdv *int32
}
type OnICECandidateFunc func(string, *webrtc.ICECandidate) error
func NewWebRTCCallManager(id string, token string, eventHandlers ...WebrtcCallEventManager) (webRTCCallManager *WebRTCCallManager, err error) {
squadList, err := loadHostedSquads(token, id)
if err != nil {
return
}
squads := make(map[string]*Squad)
for _, squad := range squadList {
squads[squad.ID] = squad
}
logger.Println(squads)
webRTCCallManager = &WebRTCCallManager{
middlewares: eventHandlers,
ID: id,
AudioTransceiver: make(map[string][]*PeerSender),
VideoTransceiver: make(map[string][]*PeerSender),
DataChannels: make(map[string]*DataChannel),
PendingCandidates: make(map[string][]*webrtc.ICECandidate),
LocalSD: make(map[string]*webrtc.SessionDescription),
RTCPeerConnections: make(map[string]*RTCPeerConnection),
RemoteTracks: make(map[string][]*RemoteTrack),
Squads: squads,
SquadMapMux: &sync.RWMutex{},
RTCPeerConnectionMapMux: &sync.RWMutex{},
LocalSDMapMux: &sync.RWMutex{},
CandidateMux: &sync.RWMutex{},
DataChannelMapMux: &sync.RWMutex{},
AudioSenderMux: &sync.RWMutex{},
VideoSenderMux: &sync.RWMutex{},
RemoteTracksMux: &sync.RWMutex{},
}
return
}
func loadHostedSquads(token string, hostId string) (squads []*Squad, err error) {
body, err := json.Marshal(map[string]interface{}{
"type": LIST_HOSTED_SQUADS_BY_HOST,
"token": token,
"from": hostId,
"payload": map[string]string{
"host": hostId,
"lastIndex": "0",
},
})
if err != nil {
return
}
res, err := http.Post("https://app.zippytal.com/req", "application/json", bytes.NewBuffer(body))
if err != nil {
logger.Println("error come from there in webrtc call manager")
return
}
bs, err := io.ReadAll(res.Body)
if err != nil {
return
}
err = json.Unmarshal(bs, &squads)
return
}
func (wm *WebRTCCallManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
peerConnection, err := wm.createPeerConnection(target, from, "aa5da62f-2800-4dd5-bf86-423cda048120", webrtc.SDPTypeOffer, cb)
if err != nil {
return
}
logger.Println("connection created")
rawOffer, err := peerConnection.CreateOffer(nil)
if err != nil {
return
}
if err = peerConnection.SetLocalDescription(rawOffer); err != nil {
return
}
wm.RTCPeerConnectionMapMux.Lock()
logger.Println("adding for target", target)
wm.RTCPeerConnections[target] = &RTCPeerConnection{
PeerConnection: peerConnection,
makingOffer: true,
makingOfferLock: &sync.Mutex{},
negotiate: wm.negotiate,
}
wm.RTCPeerConnectionMapMux.Unlock()
err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_OFFER),
From: wm.ID,
Token: "none",
Payload: map[string]string{
"to": target,
"from": wm.ID,
"sdp": rawOffer.SDP,
},
})
return
}
func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
done, errCh := make(chan struct{}), make(chan error)
go func() {
if _, ok := wm.Squads[req[SQUAD_ID]]; !ok {
err = fmt.Errorf("no corresponding squad")
errCh <- err
return
}
peerConnection, err := wm.createPeerConnection(req[FROM], req[TO], req[SQUAD_ID], webrtc.SDPTypeAnswer, cb)
if err != nil {
errCh <- err
return
}
wm.RTCPeerConnectionMapMux.Lock()
wm.RTCPeerConnections[req[FROM]] = &RTCPeerConnection{
PeerConnection: peerConnection,
makingOffer: false,
makingOfferLock: &sync.Mutex{},
negotiate: wm.negotiate,
}
wm.RTCPeerConnectionMapMux.Unlock()
offer := webrtc.SessionDescription{
Type: webrtc.SDPTypeOffer,
SDP: req[SDP],
}
if err = peerConnection.SetRemoteDescription(offer); err != nil {
errCh <- err
return
}
rawAnswer, err := peerConnection.CreateAnswer(nil)
if err != nil {
errCh <- err
return
}
wm.LocalSDMapMux.Lock()
wm.LocalSD[req[FROM]] = &rawAnswer
wm.LocalSDMapMux.Unlock()
wm.SquadMapMux.Lock()
wm.Squads[req[SQUAD_ID]].Members = append(wm.Squads[req[SQUAD_ID]].Members, req[FROM])
wm.SquadMapMux.Unlock()
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_ANSWER),
From: wm.ID,
Token: "none",
Payload: map[string]string{
"to": req[FROM],
"from": wm.ID,
"sdp": rawAnswer.SDP,
},
}); err != nil {
errCh <- err
return
}
done <- struct{}{}
}()
select {
case <-done:
return
case err = <-errCh:
return
case <-ctx.Done():
err = ctx.Err()
return
}
}
func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) {
wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock()
defer func() {
if r := recover(); err != nil {
logger.Printf("recover from panic in handle answer : %v\n", r)
}
}()
if _, ok := wm.RTCPeerConnections[req[FROM]]; !ok {
err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM])
return
}
peerConnnection := wm.RTCPeerConnections[req[FROM]]
logger.Println("---------------------")
logger.Println(req[SDP])
logger.Println("---------------------")
if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{
Type: webrtc.SDPTypeAnswer,
SDP: req[SDP],
}); err != nil {
logger.Println("error occured while setting remote description in handle answer")
return
}
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER),
From: wm.ID,
Token: "none",
Payload: map[string]string{
"from": wm.ID,
"to": req[FROM],
},
}); err != nil {
return
}
wm.CandidateMux.Lock()
for _, candidate := range wm.PendingCandidates[req[FROM]] {
logger.Println("sending candidate from answer to", req[FROM])
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
From: wm.ID,
Token: "none",
Payload: map[string]string{
"from": wm.ID,
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil {
wm.CandidateMux.Unlock()
return
}
}
wm.CandidateMux.Unlock()
wm.CandidateMux.Lock()
delete(wm.PendingCandidates, req[FROM])
wm.CandidateMux.Unlock()
wm.LocalSDMapMux.Lock()
delete(wm.LocalSD, req[FROM])
wm.LocalSDMapMux.Unlock()
return
}
func (wm *WebRTCCallManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
wm.RTCPeerConnectionMapMux.Lock()
if _, ok := wm.RTCPeerConnections[req[FROM]]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
wm.RTCPeerConnectionMapMux.Unlock()
return
}
logger.Println("handling counter offer")
connection := wm.RTCPeerConnections[req[FROM]]
wm.RTCPeerConnectionMapMux.Unlock()
wm.LocalSDMapMux.Lock()
if err = connection.SetLocalDescription(*wm.LocalSD[req[FROM]]); err != nil {
wm.LocalSDMapMux.Unlock()
return
}
wm.LocalSDMapMux.Unlock()
wm.CandidateMux.Lock()
for _, candidate := range wm.PendingCandidates[req[FROM]] {
logger.Println("sending candidate to", req[FROM])
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
From: wm.ID,
Token: "none",
Payload: map[string]string{
"from": wm.ID,
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil {
wm.CandidateMux.Unlock()
return
}
}
delete(wm.PendingCandidates, req[FROM])
wm.CandidateMux.Unlock()
wm.LocalSDMapMux.Lock()
delete(wm.LocalSD, req[FROM])
wm.LocalSDMapMux.Unlock()
return
}
func (wm *WebRTCCallManager) createPeerConnection(target string, from string, squadId string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
defer func() {
if r := recover(); err != nil {
logger.Printf("recover from panic : %v\n", r)
}
}()
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"},
},
},
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
}
peerConnection, err = webrtc.NewPeerConnection(config)
if err != nil {
return
}
logger.Println("---------------------------------------------------")
if peerType == webrtc.SDPTypeAnswer {
maxRetransmits := uint16(100)
channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits,
})
if err != nil {
return nil, err
}
channel.OnOpen(func() {
logger.Println("channel opened")
if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
logger.Println(chanErr)
}
})
channel.OnMessage(func(msg webrtc.DataChannelMessage) {
var event CallEvent
if err := json.Unmarshal(msg.Data, &event); err != nil {
logger.Println(err)
return
}
for _, handler := range wm.middlewares {
if err := handler.HandleCallEvent(event.From, squadId, event.EventId, event.Payload, event.Data, wm); err != nil {
logger.Println(err)
continue
}
}
})
logger.Println("new channel for target : ", target)
channel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold)
channel.OnBufferedAmountLow(func() {
})
wm.DataChannelMapMux.Lock()
logger.Println(target)
l := int32(0)
wm.DataChannels[target] = &DataChannel{
DataChannel: channel,
l: &l,
}
wm.DataChannelMapMux.Unlock()
} else {
peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
wm.DataChannelMapMux.Lock()
l := int32(0)
wm.DataChannels[target] = &DataChannel{
DataChannel: dc,
l: &l,
}
wm.DataChannelMapMux.Unlock()
dc.OnOpen(func() {
logger.Printf("got a new open datachannel %s\n", dc.Label())
})
dc.OnMessage(func(msg webrtc.DataChannelMessage) {
var event CallEvent
if err := json.Unmarshal(msg.Data, &event); err != nil {
logger.Println(err)
return
}
for _, handler := range wm.middlewares {
if err := handler.HandleCallEvent(event.From, squadId, event.EventId, event.Payload, event.Data, wm); err != nil {
logger.Println(err)
continue
}
}
})
})
}
wm.RemoteTracksMux.RLock()
for _, id := range wm.Squads[squadId].Members {
if id != target {
<-time.NewTimer(time.Millisecond * 50).C
if _, ok := wm.RemoteTracks[id]; !ok {
continue
}
for _, track := range wm.RemoteTracks[id] {
transceiver, err := peerConnection.AddTransceiverFromKind(track.Track.Kind(), webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
if err != nil {
logger.Println("add track error")
continue
}
if err := transceiver.Sender().ReplaceTrack(track.Track); err != nil {
logger.Println("add track error")
continue
}
if track.Track.Kind() == webrtc.RTPCodecTypeVideo {
wm.VideoSenderMux.Lock()
if len(wm.VideoTransceiver) == 0 {
wm.VideoTransceiver[id] = []*PeerSender{{ID: target, Transceiver: transceiver}}
} else {
wm.VideoTransceiver[id] = append(wm.VideoTransceiver[id], &PeerSender{ID: target, Transceiver: transceiver})
}
wm.VideoSenderMux.Unlock()
} else if track.Track.Kind() == webrtc.RTPCodecTypeAudio {
wm.AudioSenderMux.Lock()
if len(wm.AudioTransceiver) == 0 {
wm.AudioTransceiver[id] = []*PeerSender{{ID: target, Transceiver: transceiver}}
} else {
wm.AudioTransceiver[id] = append(wm.AudioTransceiver[id], &PeerSender{ID: target, Transceiver: transceiver})
}
wm.AudioSenderMux.Unlock()
}
logger.Println("track added", track)
}
}
}
wm.RemoteTracksMux.RUnlock()
peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed {
logger.Println(pcs)
wm.HandleLeavingMember(target, squadId)
}
})
peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
logger.Printf("ICE connection state has changed %s\n", is.String())
if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
logger.Println(is)
}
})
peerConnection.OnTrack(func(tr *webrtc.TrackRemote, r *webrtc.RTPReceiver) {
logger.Println("got new track")
defer func() {
if stopErr := r.Stop(); stopErr != nil {
logger.Println(stopErr)
}
}()
go func() {
ticker := time.NewTicker(1500 * time.Millisecond)
for range ticker.C {
if rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(tr.SSRC())}}); rtcpSendErr != nil {
logger.Println(rtcpSendErr)
break
}
}
}()
uniqId := uuid.New()
i := fmt.Sprintf("%s/%s", target, uniqId.String())
localTrack, newTrackErr := webrtc.NewTrackLocalStaticRTP(tr.Codec().RTPCodecCapability, i, i)
if newTrackErr != nil {
return
}
logger.Println(localTrack)
rtpbuf := make([]byte, 1400)
flag := int32(0)
remote := &RemoteTrack{ID: target, Track: localTrack, rdv: &flag}
wm.RemoteTracksMux.Lock()
if len(wm.RemoteTracks[target]) == 0 {
wm.RemoteTracks[target] = []*RemoteTrack{remote}
} else {
wm.RemoteTracks[target] = append(wm.RemoteTracks[target], remote)
}
index := len(wm.RemoteTracks[target])
logger.Println(index, wm.RemoteTracks)
wm.RemoteTracksMux.Unlock()
wm.SquadMapMux.RLock()
for _, id := range wm.Squads[squadId].Members {
if id != target {
if _, ok := wm.RTCPeerConnections[id]; !ok {
continue
}
connection := wm.RTCPeerConnections[id]
transceiver, tranceiverErr := connection.AddTransceiverFromKind(localTrack.Kind(), webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
if tranceiverErr != nil {
logger.Println(tranceiverErr)
continue
}
if replaceTrackErr := transceiver.Sender().ReplaceTrack(localTrack); replaceTrackErr != nil {
logger.Println(replaceTrackErr)
continue
}
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := transceiver.Sender().Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
if localTrack.Kind() == webrtc.RTPCodecTypeAudio {
wm.AudioSenderMux.Lock()
if len(wm.AudioTransceiver) == 0 {
wm.AudioTransceiver[target] = []*PeerSender{{ID: id, Transceiver: transceiver}}
} else {
wm.AudioTransceiver[target] = append(wm.AudioTransceiver[target], &PeerSender{ID: id, Transceiver: transceiver})
}
wm.AudioSenderMux.Unlock()
} else if localTrack.Kind() == webrtc.RTPCodecTypeVideo {
wm.VideoSenderMux.Lock()
if len(wm.VideoTransceiver) == 0 {
wm.VideoTransceiver[target] = []*PeerSender{{ID: id, Transceiver: transceiver}}
} else {
wm.VideoTransceiver[target] = append(wm.VideoTransceiver[target], &PeerSender{ID: id, Transceiver: transceiver})
}
wm.VideoSenderMux.Unlock()
}
go func() {
<-time.NewTimer(time.Second).C
connection.negotiate(id, squadId)
}()
}
}
wm.SquadMapMux.RUnlock()
for {
i, _, readErr := tr.Read(rtpbuf)
if readErr != nil {
logger.Println(readErr)
break
}
f := atomic.LoadInt32(remote.rdv)
if f == 0 {
if _, writeErr := localTrack.Write(rtpbuf[:i]); writeErr != nil && !errors.Is(writeErr, io.ErrClosedPipe) {
logger.Println(writeErr)
break
} else {
_ = rtpbuf[:i]
}
}
}
})
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
if i == nil {
return
}
wm.CandidateMux.Lock()
defer wm.CandidateMux.Unlock()
desc := peerConnection.RemoteDescription()
if desc == nil {
logger.Println("generated candidate appended to list : ", i)
wm.PendingCandidates[target] = append(wm.PendingCandidates[target], i)
} else {
logger.Println("generated candidate : ", i)
if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
logger.Println(iceCandidateErr)
}
}
})
return
}
func (wm *WebRTCCallManager) HandleRennegotiationOffer(from string, sdp string) (err error) {
wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock()
if _, ok := wm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
wm.RTCPeerConnections[from].makingOfferLock.Lock()
if wm.RTCPeerConnections[from].makingOffer {
wm.RTCPeerConnections[from].makingOfferLock.Unlock()
return fmt.Errorf("already making an offer or state is stable")
}
wm.RTCPeerConnections[from].makingOfferLock.Unlock()
if err = wm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return
}
localSd, err := wm.RTCPeerConnections[from].CreateAnswer(nil)
if err != nil {
return
}
if err = wm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return
}
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER),
From: wm.ID,
Token: "",
Payload: map[string]string{
"to": from,
"sdp": localSd.SDP,
},
}); err != nil {
logger.Println(err)
return
}
return
}
func (wm *WebRTCCallManager) HandleRennegotiationAnswer(from string, sdp string) (err error) {
wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock()
wm.RTCPeerConnections[from].makingOfferLock.Lock()
if wm.RTCPeerConnections[from].makingOffer {
wm.RTCPeerConnections[from].makingOfferLock.Unlock()
return fmt.Errorf("already making an offer or state is stable")
}
wm.RTCPeerConnections[from].makingOfferLock.Unlock()
if _, ok := wm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
err = wm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
return
}
func (wm *WebRTCCallManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock()
if candidate != nil {
err = wm.RTCPeerConnections[from].AddICECandidate(*candidate)
}
return
}
func (wm *WebRTCCallManager) HandleLeavingMember(id string, squadId string) {
wm.RTCPeerConnectionMapMux.Lock()
if _, ok := wm.RTCPeerConnections[id]; !ok {
wm.RTCPeerConnectionMapMux.Unlock()
return
}
wm.RTCPeerConnectionMapMux.Unlock()
defer func() {
wm.RTCPeerConnectionMapMux.Lock()
if _, ok := wm.RTCPeerConnections[id]; ok {
if closeErr := wm.RTCPeerConnections[id].Close(); closeErr != nil {
logger.Println("peer connection close error", closeErr)
}
}
delete(wm.RTCPeerConnections, id)
wm.RTCPeerConnectionMapMux.Unlock()
}()
logger.Printf("peer %s is leaving the squad\n", id)
wm.DataChannelMapMux.Lock()
if _, ok := wm.DataChannels[id]; ok {
wm.DataChannels[id].DataChannel.Close()
}
delete(wm.DataChannels, id)
wm.DataChannelMapMux.Unlock()
wm.LocalSDMapMux.Lock()
delete(wm.LocalSD, id)
wm.LocalSDMapMux.Unlock()
delete(wm.PendingCandidates, id)
wm.RemoteTracksMux.Lock()
delete(wm.RemoteTracks, id)
wm.RemoteTracksMux.Unlock()
wm.AudioSenderMux.Lock()
for peerId, peerSender := range wm.AudioTransceiver {
if peerId != id {
logger.Println("senders", peerSender)
c := 0
for i, sender := range peerSender {
if sender.ID == id {
if senderErr := sender.Transceiver.Sender().Stop(); senderErr != nil {
logger.Println(senderErr)
}
if transceiverErr := sender.Transceiver.Stop(); transceiverErr != nil {
logger.Println("transceiverErr occured with video", transceiverErr)
}
peerSender[len(peerSender)-i-1], peerSender[i] = peerSender[i], peerSender[len(peerSender)-i-1]
c++
}
}
wm.AudioTransceiver[peerId] = wm.AudioTransceiver[peerId][:len(peerSender)-(c)]
logger.Println(wm.AudioTransceiver[peerId])
}
}
for _, transceiver := range wm.AudioTransceiver[id] {
if senderErr := transceiver.Transceiver.Sender().Stop(); senderErr != nil {
logger.Println(senderErr)
}
if stopErr := transceiver.Transceiver.Stop(); stopErr != nil {
logger.Println("transceiver audio stop error", stopErr)
}
}
delete(wm.AudioTransceiver, id)
wm.AudioSenderMux.Unlock()
wm.VideoSenderMux.Lock()
for peerId, peerSender := range wm.VideoTransceiver {
if peerId != id {
c := 0
logger.Println("senders", peerSender)
for i, sender := range peerSender {
if sender.ID == id {
if senderErr := sender.Transceiver.Sender().Stop(); senderErr != nil {
logger.Println(senderErr)
}
if transceiverErr := sender.Transceiver.Stop(); transceiverErr != nil {
logger.Println("transceiverErr occured with video", transceiverErr)
}
peerSender[len(peerSender)-i-1], peerSender[i] = peerSender[i], peerSender[len(peerSender)-i-1]
c++
}
}
wm.VideoTransceiver[peerId] = wm.VideoTransceiver[peerId][:len(peerSender)-(c)]
logger.Println(wm.VideoTransceiver[peerId])
}
}
for _, transceiver := range wm.VideoTransceiver[id] {
if senderErr := transceiver.Transceiver.Sender().Stop(); senderErr != nil {
logger.Println(senderErr)
}
if stopErr := transceiver.Transceiver.Stop(); stopErr != nil {
logger.Println("transceiver video stop error", stopErr)
}
}
delete(wm.VideoTransceiver, id)
wm.VideoSenderMux.Unlock()
if _, ok := wm.Squads[squadId]; ok {
wm.SquadMapMux.Lock()
var index int
for i, v := range wm.Squads[squadId].Members {
if v == id {
index = i
break
}
}
if len(wm.Squads[squadId].Members) < 2 {
wm.Squads[squadId].Members = []string{}
} else {
wm.Squads[squadId].Members = append(wm.Squads[squadId].Members[:index], wm.Squads[squadId].Members[index+1:]...)
}
wm.SquadMapMux.Unlock()
}
}
func (wm *WebRTCCallManager) negotiate(target string, squadId string) {
wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock()
if _, ok := wm.RTCPeerConnections[target]; !ok {
return
}
wm.RTCPeerConnections[target].makingOfferLock.Lock()
wm.RTCPeerConnections[target].makingOffer = true
wm.RTCPeerConnections[target].makingOfferLock.Unlock()
defer func() {
wm.RTCPeerConnections[target].makingOfferLock.Lock()
wm.RTCPeerConnections[target].makingOffer = false
wm.RTCPeerConnections[target].makingOfferLock.Unlock()
}()
wm.SquadMapMux.RLock()
defer wm.SquadMapMux.RUnlock()
for _, id := range wm.Squads[squadId].Members {
if _, ok := wm.RTCPeerConnections[id]; !ok {
continue
}
connection := wm.RTCPeerConnections[id]
if connection.SignalingState() == webrtc.SignalingStateStable {
localSd, err := connection.CreateOffer(nil)
if err != nil {
logger.Println(err)
return
}
if err = connection.SetLocalDescription(localSd); err != nil {
logger.Println(err)
return
}
if err = wm.stream.Send(&Request{
Type: string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER),
From: wm.ID,
Token: "",
Payload: map[string]string{
"to": id,
"sdp": localSd.SDP,
},
}); err != nil {
logger.Println(err)
return
}
}
}
}