All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 1m31s
131 lines
2.3 KiB
Go
131 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
type Signal struct {
|
|
Type string `json:"type"`
|
|
Data json.RawMessage `json:"data"`
|
|
}
|
|
|
|
type Peer struct {
|
|
ID string
|
|
Conn *websocket.Conn
|
|
PC *webrtc.PeerConnection
|
|
Room *Room
|
|
}
|
|
|
|
func NewPeer(conn *websocket.Conn, room *Room) *Peer {
|
|
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{
|
|
ICEServers: []webrtc.ICEServer{
|
|
{URLs: []string{"stun:stun.l.google.com:19302"}},
|
|
},
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// 🔥 ОБЯЗАТЕЛЬНО для SFU
|
|
_, _ = pc.AddTransceiverFromKind(
|
|
webrtc.RTPCodecTypeAudio,
|
|
webrtc.RTPTransceiverInit{
|
|
Direction: webrtc.RTPTransceiverDirectionSendrecv,
|
|
},
|
|
)
|
|
|
|
_, _ = pc.AddTransceiverFromKind(
|
|
webrtc.RTPCodecTypeVideo,
|
|
webrtc.RTPTransceiverInit{
|
|
Direction: webrtc.RTPTransceiverDirectionSendrecv,
|
|
},
|
|
)
|
|
|
|
p := &Peer{
|
|
ID: uuid.NewString(),
|
|
Conn: conn,
|
|
PC: pc,
|
|
Room: room,
|
|
}
|
|
|
|
pc.OnICECandidate(func(c *webrtc.ICECandidate) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
data, _ := json.Marshal(c.ToJSON())
|
|
p.Send("ice", data)
|
|
})
|
|
|
|
pc.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
|
|
local := NewTrack(remote)
|
|
|
|
p.Room.mu.Lock()
|
|
for _, other := range p.Room.peers {
|
|
if other.ID != p.ID {
|
|
other.AddTrack(local)
|
|
}
|
|
}
|
|
p.Room.mu.Unlock()
|
|
|
|
for {
|
|
pkt, _, err := remote.ReadRTP()
|
|
if err != nil {
|
|
return
|
|
}
|
|
local.Write(pkt)
|
|
}
|
|
})
|
|
|
|
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() {
|
|
defer func() {
|
|
p.Conn.Close()
|
|
p.Room.Remove(p.ID)
|
|
p.PC.Close()
|
|
}()
|
|
|
|
for {
|
|
var msg Signal
|
|
if err := p.Conn.ReadJSON(&msg); err != nil {
|
|
return
|
|
}
|
|
|
|
switch msg.Type {
|
|
case "offer":
|
|
var offer webrtc.SessionDescription
|
|
json.Unmarshal(msg.Data, &offer)
|
|
|
|
p.PC.SetRemoteDescription(offer)
|
|
answer, _ := p.PC.CreateAnswer(nil)
|
|
p.PC.SetLocalDescription(answer)
|
|
|
|
data, _ := json.Marshal(answer)
|
|
p.Send("answer", data)
|
|
|
|
case "ice":
|
|
var c webrtc.ICECandidateInit
|
|
json.Unmarshal(msg.Data, &c)
|
|
p.PC.AddICECandidate(c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Peer) Send(t string, data []byte) {
|
|
p.Conn.WriteJSON(Signal{Type: t, Data: data})
|
|
}
|