Files
g365sfu/sfu/rooms.go

236 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sfu
import (
connection "g365sfu/socket/struct"
"sync"
"github.com/pion/webrtc/v4"
)
// Структуры для управления комнатами и пирами в SFU
type Peer struct {
//Идентификатор пира, который будет использоваться для связи с ним
PeerID string
//Подсоединенный пир
PeerConnection *webrtc.PeerConnection
}
type RoomTrack struct {
TrackID string
OwnerPeer string
Local *webrtc.TrackLocalStaticRTP
}
type Room struct {
//Уникальный идентификатор комнаты
RoomID string
//Сервер который создал комнату
Server *connection.Connection
//Пиры которые подключились к комнате
Peers []Peer
Tracks []RoomTrack
mu sync.RWMutex
}
// Общие переменные
var (
rooms = make(map[string]*Room)
roomsMu sync.RWMutex
)
// CreateRoom создает комнату
func CreateRoom(server *connection.Connection, roomID string) (*Room, error) {
roomsMu.Lock()
defer roomsMu.Unlock()
room := &Room{
RoomID: roomID,
Server: server,
Peers: []Peer{},
}
rooms[roomID] = room
return room, nil
}
// GetRoom получает комнату по идентификатору
func GetRoom(roomID string) (*Room, bool) {
roomsMu.RLock()
defer roomsMu.RUnlock()
room, exists := rooms[roomID]
return room, exists
}
// JoinWithOffer позволяет пиру присоединиться к комнате с помощью SDP оффера
func JoinWithOffer(roomID string, peerID string, offer webrtc.SessionDescription) (*webrtc.SessionDescription, error) {
room, exists := GetRoom(roomID)
if !exists {
return nil, ErrRoomNotFound
}
peerConnection, err := api.NewPeerConnection(pcConfig)
if err != nil {
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
}
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
_ = peerConnection.Close()
return nil, err
}
gatherDone := webrtc.GatheringCompletePromise(peerConnection)
if err = peerConnection.SetLocalDescription(answer); err != nil {
_ = peerConnection.Close()
return nil, err
}
<-gatherDone
room.mu.Lock()
room.Peers = append(room.Peers, Peer{
PeerID: peerID,
PeerConnection: peerConnection,
})
room.mu.Unlock()
return peerConnection.LocalDescription(), nil
}
func DeleteRoom(roomID string) error {
roomsMu.Lock()
room, exists := rooms[roomID]
server := room.Server
if !exists {
roomsMu.Unlock()
return ErrRoomNotFound
}
delete(rooms, roomID)
roomsMu.Unlock()
room.mu.Lock()
peers := make([]Peer, len(room.Peers))
copy(peers, room.Peers)
room.Peers = nil
room.Tracks = nil
room.mu.Unlock()
for _, p := range peers {
_ = p.PeerConnection.Close()
cleanupForwardingState(roomID, p.PeerID)
}
OnRoomDelete(roomID, server)
return nil
}
// LeaveRoom позволяет пиру покинуть комнату
func LeaveRoom(roomID string, peerID string) error {
room, exists := GetRoom(roomID)
if !exists {
return ErrRoomNotFound
}
var (
removedPC *webrtc.PeerConnection
removed bool
shouldDrop bool
)
room.mu.Lock()
// удаляем peer
nextPeers := make([]Peer, 0, len(room.Peers))
for _, p := range room.Peers {
if p.PeerID == peerID {
removedPC = p.PeerConnection
removed = true
continue
}
nextPeers = append(nextPeers, p)
}
room.Peers = nextPeers
// удаляем треки этого publisher
nextTracks := room.Tracks[:0]
for _, t := range room.Tracks {
if t.OwnerPeer != peerID {
nextTracks = append(nextTracks, t)
}
}
room.Tracks = nextTracks
shouldDrop = len(room.Peers) == 0
room.mu.Unlock()
if !removed {
return ErrPeerNotFound
}
if removedPC != nil {
_ = removedPC.Close()
}
cleanupForwardingState(roomID, peerID)
// Комната пустая -> удаляем
if shouldDrop {
return DeleteRoom(roomID)
}
// renegotiation оставшимся peer после удаления треков/peer
room.mu.RLock()
rest := make([]Peer, len(room.Peers))
copy(rest, room.Peers)
room.mu.RUnlock()
for _, p := range rest {
_ = renegotiatePeer(roomID, p.PeerID, p.PeerConnection)
}
return nil
}