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