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?
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.