Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block background throttling of video processing #2746

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Do not display a warning message when invoking `meetingSession.audioVideo.setVideoCodecSendPreferences` prior to the start of the session.
- Prevent video processing with filters from being throttled when an attendees meeting tab moves into the background.

## [3.16.0] - 2023-06-26

Expand Down
58 changes: 54 additions & 4 deletions src/videoframeprocessor/DefaultVideoFrameProcessorPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,52 @@ export default class DefaultVideoFrameProcessorPipeline implements VideoFramePro

private hasStarted: boolean = false;
private lastTimeOut: ReturnType<typeof setTimeout> | undefined;
// Timer on worker thread to overcome background throttling
private workerTimer: Worker;
private isWorkerTimerSupported: boolean = true;

constructor(private logger: Logger, private stages: VideoFrameProcessor[]) {}
constructor(private logger: Logger, private stages: VideoFrameProcessor[]) {
this.workerTimer = this.createWorkerTimer();
}

/**
* Create a timer that exists within a web worker. This timer will be used to
* retrigger the process call whenever time expires.
*/
private createWorkerTimer(): Worker {
try {
// Blob representing a script that will start a timer for the length
// of the message posted to it. After timer expiration, it will post
// a message back to the main thread holding the timerID
const timerBlob = new Blob(
[
`self.onmessage = async function(e){
var timerID = null;
const awaitTimeout = delay => new Promise( resolve => {
timerID = setTimeout(resolve, delay);
})
await awaitTimeout(e.data);
postMessage(timerID);
}`,
],
{ type: 'application/javascript' }
);
// Create the worker and link our process call to execute on
// every message it posts
const worker = new Worker(window.URL.createObjectURL(timerBlob));
worker.onmessage = event => {
// Store used timer so we can clear it during device closure
this.lastTimeOut = event.data;
// Reprocess a new frame on every timer completion
this.process(event);
};
return worker;
// If blob: is not passed as a worker-src to csp, then the
// worker timer will fail... therefore no support
} catch (error) {
this.isWorkerTimerSupported = false;
}
}

destroy(): void {
this.stop();
Expand All @@ -55,6 +99,7 @@ export default class DefaultVideoFrameProcessorPipeline implements VideoFramePro
stage.destroy();
}
}
this.workerTimer?.terminate();
}

get framerate(): number {
Expand Down Expand Up @@ -286,9 +331,14 @@ export default class DefaultVideoFrameProcessorPipeline implements VideoFramePro
});
}

// TODO: use requestAnimationFrame which is more organic and allows browser to conserve resources by its choices.
/* @ts-ignore */
this.lastTimeOut = setTimeout(this.process, nextFrameDelay);
// Post frame delay as message to worker timer to maintain desired fps
if (this.isWorkerTimerSupported) {
this.workerTimer.postMessage(nextFrameDelay);
// If the worker isn't supported, use the timer on main thread
} else {
/* @ts-ignore */
this.lastTimeOut = setTimeout(this.process, nextFrameDelay);
}
};

private forEachObserver(
Expand Down
1 change: 1 addition & 0 deletions test/dommock/DOMMockBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ export default class DOMMockBuilder {
}
};

GlobalAny.Blob = DOMBlobMock;
GlobalAny.matchMedia = function mockMatchMedia(_query: string): MediaQueryList {
return new GlobalAny.MediaQueryList();
};
Expand Down
Loading
Loading