live-sfu-demo/internal/server/rooms.go

113 lines
2.3 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 server
import (
"encoding/json"
"sync"
"gospeak-live-sfu-demo/gen"
)
// roomHub 维护房间 -> 各后端分发目标(StreamTarget)的内存状态,
// 并向订阅者广播拓扑变化(实现「分流」状态实时可见)。
type roomHub struct {
mu sync.RWMutex
rooms map[string]*roomEntry
}
type roomEntry struct {
targets map[string]*gen.StreamTarget // key: 后端名(cloudflare / srs)
subs map[chan []byte]struct{}
}
func newRoomHub() *roomHub {
return &roomHub{rooms: map[string]*roomEntry{}}
}
func (h *roomHub) get(name string) *roomEntry {
e, ok := h.rooms[name]
if !ok {
e = &roomEntry{targets: map[string]*gen.StreamTarget{}, subs: map[chan []byte]struct{}{}}
h.rooms[name] = e
}
return e
}
func (h *roomHub) setTarget(room, backend string, t *gen.StreamTarget) {
h.mu.Lock()
defer h.mu.Unlock()
e := h.get(room)
e.targets[backend] = t
h.broadcast(room, e)
}
func (h *roomHub) removeTarget(room, backend string) {
h.mu.Lock()
defer h.mu.Unlock()
e, ok := h.rooms[room]
if !ok {
return
}
delete(e.targets, backend)
if len(e.targets) == 0 && len(e.subs) == 0 {
delete(h.rooms, room)
return
}
h.broadcast(room, e)
}
func (h *roomHub) targets(room string) []*gen.StreamTarget {
h.mu.RLock()
defer h.mu.RUnlock()
e, ok := h.rooms[room]
if !ok {
return nil
}
out := make([]*gen.StreamTarget, 0, len(e.targets))
for _, t := range e.targets {
out = append(out, t)
}
return out
}
func (h *roomHub) list() []*gen.Room {
h.mu.RLock()
defer h.mu.RUnlock()
out := make([]*gen.Room, 0, len(h.rooms))
for name, e := range h.rooms {
room := &gen.Room{Name: name}
for _, t := range e.targets {
room.Targets = append(room.Targets, t)
}
out = append(out, room)
}
return out
}
func (h *roomHub) subscribe(room string) (chan []byte, func()) {
h.mu.Lock()
defer h.mu.Unlock()
e := h.get(room)
ch := make(chan []byte, 8)
e.subs[ch] = struct{}{}
return ch, func() {
h.mu.Lock()
defer h.mu.Unlock()
if e2, ok := h.rooms[room]; ok {
delete(e2.subs, ch)
}
}
}
func (h *roomHub) broadcast(room string, e *roomEntry) {
payload, _ := json.Marshal(map[string]interface{}{
"room": room,
"targets": e.targets,
})
for ch := range e.subs {
select {
case ch <- payload:
default:
}
}
}