6

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?

Della
  • 1,264
  • 2
  • 15
  • 32
  • 1
    by save you mean download? just trying to be consistent with web terminology – Sergio Alen Oct 23 '17 at 05:38
  • See [Send imageObject from webcam by ajax to Flask Server](https://stackoverflow.com/questions/46879681/send-imageobject-from-webcam-by-ajax-to-flask-server/) – guest271314 Oct 23 '17 at 05:40
  • @SergioAlen to download means save in the hard drive, just like I do with an image after running some processing with python etc.? Then yes. (My primary area is machine learning, and really not familiar with web. Just trying to stitch this thing together as part of a project. So pardon the misuse of terminologies.) – Della Oct 23 '17 at 05:43
  • 1
    You can draw the video to a `` element and use `.toDataURL()` then set the resulting `data URL` as value of `.href`, set `download` attribute at `` element, append `` element to `document.body`, click the element. – guest271314 Oct 23 '17 at 05:51
  • yes you could add the image to a canvas then do the above suggestion, see what this person has done here: http://jsfiddle.net/wboykinm/fL0q2uce/ – Sergio Alen Oct 23 '17 at 05:53

2 Answers2

17

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.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • 1
    I've seen loads of implementations of recording Video/Photos from webcam, but this is by far the simplest and easiest to understand. Nicely done! – David Ferris Mar 28 '19 at 18:40
1

What you did is a good start. You can now 'paint' the webcam picture into a canvas and then create an image from the canvas information. You can then use some tricks to prevent the browser from displaying the image and get it to download it instead.

Marc Dix
  • 1,864
  • 15
  • 29