-
Notifications
You must be signed in to change notification settings - Fork 40
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
onframe Event #210
Comments
I made a rough implementation of this in Chromium for my own testing purposes, and quickly realized he importance of ensuring the events don't tie up too much memory. Here's a proposed change to the spec to accomplish that: partial interface ImageCapture {
attribute EventHandler onframe;
}
[Exposed=Window]
interface ImageCaptureFrameEvent : Event {
[NewObject] Promise<ImageBitmap> createImageBitmap();
}; I left The promise for There's a lot of benefit to having a Below is an example of how to run class ThrottledImageCapture {
constructor (capturer) {
this.bufferCount_ = 2;
this.buffers_ = [];
this.dropCount_ = 0;
this.discardCount_ = 0;
this.pendingGrabFramePromise_ = null;
capturer.addEventListener('frame', async (event) => {
if (this.buffers_.length === this.bufferCount_) {
// We ran out of buffers, so we had to
// drop a frame due to being too slow
const frame = this.buffers_.shift();
frame.close(); // Dispose of memory
this.dropCount_++;
}
try {
this.buffers_.push(await event.createImageBitmap());
} catch {
// JS execution was too slow and the browser
// discarded the data for the frame before we
// could get it as an ImageBitmap
this.discardCount_++;
return;
}
if (this.pendingGrabFramePromise_) {
// Pending promise, consume the latest frame
this.pendingGrabFramePromise_.resolve(this.buffers_.shift());
this.pendingGrabFramePromise_ = null;
}
});
}
grabFrame () {
if (this.buffers_.length) {
// Frame available, so consume it
return Promise.resolve(this.buffers_.shift());
}
// No frame available, wait for next one. Reuse
// promise if already created
if (this.pendingGrabFramePromise_) {
return this.pendingGrabFramePromise_.promise;
}
const promise = new Promise((resolve, reject) => {
this.pendingGrabFramePromise_ = { resolve, reject };
});
this.pendingGrabFramePromise_.promise = promise;
return promise;
}
} |
Another example of how an Currently if a next frame never comes, the Chromium implementation will leave the promise from With the above class ThrottledImageCapture {
constructor (capturer) {
// See above for full constructor, keeping abbreviated
this.streamEnded_ = false;
capturer.track.addEventListener('ended', () => {
this.streamEnded_ = true;
this.pendingGrabFramePromise_.reject('Stream ended');
}
}
grabFrame () {
if (this.streamEnded_) {
return Promise.reject('Stream ended');
}
// Rest of implementation
} It really seems like an |
The current Chromium implementation of
grabFrame
waits until a new frame is available before resolving, which means ifframeRate
is 1, it may take up to 1 second for the promise to resolve.Although the spec doesn't have anything to say on the timing of
grabFrame
, I think this is unexpected behavior, and should probably be changed in Chromium, and clarified in the spec.However, there is a nice benefit to that behavior that I noticed: it throttles
grabFrame
for you automatically, so you can nicely use a loop withawait
on eachgrabFrame
and it will run at the frame rate of the video, without needing to manage this yourself.So, to retain this useful behavior, how about adding an
onframe
event toImageCapture
in the spec? It wouldThis would also be flexible enough to easily create a
Promise
version which emulates the current ChromiumgrabFrame
behavior:The text was updated successfully, but these errors were encountered: