javascript canvas capture screenshot of camera issue in Samsung Browser - javascript

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();
};
}

Related

jsQR Javascript mediadevices undefined

I am trying to implement the jsQR javascript library from git: jsQR
I am testing on Safari 13.0.4 and when i run their demo found on their webpage it runs with no problems.
Now when i implement the same exact code (copy&paste) from the demo's inspector to my page i get the message TypeError: undefined is not an object (evaluating 'navigator.mediaDevices.getUserMedia') which after some console printing the actual undefined object is navigator.mediaDevices this is strange since their demo is working i do not think it is a browser issue.
Additionally i run my page on firefox and it works perfectly fine.
What might be the problem?
Here is the code i have (it is the exact as demo anyway):
Javascript:
var video = document.createElement("video");
var canvasElement = document.getElementById("canvas");
var canvas = canvasElement.getContext("2d");
var loadingMessage = document.getElementById("loadingMessage");
var outputContainer = document.getElementById("output");
var outputMessage = document.getElementById("outputMessage");
var outputData = document.getElementById("outputData");
function drawLine(begin, end, color) {
canvas.beginPath();
canvas.moveTo(begin.x, begin.y);
canvas.lineTo(end.x, end.y);
canvas.lineWidth = 4;
canvas.strokeStyle = color;
canvas.stroke();
}
$(document).ready(function () {
console.log(navigator.mediaDevices);
})
// Use facingMode: environment to attemt to get the front camera on phones
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
video.srcObject = stream;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.play();
requestAnimationFrame(tick);
});
function tick() {
loadingMessage.innerText = "⌛ Loading video..."
if (video.readyState === video.HAVE_ENOUGH_DATA) {
loadingMessage.hidden = true;
canvasElement.hidden = false;
outputContainer.hidden = false;
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert",
});
if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
outputMessage.hidden = true;
outputData.parentElement.hidden = false;
outputData.innerText = code.data;
} else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
}
}
requestAnimationFrame(tick);
}
Html:
<div id="loadingMessage">🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>
<canvas id="canvas" hidden></canvas>
<div id="output" hidden>
<div id="outputMessage">No QR code detected.</div>
<div hidden><b>Data:</b> <span id="outputData"></span></div>
</div>
Solved it. The development server is not secure and thus safari does not allow the use of WebRTC on insecure connections.
To allow the use (on safari at least) you have to go to Develop>WebRTC>Allow media capture on insecure sites.

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 :)

Converting image taken from camera to canvas has gray on the side

Im using vanilla javascript to capture images from the camera and into the canvas. Its working fine on Chrome Android but not in iOS Safari. As seen in the screenshot, there is a gray area on the image. Is there something I need to do to not get that rendered?
JavaScript for the camera
navigator.mediaDevices.getUserMedia({
'audio': false,
'video': {
facingMode: 'environment'
}
}).then(stream => {
this.VIDEO.srcObject = stream;
this.VIDEO.addEventListener('loadeddata', async () => {
this.VIDEO.width = this.VIDEO.videoWidth;
this.VIDEO.height = this.VIDEO.videoHeight;
this.CANVAS.width = this.VIDEO.videoWidth;
this.CANVAS.height = this.VIDEO.videoHeight;
resolve();
}, false);
}).catch(error => {
console.error(error);
self.MESSAGE.textContent = error;
});
JavaScript for drawing on the canvas and the preview window
self.CONTEXT.drawImage(self.VIDEO, 0, 0, self.VIDEO.width, self.VIDEO.height);
let imgDataURL = self.CANVAS.toDataURL('image/png');
self.PLANT.src = imgDataURL;
self.POLAROID.style.backgroundImage = `url(${imgDataURL})`;

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;
}

Webcam Capture to <img>

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;
}

Categories