From 86ee0cb380eba45b7fb0134a2348d7e3d6c46128 Mon Sep 17 00:00:00 2001 From: sergystepanov Date: Sun, 7 Jun 2026 11:43:15 +0300 Subject: [PATCH] Create the main data channel without negotiation There is no point in previously pushing or pulling this channel, since we won't change any of its configuration parameters. --- pkg/network/webrtc/webrtc.go | 91 ++++++++++++++----------------- pkg/worker/coordinatorhandlers.go | 9 +-- web/js/network/webrtc.js | 30 +++++----- 3 files changed, 60 insertions(+), 70 deletions(-) diff --git a/pkg/network/webrtc/webrtc.go b/pkg/network/webrtc/webrtc.go index 30b5698d..869a09bf 100644 --- a/pkg/network/webrtc/webrtc.go +++ b/pkg/network/webrtc/webrtc.go @@ -36,6 +36,28 @@ func (p *Peer) NewConnection(vCodec, aCodec string, onICECandidate func(ice any) if p.conn, err = p.api.NewPeer(); err != nil { return } + + id := uint16(0) + negotiated := true + ordered := false + maxRetransmits := uint16(0) + + p.d, err = p.AddChannel("data", &webrtc.DataChannelInit{ + ID: &id, + Negotiated: &negotiated, + Ordered: &ordered, + MaxRetransmits: &maxRetransmits, + }, + func(data []byte) { + if len(data) == 0 || p.OnMessage == nil { + return + } + p.OnMessage(data) + }) + if err != nil { + return err + } + p.conn.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) { p.log.Debug().Msgf("WebRTC state change: %v", pcs) if pcs == webrtc.PeerConnectionStateConnected { @@ -58,21 +80,6 @@ func (p *Peer) NewConnection(vCodec, aCodec string, onICECandidate func(ice any) p.conn.OnDataChannel(func(ch *webrtc.DataChannel) { p.log.Debug().Msgf("Added [%s] datachannel", ch.Label()) - - if ch.Label() == "data" { - p.d = ch - ch.OnMessage(func(m webrtc.DataChannelMessage) { - if len(m.Data) == 0 || p.OnMessage == nil { - return - } - p.OnMessage(m.Data) - }) - ch.OnOpen(func() { - p.log.Debug().Uint16("id", *ch.ID()).Msgf("Data channel [%v] opened", ch.Label()) - }) - ch.OnError(p.logx) - ch.OnClose(func() { p.log.Debug().Msgf("Data channel [%v] has been closed", ch.Label()) }) - } }) return nil @@ -100,15 +107,6 @@ func (p *Peer) AddTrack(id, label, codec string) (*webrtc.TrackLocalStaticSample return track, nil } -func (p *Peer) AddDataChannel() error { - return p.AddChannel("data", func(data []byte) { - if len(data) == 0 || p.OnMessage == nil { - return - } - p.OnMessage(data) - }) -} - func (p *Peer) OfferAnswer(offer bool) (*webrtc.SessionDescription, error) { opts := webrtc.OfferAnswerOptions{ICETricklingSupported: true} @@ -250,17 +248,30 @@ func (p *Peer) AddCandidate(candidate string, decoder Decoder) error { return nil } -func (p *Peer) AddChannel(label string, onMessage func([]byte)) error { - ch, err := p.addDataChannel(label) +func (p *Peer) AddChannel(label string, conf *webrtc.DataChannelInit, onMessage func([]byte)) (*webrtc.DataChannel, error) { + config := conf + if conf == nil { + ordered := false + maxRetransmits := uint16(0) + config = &webrtc.DataChannelInit{Ordered: &ordered, MaxRetransmits: &maxRetransmits} + } + + ch, err := p.conn.CreateDataChannel(label, config) if err != nil { - return err - } - if label == "data" { - p.d = ch + return nil, err } + + ch.OnOpen(func() { + p.log.Debug().Uint16("id", *ch.ID()).Msgf("Data channel [%v] opened", ch.Label()) + }) ch.OnMessage(func(m webrtc.DataChannelMessage) { onMessage(m.Data) }) + ch.OnError(p.logx) + ch.OnClose(func() { + p.log.Debug().Msgf("Data channel [%v] has been closed", ch.Label()) + }) p.log.Debug().Msgf("Added [%v] chan", label) - return nil + + return ch, nil } func (p *Peer) Disconnect() { @@ -274,22 +285,4 @@ func (p *Peer) Disconnect() { p.log.Debug().Msg("WebRTC stop") } -// addDataChannel creates new WebRTC data channel. -// Default params -- ordered: true, negotiated: false. -func (p *Peer) addDataChannel(label string) (*webrtc.DataChannel, error) { - ch, err := p.conn.CreateDataChannel(label, &webrtc.DataChannelInit{ - Ordered: new(bool), - MaxRetransmits: new(uint16), - }) - if err != nil { - return nil, err - } - ch.OnOpen(func() { - p.log.Debug().Uint16("id", *ch.ID()).Msgf("Data channel [%v] opened", ch.Label()) - }) - ch.OnError(p.logx) - ch.OnClose(func() { p.log.Debug().Msgf("Data channel [%v] has been closed", ch.Label()) }) - return ch, nil -} - func (p *Peer) logx(err error) { p.log.Error().Err(err) } diff --git a/pkg/worker/coordinatorhandlers.go b/pkg/worker/coordinatorhandlers.go index 8699203d..560b6134 100644 --- a/pkg/worker/coordinatorhandlers.go +++ b/pkg/worker/coordinatorhandlers.go @@ -46,11 +46,6 @@ func (c *coordinator) HandleInitWebrtcStream(rq api.InitWebrtcStreamRequest, w * c.log.Error().Err(err).Msgf("cannot set remote SDP of peer [%v]", rq.Id) return api.EmptyPacket } - } else { - if err := peer.AddDataChannel(); err != nil { - c.log.Error().Err(err).Msgf("cannot add data channel for peer [%v]", rq.Id) - return api.EmptyPacket - } } lsdp, err := peer.OfferAnswer(!rq.Initiator) @@ -219,8 +214,8 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest, w *Worker) api.Ou s := room.WithWebRTC(user.Session) s.OnMessage = func(data []byte) { r.App().Input(user.Index, byte(caged.RetroPad), data) } if needsKbMouse { - _ = s.AddChannel("keyboard", func(data []byte) { r.App().Input(user.Index, byte(caged.Keyboard), data) }) - _ = s.AddChannel("mouse", func(data []byte) { r.App().Input(user.Index, byte(caged.Mouse), data) }) + _, _ = s.AddChannel("keyboard", nil, func(data []byte) { r.App().Input(user.Index, byte(caged.Keyboard), data) }) + _, _ = s.AddChannel("mouse", nil, func(data []byte) { r.App().Input(user.Index, byte(caged.Mouse), data) }) } c.RegisterRoom(r.Id()) diff --git a/web/js/network/webrtc.js b/web/js/network/webrtc.js index 75d881c9..d520b551 100755 --- a/web/js/network/webrtc.js +++ b/web/js/network/webrtc.js @@ -123,6 +123,22 @@ export const webrtc = { log.debug("[rtc] [config] ICE:", iceServers); pc = new RTCPeerConnection({ iceServers }); + // push datachannel + try { + let ch = pc.createDataChannel("data", { + negotiated: true, + id: 0, + ordered: false, + maxRetransmits: 0, + }); + ch = onDataChannel ? onDataChannel(ch) : ch; + if (!ch) throw new Error("null channel"); + channels.set(ch.label, ch); + } catch (e) { + log.error("[rtc] failed to create data channel", e); + return; + } + caller = initiator; log.debug(`[rtc] ${caller ? "caller" : "callee"}`); @@ -173,20 +189,6 @@ export const webrtc = { connectionTime = performance.now(); if (initiator) { - // push datachannel - try { - let ch = pc.createDataChannel("data", { - ordered: false, - maxRetransmits: 0, - }); - ch = onDataChannel ? onDataChannel(ch) : ch; - if (!ch) throw new Error("null channel"); - channels.set(ch.label, ch); - } catch (e) { - log.error("[rtc] failed to create data channel", e); - return; - } - offer().then((offer) => { if (!offer) return; signalling.init({ initiator, sdpOffer: offer });