AAAAAAAAAAAAAAAAAAA
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 2m25s
All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 2m25s
This commit is contained in:
10
main.go
10
main.go
@@ -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
73
peer.go
@@ -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
22
room.go
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user