Javascript – How to save a jpg image/video captured with webcam in the local hard drive with HTML5

getusermediahtmljavascriptvideowebcam-capture

The problem appears simple, although I cannot find a suitable solution
because of my lack of knowledge of HTML and Javascript.

The task is simply to design a webpage where a button will activate the webcam and store either a still image or a video (preferable) in the local hard drive. No upload/download required for the time being.

After some attempts, I am able to use the getusermedia() api to activate the webcam and render the video in the browser window, but unable to save it. This is what my code looks like.

if (navigator.getUserMedia) {       
    navigator.getUserMedia({video: true}, handleVideo, videoError);
}

function handleVideo(stream) {
    video.src = window.URL.createObjectURL(stream);
}

So any idea on how to save either a still image or a video in the hard drive captured the same way?

Best Answer

First, the navigator.getUserMedia API is being deprecated, you should now use the navigator.mediaDevices.getUserMedia method.

Then to take a still image, you can indeed use a canvas which can draw a video element.

const vid = document.querySelector('video');
navigator.mediaDevices.getUserMedia({video: true}) // request cam
.then(stream => {
  vid.srcObject = stream; // don't use createObjectURL(MediaStream)
  return vid.play(); // returns a Promise
})
.then(()=>{ // enable the button
  const btn = document.querySelector('button');
  btn.disabled = false;
  btn.onclick = e => {
    takeASnap()
    .then(download);
  };
})
.catch(e=>console.log('please use the fiddle instead'));

function takeASnap(){
  const canvas = document.createElement('canvas'); // create a canvas
  const ctx = canvas.getContext('2d'); // get its context
  canvas.width = vid.videoWidth; // set its size to the one of the video
  canvas.height = vid.videoHeight;
  ctx.drawImage(vid, 0,0); // the video
  return new Promise((res, rej)=>{
    canvas.toBlob(res, 'image/jpeg'); // request a Blob from the canvas
  });
}
function download(blob){
  // uses the <a download> to download a Blob
  let a = document.createElement('a'); 
  a.href = URL.createObjectURL(blob);
  a.download = 'screenshot.jpg';
  document.body.appendChild(a);
  a.click();
}
<button>take a snapshot</button>
<video id="vid"></video>

As a fiddle since Stacksnippets may block gUM requests...

And to save as a video, you can use the [MediaRecorder API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorderà, which will allow you to save a MediaStream as webm:

const vid = document.querySelector('video');
navigator.mediaDevices.getUserMedia({video: true}) // request cam
.then(stream => {
  vid.srcObject = stream; // don't use createObjectURL(MediaStream)
  return vid.play(); // returns a Promise
})
.then(()=>{ // enable the button
  const btn = document.querySelector('button');
  btn.disabled = false;
  btn.onclick = startRecording;
})
.catch(e=>console.log('please use the fiddle instead'));

function startRecording(){
  // switch button's behavior
  const btn = this;
  btn.textContent = 'stop recording';
  btn.onclick = stopRecording;
  
  const chunks = []; // here we will save all video data
  const rec = new MediaRecorder(vid.srcObject);
  // this event contains our data
  rec.ondataavailable = e => chunks.push(e.data);
  // when done, concatenate our chunks in a single Blob
  rec.onstop = e => download(new Blob(chunks));
  rec.start();
  function stopRecording(){
    rec.stop();
    // switch button's behavior
    btn.textContent = 'start recording';
    btn.onclick = startRecording;
  }
}
function download(blob){
  // uses the <a download> to download a Blob
  let a = document.createElement('a'); 
  a.href = URL.createObjectURL(blob);
  a.download = 'recorded.webm';
  document.body.appendChild(a);
  a.click();
}
<button disabled>start recording</button>
<video></video>

And as a fiddle


Notes:

The MediaRecorder API is still a quite new API and there are still some bugs in the [little set of browser implementations.