AAAAAAAAAAAAAAAAAAA
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 2m25s

This commit is contained in:
Smile Rex
2026-01-26 20:06:52 +03:00
parent b013285408
commit 87c137111a
3 changed files with 36 additions and 69 deletions

10
main.go
View File

@@ -14,20 +14,14 @@ var upgrader = websocket.Upgrader{
var room = NewRoom() var room = NewRoom()
func wsHandler(w http.ResponseWriter, r *http.Request) { func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil) ws, _ := upgrader.Upgrade(w, r, nil)
if err != nil { peer := NewPeer(ws, room)
return
}
peer := NewPeer(conn, room)
room.AddPeer(peer) room.AddPeer(peer)
go peer.ReadLoop() go peer.ReadLoop()
} }
func main() { func main() {
http.HandleFunc("/ws", wsHandler) http.HandleFunc("/ws", wsHandler)
log.Println("SFU listening on :8080") log.Println("SFU listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
} }

73
peer.go
View File

@@ -10,93 +10,67 @@ import (
type Signal struct { type Signal struct {
Type string `json:"type"` Type string `json:"type"`
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data,omitempty"`
} }
type Peer struct { type Peer struct {
ID string ID string
Conn *websocket.Conn WS *websocket.Conn
PC *webrtc.PeerConnection PC *webrtc.PeerConnection
Room *Room Room *Room
} }
func NewPeer(conn *websocket.Conn, room *Room) *Peer { func NewPeer(ws *websocket.Conn, room *Room) *Peer {
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{ pc, _ := webrtc.NewPeerConnection(webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: []webrtc.ICEServer{
{URLs: []string{"stun:stun.l.google.com:19302"}}, {URLs: []string{"stun:stun.l.google.com:19302"}},
}, },
}) })
if err != nil {
panic(err)
}
// 🔥 ОБЯЗАТЕЛЬНО для SFU // ОБЯЗАТЕЛЬНО для SFU
_, _ = pc.AddTransceiverFromKind( pc.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio)
webrtc.RTPCodecTypeAudio, pc.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo)
webrtc.RTPTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendrecv,
},
)
_, _ = pc.AddTransceiverFromKind(
webrtc.RTPCodecTypeVideo,
webrtc.RTPTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendrecv,
},
)
p := &Peer{ p := &Peer{
ID: uuid.NewString(), ID: uuid.NewString(),
Conn: conn, WS: ws,
PC: pc, PC: pc,
Room: room, Room: room,
} }
pc.OnICECandidate(func(c *webrtc.ICECandidate) { pc.OnICECandidate(func(c *webrtc.ICECandidate) {
if c == nil { if c != nil {
return b, _ := json.Marshal(c.ToJSON())
p.Send("ice", b)
} }
data, _ := json.Marshal(c.ToJSON())
p.Send("ice", data)
}) })
pc.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) { pc.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
local := NewTrack(remote) track := NewTrack(remote)
p.Room.AddTrack(p, track)
// 🔥 регистрируем трек в комнате
p.Room.AddTrack(p, local)
for { for {
pkt, _, err := remote.ReadRTP() pkt, _, err := remote.ReadRTP()
if err != nil { if err != nil {
return return
} }
local.Write(pkt) track.Write(pkt)
} }
}) })
return p return p
} }
func (p *Peer) AddTrack(track *Track) {
_, err := p.PC.AddTrack(track.Local)
if err != nil {
return
}
p.Send("renegotiate", nil)
}
func (p *Peer) ReadLoop() { func (p *Peer) ReadLoop() {
defer func() { defer func() {
p.Conn.Close()
p.Room.RemovePeer(p.ID) p.Room.RemovePeer(p.ID)
p.PC.Close() p.PC.Close()
p.WS.Close()
}() }()
for { for {
var msg Signal var msg Signal
if err := p.Conn.ReadJSON(&msg); err != nil { if err := p.WS.ReadJSON(&msg); err != nil {
return return
} }
@@ -104,13 +78,13 @@ func (p *Peer) ReadLoop() {
case "offer": case "offer":
var offer webrtc.SessionDescription var offer webrtc.SessionDescription
json.Unmarshal(msg.Data, &offer) json.Unmarshal(msg.Data, &offer)
p.PC.SetRemoteDescription(offer) p.PC.SetRemoteDescription(offer)
answer, _ := p.PC.CreateAnswer(nil) answer, _ := p.PC.CreateAnswer(nil)
p.PC.SetLocalDescription(answer) p.PC.SetLocalDescription(answer)
data, _ := json.Marshal(answer) b, _ := json.Marshal(answer)
p.Send("answer", data) p.Send("answer", b)
case "ice": case "ice":
var c webrtc.ICECandidateInit var c webrtc.ICECandidateInit
@@ -120,6 +94,11 @@ func (p *Peer) ReadLoop() {
} }
} }
func (p *Peer) Send(t string, data []byte) { func (p *Peer) AddTrack(t *Track) {
p.Conn.WriteJSON(Signal{Type: t, Data: data}) p.PC.AddTrack(t.Local)
p.Send("renegotiate", nil)
}
func (p *Peer) Send(t string, data []byte) {
p.WS.WriteJSON(Signal{Type: t, Data: data})
} }

22
room.go
View File

@@ -10,8 +10,7 @@ type Room struct {
func NewRoom() *Room { func NewRoom() *Room {
return &Room{ return &Room{
peers: make(map[string]*Peer), peers: make(map[string]*Peer),
tracks: make([]*Track, 0),
} }
} }
@@ -20,10 +19,8 @@ func (r *Room) AddPeer(p *Peer) {
defer r.mu.Unlock() defer r.mu.Unlock()
r.peers[p.ID] = p r.peers[p.ID] = p
for _, t := range r.tracks {
// 🔥 ВАЖНО: отдать новому peer ВСЕ существующие треки p.AddTrack(t)
for _, track := range r.tracks {
p.AddTrack(track)
} }
} }
@@ -33,17 +30,14 @@ func (r *Room) RemovePeer(id string) {
delete(r.peers, id) delete(r.peers, id)
} }
func (r *Room) AddTrack(from *Peer, track *Track) { func (r *Room) AddTrack(from *Peer, t *Track) {
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
// сохраняем трек r.tracks = append(r.tracks, t)
r.tracks = append(r.tracks, track) for _, p := range r.peers {
if p.ID != from.ID {
// форвардим всем, кроме источника p.AddTrack(t)
for _, peer := range r.peers {
if peer.ID != from.ID {
peer.AddTrack(track)
} }
} }
} }