mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 09:16:57 +00:00
229 lines
5.0 KiB
Go
229 lines
5.0 KiB
Go
package sfu
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/pion/webrtc/v4"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
var (
|
|
ErrRoomNotFound = errors.New("room not found")
|
|
)
|
|
|
|
// RoomManager manages all rooms in the SFU
|
|
type RoomManager struct {
|
|
rooms map[string]*Room // key: namespace:roomId
|
|
roomsMu sync.RWMutex
|
|
|
|
api *webrtc.API
|
|
config *Config
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewRoomManager creates a new room manager
|
|
func NewRoomManager(config *Config, logger *zap.Logger) (*RoomManager, error) {
|
|
if config == nil {
|
|
config = DefaultConfig()
|
|
}
|
|
|
|
api, err := NewWebRTCAPI()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RoomManager{
|
|
rooms: make(map[string]*Room),
|
|
api: api,
|
|
config: config,
|
|
logger: logger.With(zap.String("component", "sfu_manager")),
|
|
}, nil
|
|
}
|
|
|
|
// roomKey creates a unique key for namespace:roomId
|
|
func roomKey(namespace, roomID string) string {
|
|
return namespace + ":" + roomID
|
|
}
|
|
|
|
// GetOrCreateRoom gets an existing room or creates a new one
|
|
func (m *RoomManager) GetOrCreateRoom(namespace, roomID string) (*Room, bool) {
|
|
key := roomKey(namespace, roomID)
|
|
|
|
m.roomsMu.Lock()
|
|
defer m.roomsMu.Unlock()
|
|
|
|
// Check if room already exists
|
|
if room, ok := m.rooms[key]; ok {
|
|
if !room.IsClosed() {
|
|
return room, false
|
|
}
|
|
// Room is closed, remove it and create a new one
|
|
delete(m.rooms, key)
|
|
}
|
|
|
|
// Create new room
|
|
room := NewRoom(roomID, namespace, m.api, m.config, m.logger)
|
|
|
|
// Set up empty room handler
|
|
room.OnEmpty(func(r *Room) {
|
|
m.logger.Info("Room is empty, will be garbage collected",
|
|
zap.String("room_id", r.ID),
|
|
zap.String("namespace", r.Namespace),
|
|
)
|
|
// Optionally close the room immediately
|
|
// m.CloseRoom(r.Namespace, r.ID)
|
|
})
|
|
|
|
m.rooms[key] = room
|
|
|
|
m.logger.Info("Room created",
|
|
zap.String("room_id", roomID),
|
|
zap.String("namespace", namespace),
|
|
)
|
|
|
|
return room, true
|
|
}
|
|
|
|
// GetRoom gets an existing room
|
|
func (m *RoomManager) GetRoom(namespace, roomID string) (*Room, error) {
|
|
key := roomKey(namespace, roomID)
|
|
|
|
m.roomsMu.RLock()
|
|
defer m.roomsMu.RUnlock()
|
|
|
|
room, ok := m.rooms[key]
|
|
if !ok {
|
|
return nil, ErrRoomNotFound
|
|
}
|
|
|
|
if room.IsClosed() {
|
|
return nil, ErrRoomClosed
|
|
}
|
|
|
|
return room, nil
|
|
}
|
|
|
|
// CloseRoom closes and removes a room
|
|
func (m *RoomManager) CloseRoom(namespace, roomID string) error {
|
|
key := roomKey(namespace, roomID)
|
|
|
|
m.roomsMu.Lock()
|
|
room, ok := m.rooms[key]
|
|
if !ok {
|
|
m.roomsMu.Unlock()
|
|
return ErrRoomNotFound
|
|
}
|
|
delete(m.rooms, key)
|
|
m.roomsMu.Unlock()
|
|
|
|
if err := room.Close(); err != nil {
|
|
m.logger.Warn("Error closing room",
|
|
zap.String("room_id", roomID),
|
|
zap.Error(err),
|
|
)
|
|
return err
|
|
}
|
|
|
|
m.logger.Info("Room closed",
|
|
zap.String("room_id", roomID),
|
|
zap.String("namespace", namespace),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListRooms returns all rooms for a namespace
|
|
func (m *RoomManager) ListRooms(namespace string) []*Room {
|
|
m.roomsMu.RLock()
|
|
defer m.roomsMu.RUnlock()
|
|
|
|
prefix := namespace + ":"
|
|
rooms := make([]*Room, 0)
|
|
|
|
for key, room := range m.rooms {
|
|
if len(key) > len(prefix) && key[:len(prefix)] == prefix && !room.IsClosed() {
|
|
rooms = append(rooms, room)
|
|
}
|
|
}
|
|
|
|
return rooms
|
|
}
|
|
|
|
// RoomInfo contains public information about a room
|
|
type RoomInfo struct {
|
|
ID string `json:"id"`
|
|
Namespace string `json:"namespace"`
|
|
ParticipantCount int `json:"participantCount"`
|
|
Participants []ParticipantInfo `json:"participants"`
|
|
}
|
|
|
|
// GetRoomInfo returns public information about a room
|
|
func (m *RoomManager) GetRoomInfo(namespace, roomID string) (*RoomInfo, error) {
|
|
room, err := m.GetRoom(namespace, roomID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RoomInfo{
|
|
ID: room.ID,
|
|
Namespace: room.Namespace,
|
|
ParticipantCount: room.GetParticipantCount(),
|
|
Participants: room.GetParticipants(),
|
|
}, nil
|
|
}
|
|
|
|
// GetStats returns statistics about the room manager
|
|
func (m *RoomManager) GetStats() map[string]interface{} {
|
|
m.roomsMu.RLock()
|
|
defer m.roomsMu.RUnlock()
|
|
|
|
totalRooms := 0
|
|
totalParticipants := 0
|
|
|
|
for _, room := range m.rooms {
|
|
if !room.IsClosed() {
|
|
totalRooms++
|
|
totalParticipants += room.GetParticipantCount()
|
|
}
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"totalRooms": totalRooms,
|
|
"totalParticipants": totalParticipants,
|
|
}
|
|
}
|
|
|
|
// Close closes all rooms and cleans up resources
|
|
func (m *RoomManager) Close() error {
|
|
m.roomsMu.Lock()
|
|
rooms := make([]*Room, 0, len(m.rooms))
|
|
for _, room := range m.rooms {
|
|
rooms = append(rooms, room)
|
|
}
|
|
m.rooms = make(map[string]*Room)
|
|
m.roomsMu.Unlock()
|
|
|
|
for _, room := range rooms {
|
|
if err := room.Close(); err != nil {
|
|
m.logger.Warn("Error closing room during shutdown",
|
|
zap.String("room_id", room.ID),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
}
|
|
|
|
m.logger.Info("Room manager closed", zap.Int("rooms_closed", len(rooms)))
|
|
return nil
|
|
}
|
|
|
|
// GetConfig returns the SFU configuration
|
|
func (m *RoomManager) GetConfig() *Config {
|
|
return m.config
|
|
}
|
|
|
|
// UpdateICEServers updates the ICE servers configuration
|
|
func (m *RoomManager) UpdateICEServers(servers []webrtc.ICEServer) {
|
|
m.config.ICEServers = servers
|
|
}
|