1
Fork 0

Request low-resolution video when the video is small.

This commit is contained in:
Juliusz Chroboczek 2021-05-14 18:44:49 +02:00
parent 2da8faa8cf
commit 54cd546120
1 changed files with 136 additions and 3 deletions

View File

@ -275,6 +275,7 @@ function hideVideo(force) {
if(mediadiv.childElementCount > 0 && !force) if(mediadiv.childElementCount > 0 && !force)
return; return;
setVisibility('video-container', false); setVisibility('video-container', false);
scheduleReconsiderDownRate();
} }
function showVideo() { function showVideo() {
@ -284,6 +285,7 @@ function showVideo() {
setVisibility('collapse-video', hasmedia); setVisibility('collapse-video', hasmedia);
} }
setVisibility('video-container', hasmedia); setVisibility('video-container', hasmedia);
scheduleReconsiderDownRate();
} }
function fillLogin() { function fillLogin() {
@ -305,12 +307,16 @@ function setConnected(connected) {
userbox.classList.remove('invisible'); userbox.classList.remove('invisible');
connectionbox.classList.add('invisible'); connectionbox.classList.add('invisible');
displayUsername(); displayUsername();
window.onresize = function(e) {
scheduleReconsiderDownRate();
}
} else { } else {
fillLogin(); fillLogin();
userbox.classList.add('invisible'); userbox.classList.add('invisible');
connectionbox.classList.remove('invisible'); connectionbox.classList.remove('invisible');
displayError('Disconnected', 'error'); displayError('Disconnected', 'error');
hideVideo(); hideVideo();
window.onresize = null;
} }
} }
@ -580,17 +586,32 @@ function mapRequest(what) {
return {'': ['audio','video']} return {'': ['audio','video']}
break; break;
default: default:
displayError(`Unknown value ${what} in request`); throw new Error(`Unknown value ${what} in request`);
return {};
} }
} }
/**
* @param {string} what
* @param {string} label
* @returns {Array<string>}
*/
function mapRequestLabel(what, label) {
let r = mapRequest(what);
if(label in r)
return r[label];
else
return r[''];
}
getSelectElement('requestselect').onchange = function(e) { getSelectElement('requestselect').onchange = function(e) {
e.preventDefault(); e.preventDefault();
if(!(this instanceof HTMLSelectElement)) if(!(this instanceof HTMLSelectElement))
throw new Error('Unexpected type for this'); throw new Error('Unexpected type for this');
updateSettings({request: this.value}); updateSettings({request: this.value});
serverConnection.request(mapRequest(this.value)); serverConnection.request(mapRequest(this.value));
reconsiderDownRate();
}; };
const activityDetectionInterval = 200; const activityDetectionInterval = 200;
@ -1482,6 +1503,113 @@ function muteLocalTracks(mute) {
} }
} }
/**
* @param {string} id
* @param {boolean} force
* @param {boolean} [value]
*/
function forceDownRate(id, force, value) {
let c = serverConnection.down[id];
if(!c)
throw new Error("Unknown down stream");
if('requested' in c.userdata) {
if(force)
c.userdata.requested.force = !!value;
else
delete(c.userdata.requested.force);
} else {
if(force)
c.userdata.requested = {force: value};
}
reconsiderDownRate(id);
}
/**
* Maps 'video' to 'video-low'. Returns null if nothing changed.
*
* @param {string[]} requested
* @returns {string[]}
*/
function mapVideoToLow(requested) {
let result = [];
let found = false;
for(let i = 0; i < requested.length; i++) {
let r = requested[i];
if(r === 'video') {
r = 'video-low';
found = true;
}
result.push(r);
}
if(!found)
return null;
return result;
}
/**
* Reconsider the video track requested for a given down stream.
*
* @param {string} [id] - the id of the track to reconsider, all if null.
*/
function reconsiderDownRate(id) {
if(!serverConnection)
return;
if(!id) {
for(let id in serverConnection.down) {
reconsiderDownRate(id);
}
return;
}
let c = serverConnection.down[id];
if(!c)
throw new Error("Unknown down stream");
let normalrequest = mapRequestLabel(getSettings().request, c.label);
let requestlow = mapVideoToLow(normalrequest);
if(requestlow === null)
return;
let old = c.userdata.requested;
let low = false;
if(old && ('force' in old)) {
low = old.force;
} else {
let media = /** @type {HTMLVideoElement} */
(document.getElementById('media-' + c.localId));
if(!media)
throw new Error("No media for stream");
let w = media.scrollWidth;
let h = media.scrollHeight;
if(w && h && w * h <= 320 * 240) {
low = true;
}
}
if(low !== !!(old && old.low)) {
if('requested' in c.userdata)
c.userdata.requested.low = low;
else
c.userdata.requested = {low: low};
c.request(low ? requestlow : null);
}
}
let reconsiderDownRateTimer = null;
/**
* Schedules reconsiderDownRate() to be run later. The delay avoids too
* much recomputations when resizing the window.
*/
function scheduleReconsiderDownRate() {
if(reconsiderDownRateTimer)
return;
reconsiderDownRateTimer =
setTimeout(() => {
reconsiderDownRateTimer = null;
reconsiderDownRate();
}, 200);
}
/** /**
* setMedia adds a new media element corresponding to stream c. * setMedia adds a new media element corresponding to stream c.
* *
@ -1533,6 +1661,12 @@ async function setMedia(c, isUp, mirror, video) {
if(!video && media.srcObject !== c.stream) if(!video && media.srcObject !== c.stream)
media.srcObject = c.stream; media.srcObject = c.stream;
if(!isUp) {
media.onfullscreenchange = function(e) {
forceDownRate(c.id, document.fullscreenElement === media, false);
}
}
let label = document.getElementById('label-' + c.localId); let label = document.getElementById('label-' + c.localId);
if(!label) { if(!label) {
label = document.createElement('div'); label = document.createElement('div');
@ -2936,7 +3070,6 @@ function start() {
fillLogin(); fillLogin();
document.getElementById("login-container").classList.remove('invisible'); document.getElementById("login-container").classList.remove('invisible');
setViewportHeight(); setViewportHeight();
} }