Search code examples
javascripthtml5-videogetusermediamediastream

How to clear html video element when removing track


The following code does pretty much what I want:

video = document.querySelector('video');
video.srcObject = new MediaStream();

navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
  stream.getTracks().forEach(track => {
    video.srcObject.addTrack(track);
  });
});

setTimeout(() => {
  video.srcObject.getVideoTracks().forEach(track => {
    track.stop()
    video.srcObject.removeTrack(track);
  });
}, 5000);

It starts the camera and displays the result on screen. After 5 seconds the camera is stopped again. However, the last image from the camera remains as a freeze frame in the video. How can I clear the video element?

(Note that I only remove the video tracks, so there might still be audio tracks playing.)

EDIT: I messed up the audio part of the question. In the real code there are audio tracks that should keep playing, I just forgot to add them to the example.


Solution

  • Your code is removing all the tracks from the MediaStream, not only the video ones. To remove only the video ones, you'd do

    video.srcObject.getVideoTracks().forEach(track => {
      track.stop()
      video.srcObject.removeTrack(track);
    });
    

    Note how getTracks is replaced with getVideoTracks.

    But even then, the last frame from the video track may very well still stay in the <video> element. To get rid of it, but keep the audio tracks, you'd be better creating a new MediaStream from the audio tracks and set this as the new srcObject of your <video>:

    Code of this fiddle since StackSnippet's iframe aren't allowed to call getUserMedia.

    (async ()=>{
    
      const video = document.querySelector('video');
      const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true } );
      video.srcObject = stream;
      video.play();
    
      btn.onclick = e => {
        stream.getVideoTracks().forEach( vidTrack => vidTrack.stop() );
        const new_stream = new MediaStream(stream.getAudioTracks());
        video.srcObject = new_stream;
        btn.remove();
      }
    
    })();
    
    <button id="btn">keep only audio</button>
    <video controls autoplay></video>
    

    And if you wanted to replace it with a solid color (e.g a black frame), then you could pass a CanvasCaptureStreamTrack along with the audio tracks, and draw that solid color on the source <canvas>: fiddle