mirror of
https://github.com/jech/galene.git
synced 2024-12-22 15:25:48 +01:00
Make filters run asynchronously.
Drop a frame if a filter takes too long to run.
This commit is contained in:
parent
94e7e9c147
commit
38807d5f6b
1 changed files with 47 additions and 34 deletions
|
@ -1025,9 +1025,9 @@ function cancelReconsiderParameters() {
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} filterDefinition
|
* @typedef {Object} filterDefinition
|
||||||
* @property {string} [description]
|
* @property {string} [description]
|
||||||
* @property {(this: Filter) => void} [init]
|
* @property {(this: Filter) => Promise<void>} [init]
|
||||||
* @property {(this: Filter) => void} [cleanup]
|
* @property {(this: Filter) => Promise<void>} [cleanup]
|
||||||
* @property {(this: Filter, src: HTMLVideoElement, ctx: CanvasRenderingContext2D) => boolean} draw
|
* @property {(this: Filter, src: HTMLVideoElement, ctx: RenderingContext) => Promise<boolean>} draw
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1067,9 +1067,11 @@ function Filter(stream, definition) {
|
||||||
this.userdata = {}
|
this.userdata = {}
|
||||||
/** @type {MediaStream} */
|
/** @type {MediaStream} */
|
||||||
this.captureStream = this.canvas.captureStream(0);
|
this.captureStream = this.canvas.captureStream(0);
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Filter.prototype.start = function() {
|
Filter.prototype.start = async function() {
|
||||||
/** @ts-ignore */
|
/** @ts-ignore */
|
||||||
if(!this.captureStream.getTracks()[0].requestFrame) {
|
if(!this.captureStream.getTracks()[0].requestFrame) {
|
||||||
console.warn('captureFrame not supported, using fixed framerate');
|
console.warn('captureFrame not supported, using fixed framerate');
|
||||||
|
@ -1089,11 +1091,11 @@ Filter.prototype.start = function() {
|
||||||
this.video.muted = true;
|
this.video.muted = true;
|
||||||
this.video.play();
|
this.video.play();
|
||||||
if(this.definition.init)
|
if(this.definition.init)
|
||||||
this.definition.init.call(this);
|
await this.definition.init.call(this);
|
||||||
this.timer = setInterval(() => this.draw(), 1000 / this.frameRate);
|
this.timer = setInterval(() => this.draw(), 1000 / this.frameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
Filter.prototype.draw = function() {
|
Filter.prototype.draw = async function() {
|
||||||
if(this.video.videoWidth === 0 && this.video.videoHeight === 0) {
|
if(this.video.videoWidth === 0 && this.video.videoHeight === 0) {
|
||||||
// video not started yet
|
// video not started yet
|
||||||
return;
|
return;
|
||||||
|
@ -1115,9 +1117,18 @@ Filter.prototype.draw = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.busy) {
|
||||||
|
// drop frame
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.busy = true;
|
||||||
let ok = false;
|
let ok = false;
|
||||||
try {
|
try {
|
||||||
ok = this.definition.draw.call(this, this.video, this.context);
|
ok = await this.definition.draw.call(
|
||||||
|
this, this.video, this.context,
|
||||||
|
);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
@ -1125,18 +1136,20 @@ Filter.prototype.draw = function() {
|
||||||
/** @ts-ignore */
|
/** @ts-ignore */
|
||||||
this.captureStream.getTracks()[0].requestFrame();
|
this.captureStream.getTracks()[0].requestFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.count++;
|
this.count++;
|
||||||
|
} finally {
|
||||||
|
this.busy = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Filter.prototype.stop = function() {
|
Filter.prototype.stop = async function() {
|
||||||
if(!this.timer)
|
if(!this.timer)
|
||||||
return;
|
return;
|
||||||
this.captureStream.getTracks()[0].stop();
|
this.captureStream.getTracks()[0].stop();
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
if(this.definition.cleanup)
|
if(this.definition.cleanup)
|
||||||
this.definition.cleanup.call(this);
|
await this.definition.cleanup.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1144,7 +1157,7 @@ Filter.prototype.stop = function() {
|
||||||
*
|
*
|
||||||
* @param {Stream} c
|
* @param {Stream} c
|
||||||
*/
|
*/
|
||||||
function removeFilter(c) {
|
async function removeFilter(c) {
|
||||||
let old = c.userdata.filter;
|
let old = c.userdata.filter;
|
||||||
if(!old)
|
if(!old)
|
||||||
return;
|
return;
|
||||||
|
@ -1153,7 +1166,7 @@ function removeFilter(c) {
|
||||||
throw new Error('userdata.filter is not a filter');
|
throw new Error('userdata.filter is not a filter');
|
||||||
|
|
||||||
c.setStream(old.inputStream);
|
c.setStream(old.inputStream);
|
||||||
old.stop();
|
await old.stop();
|
||||||
c.userdata.filter = null;
|
c.userdata.filter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1162,14 +1175,14 @@ function removeFilter(c) {
|
||||||
*
|
*
|
||||||
* @param {Stream} c
|
* @param {Stream} c
|
||||||
*/
|
*/
|
||||||
function setFilter(c) {
|
async function setFilter(c) {
|
||||||
removeFilter(c);
|
await removeFilter(c);
|
||||||
|
|
||||||
if(!c.userdata.filterDefinition)
|
if(!c.userdata.filterDefinition)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let filter = new Filter(c.stream, c.userdata.filterDefinition);
|
let filter = new Filter(c.stream, c.userdata.filterDefinition);
|
||||||
filter.start();
|
await filter.start();
|
||||||
c.setStream(filter.outputStream);
|
c.setStream(filter.outputStream);
|
||||||
c.userdata.filter = filter;
|
c.userdata.filter = filter;
|
||||||
}
|
}
|
||||||
|
@ -1180,7 +1193,7 @@ function setFilter(c) {
|
||||||
let filters = {
|
let filters = {
|
||||||
'mirror-h': {
|
'mirror-h': {
|
||||||
description: "Horizontal mirror",
|
description: "Horizontal mirror",
|
||||||
draw: function(src, ctx) {
|
draw: async function(src, ctx) {
|
||||||
if(!(ctx instanceof CanvasRenderingContext2D))
|
if(!(ctx instanceof CanvasRenderingContext2D))
|
||||||
throw new Error('bad context type');
|
throw new Error('bad context type');
|
||||||
if(ctx.canvas.width !== src.videoWidth ||
|
if(ctx.canvas.width !== src.videoWidth ||
|
||||||
|
@ -1196,7 +1209,7 @@ let filters = {
|
||||||
},
|
},
|
||||||
'mirror-v': {
|
'mirror-v': {
|
||||||
description: "Vertical mirror",
|
description: "Vertical mirror",
|
||||||
draw: function(src, ctx) {
|
draw: async function(src, ctx) {
|
||||||
if(!(ctx instanceof CanvasRenderingContext2D))
|
if(!(ctx instanceof CanvasRenderingContext2D))
|
||||||
throw new Error('bad context type');
|
throw new Error('bad context type');
|
||||||
if(ctx.canvas.width !== src.videoWidth ||
|
if(ctx.canvas.width !== src.videoWidth ||
|
||||||
|
@ -1259,20 +1272,20 @@ function doSimulcast() {
|
||||||
* @param {MediaStream} stream
|
* @param {MediaStream} stream
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function setUpStream(c, stream) {
|
async function setUpStream(c, stream) {
|
||||||
if(c.stream != null)
|
if(c.stream != null)
|
||||||
throw new Error("Setting nonempty stream");
|
throw new Error("Setting nonempty stream");
|
||||||
|
|
||||||
c.setStream(stream);
|
c.setStream(stream);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setFilter(c);
|
await setFilter(c);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
displayWarning("Couldn't set filter: " + e);
|
displayWarning("Couldn't set filter: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.onclose = replace => {
|
c.onclose = async replace => {
|
||||||
removeFilter(c);
|
await removeFilter(c);
|
||||||
if(!replace) {
|
if(!replace) {
|
||||||
stopStream(c.stream);
|
stopStream(c.stream);
|
||||||
if(c.userdata.onclose)
|
if(c.userdata.onclose)
|
||||||
|
@ -1394,7 +1407,7 @@ function setUpStream(c, stream) {
|
||||||
* @returns {Promise<Stream>}
|
* @returns {Promise<Stream>}
|
||||||
*/
|
*/
|
||||||
async function replaceUpStream(c) {
|
async function replaceUpStream(c) {
|
||||||
removeFilter(c);
|
await removeFilter(c);
|
||||||
let cn = newUpStream(c.localId);
|
let cn = newUpStream(c.localId);
|
||||||
cn.label = c.label;
|
cn.label = c.label;
|
||||||
if(c.userdata.filterDefinition)
|
if(c.userdata.filterDefinition)
|
||||||
|
@ -1403,7 +1416,7 @@ async function replaceUpStream(c) {
|
||||||
cn.userdata.onclose = c.userdata.onclose;
|
cn.userdata.onclose = c.userdata.onclose;
|
||||||
let media = /** @type{HTMLVideoElement} */
|
let media = /** @type{HTMLVideoElement} */
|
||||||
(document.getElementById('media-' + c.localId));
|
(document.getElementById('media-' + c.localId));
|
||||||
setUpStream(cn, c.stream);
|
await setUpStream(cn, c.stream);
|
||||||
await setMedia(cn,
|
await setMedia(cn,
|
||||||
cn.label == 'camera' && getSettings().mirrorView,
|
cn.label == 'camera' && getSettings().mirrorView,
|
||||||
cn.label == 'video' && media);
|
cn.label == 'video' && media);
|
||||||
|
@ -1471,7 +1484,7 @@ async function addLocalMedia(localId) {
|
||||||
let old = serverConnection.findByLocalId(localId);
|
let old = serverConnection.findByLocalId(localId);
|
||||||
if(old) {
|
if(old) {
|
||||||
// make sure that the camera is released before we try to reopen it
|
// make sure that the camera is released before we try to reopen it
|
||||||
removeFilter(old);
|
await removeFilter(old);
|
||||||
stopStream(old.stream);
|
stopStream(old.stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1507,7 +1520,7 @@ async function addLocalMedia(localId) {
|
||||||
displayWarning(`Unknown filter ${settings.filter}`);
|
displayWarning(`Unknown filter ${settings.filter}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpStream(c, stream);
|
await setUpStream(c, stream);
|
||||||
await setMedia(c, settings.mirrorView);
|
await setMedia(c, settings.mirrorView);
|
||||||
setButtonsVisibility();
|
setButtonsVisibility();
|
||||||
}
|
}
|
||||||
|
@ -1546,7 +1559,7 @@ async function addShareMedia() {
|
||||||
|
|
||||||
let c = newUpStream();
|
let c = newUpStream();
|
||||||
c.label = 'screenshare';
|
c.label = 'screenshare';
|
||||||
setUpStream(c, stream);
|
await setUpStream(c, stream);
|
||||||
await setMedia(c);
|
await setMedia(c);
|
||||||
setButtonsVisibility();
|
setButtonsVisibility();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue