Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript DOMException: The associated Track is in an invalid state

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.

like image 907
we_mor Avatar asked Mar 03 '23 17:03

we_mor


2 Answers

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);
  }
like image 85
fomasha Avatar answered Mar 05 '23 10:03

fomasha


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.

like image 41
hunterloftis Avatar answered Mar 05 '23 09:03

hunterloftis