Webcam Capture to <img> - javascript

Using javascript or Jquery I want to get the current image from the webcam feed on my page and put it in an tag.
I have a bit of script which generates tags dynamically with unique Id's so after generating one all I want to do is capture an image from the webcam at that exact moment and save the image in the generated tag. After taking the image I just want the webcam to carry until the next time it takes a picture.
I already have a webcam feed running using a library which does face tracking, however I want to extend this with this feature to create a gallery of captured images on the page.
The library I am using is ClmTracker
Creator of the library suggested calling getImageData(x,y,w,h) on the video element and I have tried this. also tried to implement tutorials I have seen on other websites but to no avail. It would seem the answer would need to be specific to my code. I have tried to use canvas instead of tags to put the image in to, but I kept getting errors due to them being created dynamically in the code.
var vid = document.getElementById('videoel');
var overlay = document.getElementById('overlay');
var overlayCC = overlay.getContext('2d');
/********** check and set up video/webcam **********/
function enablestart() {
var startbutton = document.getElementById('startbutton');
startbutton.value = "start";
startbutton.disabled = null;
}
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.msURL || window.mozURL;
// check for camerasupport
if (navigator.getUserMedia) {
// set up stream
var videoSelector = {
video: true
};
if (window.navigator.appVersion.match(/Chrome\/(.*?) /)) {
var chromeVersion = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
if (chromeVersion < 20) {
videoSelector = "video";
}
};
navigator.getUserMedia(videoSelector, function (stream) {
if (vid.mozCaptureStream) {
vid.mozSrcObject = stream;
} else {
vid.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
}
vid.play();
}, function () {
//insertAltVideo(vid);
alert("There was some problem trying to fetch video from your webcam. If you have a webcam, please make sure to accept when the browser asks for access to your webcam.");
});
} else {
//insertAltVideo(vid);
alert("This demo depends on getUserMedia, which your browser does not seem to support. :(");
}
vid.addEventListener('canplay', enablestart, false);
How can I capture an image from the webcam and put it in a div using the code above as a basis?
I'm not sure I can give any more details as I have not got the knowledge on how to do this.

First, draw it to a canvas:
var canvas = document.createElement('canvas');
canvas.height = video.height;
canvas.width = video.width;
canvas.getContext('2d').drawImage(video, 0, 0);
And now you can create the image:
var img = document.createElement('img');
img.src = canvas.toDataURL();

I cannot get to seem to get the screenshot porting working, but the webcam part is working fine, I'll update when complete.
JSFiddle
HTML
<div id="container">
<video autoplay="true" id="videoElement">dsasd</video>
<br>
<button onclick="snap()">Screenshot</button>
<canvas></canvas>
</div>
JS
var video = document.querySelector("#videoElement");
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({
video: true
}, handleVideo, videoError);
}
function handleVideo(stream) {
video.src = window.URL.createObjectURL(stream);
}
function videoError(e) {
// do something
}
// Get handles on the video and canvas elements
var video = document.querySelector('video');
var canvas = document.querySelector('canvas');
// Get a handle on the 2d context of the canvas element
var context = canvas.getContext('2d');
// Define some vars required later
var w, h, ratio;
// Add a listener to wait for the 'loadedmetadata' state so the video's dimensions can be read
video.addEventListener('loadedmetadata', function() {
// Calculate the ratio of the video's width to height
ratio = video.videoWidth / video.videoHeight;
// Define the required width as 100 pixels smaller than the actual video's width
w = video.videoWidth - 100;
// Calculate the height based on the video's width and the ratio
h = parseInt(w / ratio, 10);
// Set the canvas width and height to the values just calculated
canvas.width = w;
canvas.height = h;
}, false);
// Takes a snapshot of the video
function snap() {
// Define the size of the rectangle that will be filled (basically the entire element)
context.fillRect(0, 0, w, h);
// Grab the image from the video
context.drawImage(video, 0, 0, w, h);
}
CSS
container {
margin: 0px auto;
width: 500px;
height: 375px;
border: 10px #333 solid;
}
videoElement, canvas {
width: 500px;
height: 375px;
background-color: #666;
}

Related

javascript canvas capture screenshot of camera issue in Samsung Browser

Well, I'm having a very weird issue that's only happening on Samsung Browser. On Chrome and other browsers, this works well.
When I take a snapshot of a current frame of a video (Currently the mobile camera) on javascript I get the image with distortion and generally bad.
The code that takes the snapshot is:
function takeSnapshot() {
// Here we're using a trick that involves a hidden canvas element.
video.pause();
var hidden_canvas = document.createElement('canvas'),
context = hidden_canvas.getContext('2d');
var width = video.videoWidth,
height = video.videoHeight;
if (width && height) {
// Setup a canvas with the same dimensions as the video.
hidden_canvas.width = width;
hidden_canvas.height = height;
// Make a copy of the current frame in the video on the canvas.
context.drawImage(video, 0, 0, width, height);
// Turn the canvas image into a dataURL that can be used as a src for our photo.
return hidden_canvas.toDataURL('image/jpeg');
}
}
Do I'm missing something else to make it work in Samsung Browser? Or I just put a message that this is not compatible with this browser?
Currently tested on Samsung Galaxy S9, Android 10.
------------- Update
I found what is causing the image to be captured badly.
I'm using custom size for the image, in this case, is a horizontal rectangle.
I do this when init the video:
var w = 2000; // This renders the video as a horizontal rectangle, this does the issue.
var h = 1200;
var userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
var isSamsungBrowser = userAgent.indexOf('SamsungBrowser') >= 0;
// Quick fix:
if(SamsungBrowser){ // If I render as vertical renctangle, the issue is gone.
w = 1200;
h = 2000;
}
navigator.getMedia(
{
video:
{
deviceId: videoSource ? { exact: videoSource } : undefined,
width: { ideal: h },
height: { ideal: w }
}
},
// Success Callback
function (stream) {
// Create an object URL for the video stream and
// set it as src of our HTLM video element.
try {
currentStream = stream;
video.srcObject = stream;
} catch (error) {
video.src = window.URL.createObjectURL(stream);
}
window.stream = stream;
// Play the video element to start the stream.
video.play();
video.onplay = function () {
showVideo();
};
}

How to fix element.videoHeight in js with WebRTC?

I need some serious help about my code which is driving me crazy !!
I'm trying to set the format of my video to match a square rendering but my js won't set the element.videoHeight to the value I've defined !
I feel like I tried everything : changing the video.height AND video.videoHeight in multiple places, trying the 'loadedmetadata' method, clearing all cookies and caches etc...
Here's my current code:
(function ()
{
let width = 0;
let height = 0;
let streaming = false;
let video = null;
let canvas = null;
function startup()
{
video = document.getElementById('webcam');
canvas = document.getElementById('transit');
width = video.offsetWidth;
video.width = width;
video.height = width;
document.getElementById('transit').width = width;
document.getElementById('result').width = width;
navigator.mediaDevices.getUserMedia({ video: { width: width, height: width }, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occured: " + err);
});
video.addEventListener('canplay', function(ev){
if (!streaming) {
height = width;
video.setAttribute('width', width);
video.setAttribute('height', width);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
}
}, false);
window.addEventListener('load', startup, false);
})();
I want the video.videoWidth to be equal to video.videoHeight.
But event if I manage to change the video.videoWidth in my code (according to some console.log), the video.videoHeight stays the same !
The result is like if I put a div around my tag and the div is a square, but the video itself is still in some kind of 4/3 format !
And oddly enough, it works just fine on my personal computer, but it doesn't on some other one, both of them being Mac OSX... And with the same js version obviously.
Help ! :'(
videoHeight is a read-only property, see mdn.
To get a square rendering (even if the video is 4:3) use the CSS 'cover' object-fit combined with width and height.
Actually, thank you for your answer but I found the error : it was because the definition of the video can't exceed 720px in height !!
So I just added a condition stating that :
if (video.offsetHeight > 720)
width = 720;
else
width = video.offsetWidth;
So now I can have a square video rendering :)

How to specify image as input for HTML form by using its id?

I'm capturing photo via webcam on a web application, which would display the captured photo on screen, then send it to servlet so that I can save it directly to a path in my computer.
My problem now is I don't know how to post the photo to servlet. I googled and found some code that can upload a user-chosen file to servlet, but I need my webcam photo to be the form input without letting user choose a file to upload. I have the id of the photo.
Here's what I found:
<form method="post" action="UploadServlet" enctype="multipart/form-data">
Select file to upload: <input type="file" name="file" size="60" /><br /><br />
<input type="submit" value="Upload" />
</form>
This code integrates well with my code and everything can run, but I don't want to upload a file, I want to set the captured image as my form input so the user just have to click "submit" after seeing the image.
Code that displays the captured photo:
<div class="output"><img id="photo" alt="The image preview will appear in this box."></div>
The captured photo is rendered and displayed here as a PNG image. How do I make it the form input?
JavaScript:
(function() {
var width = 320;
var height = 0;
var streaming = false;
var video = null;
var canvas = null;
var photo = null;
var startbutton = null;
function startup() {
video = document.getElementById('video');
canvas = document.getElementById('canvas');
photo = document.getElementById('photo');
var confirmPhoto = document.getElementById('photo');
startbutton = document.getElementById('startbutton');
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
navigator.getMedia(
{
video: true,
audio: false
},
function(stream) {
if (navigator.mozGetUserMedia) {
video.mozSrcObject = stream;
} else {
var vendorURL = window.URL || window.webkitURL;
video.src = vendorURL.createObjectURL(stream);
}
video.play();
},
function(err) {
console.log("An error occured! " + err);
}
);
video.addEventListener('canplay', function(ev){
if (!streaming) {
height = video.videoHeight / (video.videoWidth/width);
if (isNaN(height)) {
height = width / (4/3);
}
video.setAttribute('width', width);
video.setAttribute('height', height);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
}
}, false);
startbutton.addEventListener('click', function(ev){
takepicture();
ev.preventDefault();
}, false);
clearphoto();
}
function clearphoto() {
var context = canvas.getContext('2d');
context.fillStyle = "#AAA";
context.fillRect(0, 0, canvas.width, canvas.height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
}
function takepicture() {
var context = canvas.getContext('2d');
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
} else {
clearphoto();
}
}
window.addEventListener('load', startup, false);})();
Please help! Thank you!!

How to make AudioContext() and RequestAnimationFrame() running in current browsers

I am trying to create a visualiser using HTML5 Audio and frequencies based on a canvas. It is working fine on chrome and safari. But I want to make it working on IE and Firefox as well.
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.crossOrigin = "anonymous";
audio.src = 'http://www.streaming507.com:8072/stream';
audio.controls = true;
audio.loop = true;
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player() {
document.getElementById('audio_box').appendChild(audio);
context = new webkitAudioContext(); // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
//frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper() {
window.webkitRequestAnimationFrame(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
var my_gradient = ctx.createLinearGradient(0, 0, 170, 0);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(0.5, "red");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
div#mp3_player {
width: 500px;
height: 60px;
background: #000;
padding: 5px;
margin: 50px auto;
}
div#mp3_player > div > audio {
width: 500px;
background: #000;
float: left;
}
div#mp3_player > canvas {
width: 500px;
height: 30px;
background: #002D3C;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_render"></canvas>
</div>
Please note, it does not show the visualisation on stack overflow, but it work on local host.
Basically the issue that it does not show on IE and Moz is webkitAudioContext(), and webkitRequestAnimationFrame(). removing webkit from the first one makes it working on chrome and IE but stops working on Safari.
Is there any idea how to make it working in current browsers like Chrome, Safari, Firefox and IE.
Try to allocate the AudioContext this way:
var Actx;
try {
Actx = (AudioContext || webkitAudioContext);
}
catch(err) {
// sorry, no visualizer for you...
}
if (Actx) {
context = new Actx();
// ... continue here
}
else {
// optionally rub it in here instead of in catch...
}
For requestAnimationFrame you can use this polyfill, then use it without prefix:
function frameLooper() {
window.requestAnimationFrame(frameLooper);
...
You probably also want to wait for the audio element's "canplay" event.

HTML5 camera stretches the picture

I'm using the HTML5 camera for taking picture. I have noticed that after the picture is captured, the final image width and height are stretched though I'm applying the same width and height for both video as well as canvas.
Please refer this JSFIDDLE
var video = document.querySelector("#videoElement");
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, handleVideo, videoError);
}
function handleVideo(stream) {
video.src = window.URL.createObjectURL(stream);
}
function videoError(e) {
// do something
}
var v,canvas,context,w,h;
var imgtag = document.getElementById('imgtag');
var sel = document.getElementById('fileselect');
//document.addEventListener('DOMContentLoaded', function(){
v = document.getElementById('videoElement');
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
w = canvas.width;
h = canvas.height;
//},false);
function draw(v,c,w,h) {
if(v.paused || v.ended) return false;
context.drawImage(v,0,0,w,h);
var uri = canvas.toDataURL("image/png");
imgtag.src = uri;
}
document.getElementById('save').addEventListener('click',function(e){
draw(v,context,w,h);
});
var fr;
sel.addEventListener('change',function(e){
var f = sel.files[0];
fr = new FileReader();
fr.onload = receivedText;
fr.readAsDataURL(f);
})
function receivedText() {
imgtag.src = fr.result;
}
Your code has hard-coded values for width and height, both for video and canvas elements.
Avoid setting absolute sizes in both directions as the dimension of the video element can vary. You can set the size by using just the width or just the height, or by using CSS style/rule.
For canvas you need to set the size based on the actual size of the video element. For this use the video element properties:
video.videoWidth;
video.videoHeight;
You can apply a scale to those as you want, for example:
scale = 300 / v.videoWidth;
w = v.videoWidth * scale;
h = v.videoHeight * scale;
canvas.width = w;
canvas.height = h;
context.drawImage(v, 0, 0, w, h);
Updated fiddle
Should someone still be facing this problem in 2021...
// init media navigator
navigator.mediaDevices.getUserMedia(constraints).then(stream => {
// get the actual settings of video element,
// which includes real video width and height
const streamSettings = stream.getVideoTracks()[0].getSettings();
// set the constrains to canvas element
canvasElement.width = streamSettings.width;
canvasElement.height = streamSettings.height;
}

Categories