Исправление Race при renegotiation
This commit is contained in:
@@ -59,7 +59,7 @@ func Bootstrap() {
|
||||
// TURN сервер выключен в конфиге, что может влиять на соединение некоторых пользователей
|
||||
logger.LogInfoMessage("starting without TURN server, peer connections may fail if clients are behind symmetric NATs")
|
||||
}
|
||||
logger.LogInfoMessage("server started at x.x.x.x:" + port)
|
||||
logger.LogInfoMessage("server SFU started at x.x.x.x:" + port)
|
||||
http.ListenAndServe(":"+port, nil)
|
||||
}
|
||||
|
||||
@@ -116,13 +116,14 @@ func OnRoomDelete(roomID string, server *connection.Connection) {
|
||||
server.WriteBinary(buffer.Bytes())
|
||||
}
|
||||
|
||||
func OnPeerDisconnected(roomID string, peerID string, server *connection.Connection) {
|
||||
buffer := bytebuffer.Allocate(1 + 4 + len([]byte(roomID)) + 4 + len([]byte(peerID)))
|
||||
func OnPeerDisconnected(roomID string, peerID string, server *connection.Connection, reason sfu.DisconnectReason) {
|
||||
buffer := bytebuffer.Allocate(1 + 4 + len([]byte(roomID)) + 4 + len([]byte(peerID)) + 4)
|
||||
buffer.Put(byte(network.ON_PEER_DISCONNECTED))
|
||||
buffer.PutUint32(uint32(len([]byte(roomID))))
|
||||
buffer.PutBytes([]byte(roomID))
|
||||
buffer.PutUint32(uint32(len([]byte(peerID))))
|
||||
buffer.PutBytes([]byte(peerID))
|
||||
buffer.PutUint32(uint32(reason))
|
||||
buffer.Flip()
|
||||
server.WriteBinary(buffer.Bytes())
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func LogInfoMessage(message string) {
|
||||
fmt.Printf("%s[g365sfu] %s[%s]%s %s[INFO]%s %s\n",
|
||||
colorBlue,
|
||||
colorGray, timestamp, colorReset,
|
||||
colorGreen, colorReset,
|
||||
colorCyan, colorReset,
|
||||
message,
|
||||
)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func LogSuccessMessage(message string) {
|
||||
fmt.Printf("%s[g365sfu] %s[%s]%s %s[SUCCESS]%s %s\n",
|
||||
colorBlue,
|
||||
colorGray, timestamp, colorReset,
|
||||
colorCyan, colorReset,
|
||||
colorGreen, colorReset,
|
||||
message,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ func BindPeerLifecycle(roomID, peerID string, pc *webrtc.PeerConnection) {
|
||||
mu sync.Mutex
|
||||
disconnecting bool
|
||||
timer *time.Timer
|
||||
leaveOnce sync.Once
|
||||
)
|
||||
|
||||
room, exists := GetRoom(roomID)
|
||||
@@ -99,6 +100,26 @@ func BindPeerLifecycle(roomID, peerID string, pc *webrtc.PeerConnection) {
|
||||
}
|
||||
server := room.Server
|
||||
|
||||
cancelTimer := func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
timer = nil
|
||||
}
|
||||
disconnecting = false
|
||||
}
|
||||
|
||||
leaveAndNotify := func(reason DisconnectReason) {
|
||||
leaveOnce.Do(func() {
|
||||
cancelTimer()
|
||||
err := LeaveRoom(roomID, peerID)
|
||||
if OnPeerDisconnected != nil && err == nil {
|
||||
OnPeerDisconnected(roomID, peerID, server, reason)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
startTimer := func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
@@ -114,35 +135,20 @@ func BindPeerLifecycle(roomID, peerID string, pc *webrtc.PeerConnection) {
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
_ = LeaveRoom(roomID, peerID)
|
||||
if OnPeerDisconnected != nil {
|
||||
OnPeerDisconnected(roomID, peerID, server)
|
||||
}
|
||||
leaveAndNotify(DisconnectReasonFailed)
|
||||
})
|
||||
}
|
||||
|
||||
cancelTimer := func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
timer = nil
|
||||
}
|
||||
disconnecting = false
|
||||
}
|
||||
|
||||
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||
switch state {
|
||||
case webrtc.ICEConnectionStateConnected, webrtc.ICEConnectionStateCompleted:
|
||||
cancelTimer()
|
||||
case webrtc.ICEConnectionStateDisconnected:
|
||||
startTimer()
|
||||
case webrtc.ICEConnectionStateFailed, webrtc.ICEConnectionStateClosed:
|
||||
cancelTimer()
|
||||
_ = LeaveRoom(roomID, peerID)
|
||||
if OnPeerDisconnected != nil {
|
||||
OnPeerDisconnected(roomID, peerID, server)
|
||||
}
|
||||
case webrtc.ICEConnectionStateClosed:
|
||||
leaveAndNotify(DisconnectReasonClosed)
|
||||
case webrtc.ICEConnectionStateFailed:
|
||||
leaveAndNotify(DisconnectReasonFailed)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -152,16 +158,15 @@ func BindPeerLifecycle(roomID, peerID string, pc *webrtc.PeerConnection) {
|
||||
cancelTimer()
|
||||
case webrtc.PeerConnectionStateDisconnected:
|
||||
startTimer()
|
||||
case webrtc.PeerConnectionStateFailed, webrtc.PeerConnectionStateClosed:
|
||||
cancelTimer()
|
||||
_ = LeaveRoom(roomID, peerID)
|
||||
if OnPeerDisconnected != nil {
|
||||
OnPeerDisconnected(roomID, peerID, server)
|
||||
}
|
||||
case webrtc.PeerConnectionStateClosed:
|
||||
leaveAndNotify(DisconnectReasonClosed)
|
||||
case webrtc.PeerConnectionStateFailed:
|
||||
leaveAndNotify(DisconnectReasonFailed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Вызывается при JoinWithOffer для ретрансляции RTP пакетов от издателя к другим участникам комнаты
|
||||
// Вызывается при JoinWithOffer для ретрансляции RTP пакетов от издателя к другим участникам комнаты
|
||||
func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *webrtc.PeerConnection) {
|
||||
publisherPC.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
|
||||
@@ -178,7 +183,7 @@ func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *
|
||||
remote.StreamID(),
|
||||
)
|
||||
if err != nil {
|
||||
logger.LogErrorMessage("SetupForwardingForPeer: NewTrackLocalStaticRTP error")
|
||||
logger.LogErrorMessage("SetupForwardingForPeer: NewTrackLocalStaticRTP error: " + err.Error())
|
||||
return
|
||||
}
|
||||
defer removeRoomTrack(roomID, localTrack.ID())
|
||||
@@ -203,32 +208,35 @@ func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *
|
||||
continue
|
||||
}
|
||||
|
||||
// Не трогаем закрытые/failed соединения
|
||||
if !isPeerConnectionAlive(sub.PeerConnection) {
|
||||
fmt.Println("SetupForwardingForPeer: skipping dead peer:", sub.PeerID,
|
||||
sub.PeerConnection.ConnectionState().String())
|
||||
continue
|
||||
}
|
||||
|
||||
sender, err := sub.PeerConnection.AddTrack(localTrack)
|
||||
if err != nil {
|
||||
fmt.Println("SetupForwardingForPeer: AddTrack error:", roomID, sub.PeerID, err)
|
||||
logger.LogWarnMessage("SetupForwardingForPeer: AddTrack error: " + sub.PeerID + " " + err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// RTCP drain
|
||||
senderCopy := sender
|
||||
go func() {
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
if _, _, e := sender.Read(buf); e != nil {
|
||||
if _, _, e := senderCopy.Read(buf); e != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = renegotiatePeer(roomID, sub.PeerID, sub.PeerConnection); err != nil {
|
||||
fmt.Println("SetupForwardingForPeer: renegotiatePeer error:", roomID, sub.PeerID, err)
|
||||
}
|
||||
subID := sub.PeerID
|
||||
subPC := sub.PeerConnection
|
||||
|
||||
go func() {
|
||||
logger.LogInfoMessage("SetupForwardingForPeer: starting renegotiation for peer=" + subID)
|
||||
if err := renegotiatePeer(roomID, subID, subPC); err != nil {
|
||||
logger.LogWarnMessage("SetupForwardingForPeer: renegotiatePeer error: " + subID + " " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Для video просим keyframe
|
||||
@@ -245,11 +253,11 @@ func SetupForwardingForPeer(roomID string, publisherPeerID string, publisherPC *
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
fmt.Println("SetupForwardingForPeer: ReadRTP error:", err)
|
||||
logger.LogWarnMessage("SetupForwardingForPeer: ReadRTP error: " + err.Error())
|
||||
return
|
||||
}
|
||||
if err = localTrack.WriteRTP(pkt); err != nil {
|
||||
fmt.Println("SetupForwardingForPeer: WriteRTP error:", err)
|
||||
logger.LogWarnMessage("SetupForwardingForPeer: WriteRTP error: " + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
11
sfu/reason.go
Normal file
11
sfu/reason.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package sfu
|
||||
|
||||
// Причины отключения пира от комнаты, которые могут быть использованы для логирования или уведомлений
|
||||
type DisconnectReason int
|
||||
|
||||
const (
|
||||
// Пир отключился из-за ошибки соединения или другой проблемы
|
||||
DisconnectReasonFailed DisconnectReason = 0
|
||||
// Пир отключился по своей инициативе (например, закрыл приложение)
|
||||
DisconnectReasonClosed = 1
|
||||
)
|
||||
79
sfu/rooms.go
79
sfu/rooms.go
@@ -1,6 +1,7 @@
|
||||
package sfu
|
||||
|
||||
import (
|
||||
"g365sfu/logger"
|
||||
connection "g365sfu/socket/struct"
|
||||
"sync"
|
||||
|
||||
@@ -76,43 +77,9 @@ func JoinWithOffer(roomID string, peerID string, offer webrtc.SessionDescription
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if OnLocalICECandidate != nil {
|
||||
OnLocalICECandidate(roomID, peerID, c.ToJSON())
|
||||
}
|
||||
})
|
||||
|
||||
BindPeerLifecycle(roomID, peerID, peerConnection)
|
||||
SetupForwardingForPeer(roomID, peerID, peerConnection)
|
||||
|
||||
room.mu.RLock()
|
||||
existingTracks := make([]RoomTrack, len(room.Tracks))
|
||||
copy(existingTracks, room.Tracks)
|
||||
room.mu.RUnlock()
|
||||
|
||||
for _, t := range existingTracks {
|
||||
if t.OwnerPeer == peerID {
|
||||
continue
|
||||
}
|
||||
|
||||
sender, err := peerConnection.AddTrack(t.Local)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
if _, _, e := sender.Read(buf); e != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
||||
_ = peerConnection.Close()
|
||||
return nil, err
|
||||
@@ -131,13 +98,57 @@ func JoinWithOffer(roomID string, peerID string, offer webrtc.SessionDescription
|
||||
}
|
||||
<-gatherDone
|
||||
|
||||
peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if OnLocalICECandidate != nil {
|
||||
OnLocalICECandidate(roomID, peerID, c.ToJSON())
|
||||
}
|
||||
})
|
||||
|
||||
// Добавляем peer в комнату и сразу снимаем snapshot существующих треков
|
||||
// в одном локе — чтобы не было race с OnTrack
|
||||
room.mu.Lock()
|
||||
room.Peers = append(room.Peers, Peer{
|
||||
PeerID: peerID,
|
||||
PeerConnection: peerConnection,
|
||||
})
|
||||
existingTracks := make([]RoomTrack, len(room.Tracks))
|
||||
copy(existingTracks, room.Tracks)
|
||||
room.mu.Unlock()
|
||||
|
||||
// Подписываем нового peer на уже существующие треки ПОСЛЕ добавления в комнату
|
||||
for _, t := range existingTracks {
|
||||
if t.OwnerPeer == peerID {
|
||||
continue
|
||||
}
|
||||
|
||||
sender, err := peerConnection.AddTrack(t.Local)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
senderCopy := sender
|
||||
go func() {
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
if _, _, e := senderCopy.Read(buf); e != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Если были добавлены треки — нужна renegotiation
|
||||
if len(existingTracks) > 0 {
|
||||
go func() {
|
||||
if err := renegotiatePeer(roomID, peerID, peerConnection); err != nil {
|
||||
logger.LogWarnMessage("JoinWithOffer: renegotiatePeer error: " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return peerConnection.LocalDescription(), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ var OnServerOffer func(roomID string, peerID string, offer webrtc.SessionDescrip
|
||||
var OnLocalICECandidate func(roomID, peerID string, candidate webrtc.ICECandidateInit)
|
||||
|
||||
// Коллбек для обработки отключения пира (обрыв связи)
|
||||
var OnPeerDisconnected func(roomID, peerID string, server *connection.Connection)
|
||||
var OnPeerDisconnected func(roomID, peerID string, server *connection.Connection, reason DisconnectReason)
|
||||
|
||||
// Коллбек для обработки удаления комнаты
|
||||
var OnRoomDelete func(roomID string, server *connection.Connection)
|
||||
|
||||
Reference in New Issue
Block a user