I'm trying to create a photo capture web app on a nodeJS server, and i'm using the javascript code below.
const btn = document.querySelector('#btn');
btn.addEventListener('click', (event) => {
navigator.mediaDevices.getUserMedia({video: true})
.then(gotMedia)
.catch(error => console.error('getUserMedia() error:', error));
event.preventDefault()
})
And the gotMedia function is this:
function gotMedia(mediaStream) {
const mediaStreamTrack = mediaStream.getVideoTracks()[0];
const imageCapture = new ImageCapture(mediaStreamTrack);
console.log(imageCapture);
const canvas = document.querySelector('canvas');
// ...
imageCapture.grabFrame()
.then(imageBitmap => {
canvas.width = imageBitmap.width;
canvas.height = imageBitmap.height;
canvas.getContext('2d').drawImage(imageBitmap, 0, 0);
})
.catch(error => console.error('grabFrame() error:', error));
}
Everything works fine, the image capturing is ok, but an error occurs, when i snap photos rapidly one after another, that says:
grabFrame() error: DOMException: The associated Track is in an invalid state.
This usually happens when i capture too many photos (like clicking rapidly for about more than 20 seconds), but it also has happened on the first five snapshots. Does anyone know what's going on and what should i change, in order to fix this? Thank you for your time.
According to the spec such error could be result of not acceptable state. In chromium sources I have found this method.
I've overcame error using code like this:
const promise = getPrevImagePromise();
if (!promise && !(imageCapture.track.readyState != 'live' || !imageCapture.track.enabled || imageCapture.track.muted)) {
const imagePromise = imageCapture.grabFrame()
.then((image) => {
// do work with image
})
.then(() => {
deletePrevImagePromise()
})
.catch((error) => {
//
});
setPrevImagePromise(imagePromise);
}
I ran into the same issue and was unable to get the bleeding-edge grabFrame
function to work reliably.
Instead, you can draw from an intermediate video source. In your function, that would look something like this (untested):
function gotMedia(mediaStream) {
const mediaStreamTrack = mediaStream.getVideoTracks()[0];
const canvas = document.querySelector('canvas');
const video = document.createElement('video');
video.autoplay = true;
video.srcObject = mediaStream;
video.onplay = () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
};
}
Of course, if you're going to be capturing a lot of frames, it'd be best to create a single video alongside the stream and to capture from that each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With