package main import ( "encoding/json" "github.com/google/uuid" "github.com/gorilla/websocket" "github.com/pion/webrtc/v4" ) type Peer struct { ID string Conn *websocket.Conn PC *webrtc.PeerConnection Room *Room Senders map[string]*webrtc.RTPSender } type Signal struct { Type string `json:"type"` Data json.RawMessage `json:"data"` } func NewPeer(conn *websocket.Conn) *Peer { pc, _ := webrtc.NewPeerConnection(webrtc.Configuration{}) p := &Peer{ ID: uuid.NewString(), Conn: conn, PC: pc, Senders: make(map[string]*webrtc.RTPSender), } pc.OnTrack(func(track *webrtc.TrackRemote, _ *webrtc.RTPReceiver) { localTrack := NewTrack(track) p.Room.ForwardTrack(p, localTrack) for { pkt, _, err := track.ReadRTP() if err != nil { return } localTrack.Write(pkt) } }) pc.OnICECandidate(func(c *webrtc.ICECandidate) { if c == nil { return } msg, _ := json.Marshal(c.ToJSON()) p.Send("ice", msg) }) 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 p.Conn.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) { msg := Signal{Type: t, Data: data} p.Conn.WriteJSON(msg) }