From 6a37033ca87615610a73461d2605756cb918bbee Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Tue, 11 Aug 2020 15:13:30 +0200 Subject: [PATCH] Allow both sides to initiate renegotiation. More reliable reaction to network failures when they are detected by just one side. --- static/sfu.js | 22 +++++++++++++++++++--- webclient.go | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/static/sfu.js b/static/sfu.js index a624faf..a3bd7ba 100644 --- a/static/sfu.js +++ b/static/sfu.js @@ -648,6 +648,17 @@ function serverConnect() { case 'answer': gotAnswer(m.id, m.answer); break; + case 'renegotiate': + let c = up[id]; + if(c) { + try { + c.pc.restartIce() + } catch(e) { + console.error(e); + displayError(e); + } + } + break; case 'close': gotClose(m.id); break; @@ -743,6 +754,11 @@ async function gotOffer(id, labels, offer, renegotiate) { pc.oniceconnectionstatechange = e => { setMediaStatus(id); + if(pc.iceConnectionState === 'failed') { + send({type: 'renegotiate', + id: id, + }); + } } c.pc.ontrack = function(e) { @@ -1179,7 +1195,7 @@ async function newUpStream(id) { pc.onnegotiationneeded = async e => { try { - await negotiate(id); + await negotiate(id, false); } catch(e) { console.error(e); displayError(e); @@ -1213,7 +1229,7 @@ async function newUpStream(id) { return id; } -async function negotiate(id) { +async function negotiate(id, restartIce) { let c = up[id]; if(!c) throw new Error('unknown connection'); @@ -1221,7 +1237,7 @@ async function negotiate(id) { if(typeof(c.pc.getTransceivers) !== 'function') throw new Error('Browser too old, please upgrade'); - let offer = await c.pc.createOffer({}); + let offer = await c.pc.createOffer({iceRestart: restartIce}); if(!offer) throw(new Error("Didn't create offer")); await c.pc.setLocalDescription(offer); diff --git a/webclient.go b/webclient.go index 102cbd7..8e58036 100644 --- a/webclient.go +++ b/webclient.go @@ -236,6 +236,12 @@ func addUpConn(c *webClient, id string) (*rtpUpConnection, bool, error) { sendICE(c, id, candidate) }) + conn.pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) { + if state == webrtc.ICEConnectionStateFailed { + c.action(connectionFailedAction{id: id}) + } + }) + return conn, true, nil } @@ -772,14 +778,11 @@ func clientLoop(c *webClient, conn *websocket.Conn) error { go a.c.pushConn(u.id, u, ts, u.label) } case connectionFailedAction: - down := getDownConn(c, a.id) - if down == nil { - log.Printf("Failed indication for " + - "unknown connection") - continue - } - err := negotiate(c, down, true, true) - if err != nil { + if down := getDownConn(c, a.id); down != nil { + err := negotiate(c, down, true, true) + if err == nil { + return err + } tracks := make( []upTrack, len(down.tracks), ) @@ -790,7 +793,16 @@ func clientLoop(c *webClient, conn *websocket.Conn) error { down.remote.Id(), down.remote, tracks, down.remote.Label(), ) + } else if up := getUpConn(c, a.id); up != nil { + c.write(clientMessage{ + Type: "renegotiate", + Id: a.id, + }) + } else { + log.Printf("Attempting to renegotiate " + + "unknown connection") } + case permissionsChangedAction: c.write(clientMessage{ Type: "permissions", @@ -930,6 +942,16 @@ func handleClientMessage(c *webClient, m clientMessage) error { if err != nil { return err } + case "renegotiate": + down := getDownConn(c, m.Id) + if down != nil { + err := negotiate(c, down, true, true) + if err != nil { + return err + } + } else { + log.Printf("Trying to renegotiate unknown connection") + } case "close": found := delUpConn(c, m.Id) if !found {