Commit 8168c2a9 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Rework the up connection state machine.

It is now more similar to the down connection, using the onclose
callback for resource management.
parent 10ebe0e4
...@@ -40,11 +40,10 @@ serverConnection.ondownstream = ...; ...@@ -40,11 +40,10 @@ serverConnection.ondownstream = ...;
``` ```
The `onconnected` callback is called when we connect to the server. The The `onconnected` callback is called when we connect to the server. The
`onclose` callback is called when the socket is closed; you should use it `onclose` callback is called when the socket is closed; all streams will
to close all your up streams (down streams will be closed by the server). have been closed by the time it is called. The `onusermessage` callback
The `onusermessage` callback indicates an application-specific message, indicates an application-specific message, either from another user or
either from another user or from the server; the field `kind` indicates from the server; the field `kind` indicates the kind of message.
the kind of message.
Once you have joined a group (see below), the remaining callbacks may Once you have joined a group (see below), the remaining callbacks may
trigger. The `onuser` callback is used to indicate that a user has joined trigger. The `onuser` callback is used to indicate that a user has joined
...@@ -126,8 +125,8 @@ a change in the status of the stream; states `connected` and `complete` ...@@ -126,8 +125,8 @@ a change in the status of the stream; states `connected` and `complete`
indicate a functioning stream; other states indicate that the stream is indicate a functioning stream; other states indicate that the stream is
not working right now but might recover in the future. not working right now but might recover in the future.
The `onclose` callback is called when the stream is destroyed by the The `onclose` callback is called when the stream is destroyed, either by
server. the server or in response to a call to the `close` method.
## Pushing outgoing video streams ## Pushing outgoing video streams
......
...@@ -309,7 +309,6 @@ function gotConnected() { ...@@ -309,7 +309,6 @@ function gotConnected() {
* @param {string} reason * @param {string} reason
*/ */
function gotClose(code, reason) { function gotClose(code, reason) {
delUpMediaKind(null);
setConnected(false); setConnected(false);
if(code != 1000) { if(code != 1000) {
console.warn('Socket close', code, reason); console.warn('Socket close', code, reason);
...@@ -374,7 +373,7 @@ getButtonElement('presentbutton').onclick = async function(e) { ...@@ -374,7 +373,7 @@ getButtonElement('presentbutton').onclick = async function(e) {
getButtonElement('unpresentbutton').onclick = function(e) { getButtonElement('unpresentbutton').onclick = function(e) {
e.preventDefault(); e.preventDefault();
delUpMediaKind('local'); closeUpMediaKind('local');
resizePeers(); resizePeers();
}; };
...@@ -482,13 +481,13 @@ document.getElementById('sharebutton').onclick = function(e) { ...@@ -482,13 +481,13 @@ document.getElementById('sharebutton').onclick = function(e) {
document.getElementById('unsharebutton').onclick = function(e) { document.getElementById('unsharebutton').onclick = function(e) {
e.preventDefault(); e.preventDefault();
delUpMediaKind('screenshare'); closeUpMediaKind('screenshare');
resizePeers(); resizePeers();
}; };
document.getElementById('stopvideobutton').onclick = function(e) { document.getElementById('stopvideobutton').onclick = function(e) {
e.preventDefault(); e.preventDefault();
delUpMediaKind('video'); closeUpMediaKind('video');
resizePeers(); resizePeers();
}; };
...@@ -757,10 +756,6 @@ function newUpStream(id) { ...@@ -757,10 +756,6 @@ function newUpStream(id) {
c.onerror = function(e) { c.onerror = function(e) {
console.error(e); console.error(e);
displayError(e); displayError(e);
delUpMedia(c);
};
c.onabort = function() {
delUpMedia(c);
}; };
c.onnegotiationcompleted = function() { c.onnegotiationcompleted = function() {
setMaxVideoThroughput(c, getMaxVideoThroughput()); setMaxVideoThroughput(c, getMaxVideoThroughput());
...@@ -928,23 +923,16 @@ function setFilter(c, f) { ...@@ -928,23 +923,16 @@ function setFilter(c, f) {
c.userdata.filter.stop(); c.userdata.filter.stop();
c.userdata.filter = null; c.userdata.filter = null;
} }
if(!c.onclose) {
console.warn("onclose not set, this shouldn't happen");
c.onclose = null;
}
return return
} }
if(c.userdata.filter) if(c.userdata.filter)
setFilter(c, null); setFilter(c, null);
if(c.onclose)
throw new Error('onclose already taken');
if(f.inputStream != c.stream) if(f.inputStream != c.stream)
throw new Error('Setting filter for wrong stream'); throw new Error('Setting filter for wrong stream');
c.stream = f.outputStream; c.stream = f.outputStream;
c.userdata.filter = f; c.userdata.filter = f;
c.onclose = () => setFilter(c, null);
} }
/** /**
...@@ -1028,13 +1016,10 @@ async function addLocalMedia(id) { ...@@ -1028,13 +1016,10 @@ async function addLocalMedia(id) {
if(!audio && !video) { if(!audio && !video) {
if(old) if(old)
delUpMedia(old); old.close();
return; return;
} }
if(old)
stopUpMedia(old);
let constraints = {audio: audio, video: video}; let constraints = {audio: audio, video: video};
/** @type {MediaStream} */ /** @type {MediaStream} */
let stream = null; let stream = null;
...@@ -1043,7 +1028,7 @@ async function addLocalMedia(id) { ...@@ -1043,7 +1028,7 @@ async function addLocalMedia(id) {
} catch(e) { } catch(e) {
displayError(e); displayError(e);
if(old) if(old)
delUpMedia(old); old.close();
return; return;
} }
...@@ -1058,8 +1043,22 @@ async function addLocalMedia(id) { ...@@ -1058,8 +1043,22 @@ async function addLocalMedia(id) {
try { try {
let f = new Filter(stream, filter); let f = new Filter(stream, filter);
setFilter(c, f); setFilter(c, f);
c.onclose = () => {
stopStream(stream);
setFilter(c, null);
delMedia(c.id);
}
} catch(e) { } catch(e) {
displayWarning(e); displayWarning(e);
c.onclose = () => {
stopStream(c.stream);
delMedia(c.id);
}
}
} else {
c.onclose = () => {
stopStream(c.stream);
delMedia(c.id);
} }
} }
...@@ -1110,11 +1109,13 @@ async function addShareMedia() { ...@@ -1110,11 +1109,13 @@ async function addShareMedia() {
let c = newUpStream(); let c = newUpStream();
c.kind = 'screenshare'; c.kind = 'screenshare';
c.stream = stream; c.stream = stream;
c.onclose = () => {
stopStream(stream);
delMedia(c.id);
}
stream.getTracks().forEach(t => { stream.getTracks().forEach(t => {
c.pc.addTrack(t, stream); c.pc.addTrack(t, stream);
t.onended = e => { t.onended = e => c.close();
delUpMedia(c);
};
c.labels[t.id] = 'screenshare'; c.labels[t.id] = 'screenshare';
}); });
c.onstats = gotUpStats; c.onstats = gotUpStats;
...@@ -1144,12 +1145,14 @@ async function addFileMedia(file) { ...@@ -1144,12 +1145,14 @@ async function addFileMedia(file) {
c.kind = 'video'; c.kind = 'video';
c.stream = stream; c.stream = stream;
c.onclose = function() { c.onclose = function() {
stopStream(c.stream);
let media = /** @type{HTMLVideoElement} */ let media = /** @type{HTMLVideoElement} */
(document.getElementById('media-' + this.id)); (document.getElementById('media-' + this.id));
if(media && media.src) { if(media && media.src) {
URL.revokeObjectURL(media.src); URL.revokeObjectURL(media.src);
media.src = null; media.src = null;
} }
delMedia(c.id);
} }
stream.onaddtrack = function(e) { stream.onaddtrack = function(e) {
...@@ -1186,7 +1189,7 @@ async function addFileMedia(file) { ...@@ -1186,7 +1189,7 @@ async function addFileMedia(file) {
if(Object.keys(c.labels).length === 0) { if(Object.keys(c.labels).length === 0) {
stream.onaddtrack = null; stream.onaddtrack = null;
stream.onremovetrack = null; stream.onremovetrack = null;
delUpMedia(c); c.close();
} }
}; };
await setMedia(c, true, false, video); await setMedia(c, true, false, video);
...@@ -1195,13 +1198,10 @@ async function addFileMedia(file) { ...@@ -1195,13 +1198,10 @@ async function addFileMedia(file) {
} }
/** /**
* @param {Stream} c * @param {MediaStream} s
*/ */
function stopUpMedia(c) { function stopStream(s) {
setFilter(c, null); s.getTracks().forEach(t => {
if(!c.stream)
return;
c.stream.getTracks().forEach(t => {
try { try {
t.stop(); t.stop();
} catch(e) { } catch(e) {
...@@ -1211,31 +1211,18 @@ function stopUpMedia(c) { ...@@ -1211,31 +1211,18 @@ function stopUpMedia(c) {
} }
/** /**
* @param {Stream} c * closeUpMediaKind closes all up connections that correspond to a given
*/ * kind of media. If kind is null, it closes all up connections.
function delUpMedia(c) { *
stopUpMedia(c);
c.close();
delMedia(c.id);
delete(serverConnection.up[c.id]);
setButtonsVisibility();
}
/**
* delUpMediaKind reoves all up media of the given kind. If kind is
* falseish, it removes all up media.
* @param {string} kind * @param {string} kind
*/ */
function delUpMediaKind(kind) { function closeUpMediaKind(kind) {
for(let id in serverConnection.up) { for(let id in serverConnection.up) {
let c = serverConnection.up[id]; let c = serverConnection.up[id];
if(kind && c.kind != kind) if(kind && c.kind != kind)
continue continue
delUpMedia(c); c.close();
} }
setButtonsVisibility();
hideVideo();
} }
/** /**
...@@ -1501,6 +1488,7 @@ function delMedia(id) { ...@@ -1501,6 +1488,7 @@ function delMedia(id) {
media.srcObject = null; media.srcObject = null;
mediadiv.removeChild(peer); mediadiv.removeChild(peer);
setButtonsVisibility();
resizePeers(); resizePeers();
hideVideo(); hideVideo();
} }
......
...@@ -238,12 +238,10 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -238,12 +238,10 @@ ServerConnection.prototype.connect = async function(url) {
sc.permissions = {}; sc.permissions = {};
for(let id in sc.up) { for(let id in sc.up) {
let c = sc.up[id]; let c = sc.up[id];
delete(sc.up[id]);
c.close(); c.close();
} }
for(let id in sc.down) { for(let id in sc.down) {
let c = sc.down[id]; let c = sc.down[id];
delete(sc.down[id]);
c.close(); c.close();
} }
if(sc.group && sc.onjoined) if(sc.group && sc.onjoined)
...@@ -408,9 +406,9 @@ ServerConnection.prototype.newUpStream = function(id) { ...@@ -408,9 +406,9 @@ ServerConnection.prototype.newUpStream = function(id) {
let pc = new RTCPeerConnection(sc.rtcConfiguration); let pc = new RTCPeerConnection(sc.rtcConfiguration);
if(!pc) if(!pc)
throw new Error("Couldn't create peer connection"); throw new Error("Couldn't create peer connection");
if(sc.up[id]) { if(sc.up[id])
sc.up[id].close(); sc.up[id].close();
}
let c = new Stream(this, id, pc, true); let c = new Stream(this, id, pc, true);
sc.up[id] = c; sc.up[id] = c;
...@@ -530,7 +528,6 @@ ServerConnection.prototype.gotOffer = async function(id, labels, source, usernam ...@@ -530,7 +528,6 @@ ServerConnection.prototype.gotOffer = async function(id, labels, source, usernam
// SDP is rather inflexible as to what can be renegotiated. // SDP is rather inflexible as to what can be renegotiated.
// Unless the server indicates that this is a renegotiation with // Unless the server indicates that this is a renegotiation with
// all parameters unchanged, tear down the existing connection. // all parameters unchanged, tear down the existing connection.
delete(sc.down[id]);
c.close(true); c.close(true);
c = null; c = null;
} }
...@@ -669,7 +666,6 @@ ServerConnection.prototype.gotClose = function(id) { ...@@ -669,7 +666,6 @@ ServerConnection.prototype.gotClose = function(id) {
let c = this.down[id]; let c = this.down[id];
if(!c) if(!c)
throw new Error('unknown down stream'); throw new Error('unknown down stream');
delete(this.down[id]);
c.close(); c.close();
}; };
...@@ -888,19 +884,17 @@ function Stream(sc, id, pc, up) { ...@@ -888,19 +884,17 @@ function Stream(sc, id, pc, up) {
*/ */
Stream.prototype.close = function(nocallback) { Stream.prototype.close = function(nocallback) {
let c = this; let c = this;
if(!c.sc) {
console.warn('Closing closed stream');
return;
}
if(c.statsHandler) { if(c.statsHandler) {
clearInterval(c.statsHandler); clearInterval(c.statsHandler);
c.statsHandler = null; c.statsHandler = null;
} }
if(c.stream) {
c.stream.getTracks().forEach(t => {
try {
t.stop();
} catch(e) {
}
});
}
c.pc.close(); c.pc.close();
if(c.up && c.localDescriptionSent) { if(c.up && c.localDescriptionSent) {
...@@ -913,9 +907,21 @@ Stream.prototype.close = function(nocallback) { ...@@ -913,9 +907,21 @@ Stream.prototype.close = function(nocallback) {
} }
} }
if(c.up) {
if(c.sc.up[c.id] === c)
delete(c.sc.up[c.id]);
else
console.warn('Closing unknown stream');
} else {
if(c.sc.down[c.id] === c)
delete(c.sc.down[c.id]);
else
console.warn('Closing unknown stream');
}
c.sc = null;
if(!nocallback && c.onclose) if(!nocallback && c.onclose)
c.onclose.call(c); c.onclose.call(c);
c.sc = null;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment