Extract snapshot from video element with camera source - javascript

Context
I'm trying to capture a snapshot through the client's video camera and save it as an image. I have a JSFiddle demo.
Problem
I'm not succeeding in getting the whole video area from the Video element to the Canvas with Context2D's drawImage. Althought the Video is 400 by 300
<video width="400" height="300" ...
and the canvas is the same 400 by 300
<canvas width="400" height="300" ...
and when I draw I specify for both source and target 400 by 300...
ctx.drawImage(video, 0, 0, 400, 300, 0, 0, 400, 300);
This is a sample of what is happening on my client.
The red framed element is the video and the green one is the canvas.
I suspect this is because the video stream is not 400 by 300 (not even 4:3 aspect ratio), but I couldn't find yet a solution for this or at least some indication in the HTML specs that this behavior is because of the video stream.
Does anyone have some more experience in this area and can point me in the right direction?
Sorry for the bad quality of the code in the fiddle.

When you set the HTMLElement's widthand height attributes, you're only setting its display size, not the size of the frame inside it.
Canvas2dContext.drawImage(video, x, y) will take the frame size, (which can be set by the mandatory of getUserMedia btw).
So you need either to set your canvas' width and height to the video.videoWidthand video.videoHeight properties to get the full quality image grabbed by the webcam, ot to call ctx.drawImage(video, 0,0, 400, 300) to rescale it on the canvas as it is scaled by CSS, or setting directly the mandatory during the getUserMedia call (latests specs are navigator.mediaDevices.getUserMedia(video: { width: 300, height:300 });) but unfortunately, for this latest, you can't be sure the browser will honor it, except by checking the video.videoHeight/Width ;-)
var ctx = rendering.getContext('2d');
btn.onclick = function() {
switch (select.value) {
case 'css rescale':
rendering.width = 400;
rendering.height = 300;
ctx.drawImage(video, 0, 0, 400, 300);
break;
case 'full quality':
rendering.width = video.videoWidth;
rendering.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
break;
}
}
var initVideo = function(stream) {
video.src = URL.createObjectURL(stream);
video.play();
};
navigator.mediaDevices.getUserMedia({
video: true
})
.then(function(s) {
initVideo(s)
})
.catch(function(err) {
if (err.message.indexOf('secure origins') > -1) {
document.body.innerHTML = '<h3><a target="_blank" href="https://jsfiddle.net/x64ta5r3/">jsfiddle link for chrome and its https policy...</a></h3>(right click -> open Link...';
}
});
<button id="btn">take a snapshot</button>
<select id="select">
<option value="css rescale">css rescale</option>
<option value="full quality">full quality</option>
</select>
<video width="400" height="300" id="video"></video>
<canvas id="rendering"></canvas>
jsfiddle link for chrome and its https policy...

Related

How to fix the black screen with canvas.toDataURL() on Safari browser?

I have a video player (video.js) in my application, and a function which takes a snapshot of the video.
The function create a canvas which draws the snapshot and convert the canvas into a DataURL.
But this doesn't work in Safari.
I'm getting a black screen on Safari.
the video tag looks like:
<video crossorigin="anonymous" playsinline="playsinline" id="player_2140_html5_api" class="vjs-tech" data-setup="{ "inactivityTimeout": 0 }" tabindex="-1" autoplay="autoplay" loop="loop"></video>
This is the function which creates the snapshot:
var canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 480;
var ctx = canvas.getContext('2d');
var video = document.querySelector('video');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
this.snapshotVideo = canvas.toDataURL('image/jpeg');
The generated dataUrl in Chrome is an image.
The generated dataUrl in Safari browser is a black rectangle.
In Safari the output is black instead of an image of the taken snapshot via the video, how can I solve this?
I resolved this problem on iOS...
I used setTimeout function:
setTimeout(() => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
canvas.toBlob(() => {
this.snapshotVideo = canvas.toDataURL('image/jpeg');
});
},100);
Same here.
The problem seen first time at Safari 15. Before it works well with your code snipped.
See last comments here:
https://bugs.webkit.org/show_bug.cgi?id=181663
or here:
https://bugs.webkit.org/show_bug.cgi?id=229611
I have the same issue. Calling toDataURL on safari 15 up (mac, iPad or iPhone) seems to occasionally return "" blank string. Previous versions of safari work fine. I've checked that it is not exceeding the browser canvas size limits which would return "data:," but that is not causing the issue and is returning a blank string.
I got the same problem on Safari iOS 15.5 & 15.6 for handling image drawing.
After calling drawImage, the toDataURL() always return blank image.
I changed some code to avoid using canvas in the drawImage and use image object as the first parameter.
var scrollTop = 100;
var oc2 = document.createElement('canvas');
if (oc2.getContext) {
var octx2 = oc2.getContext('2d');
oc2.width = 200;
oc2.height = 300;
var img1 = new Image();
img1.onload = function() {
octx2.drawImage(img1, 0, scrollTop, 200, 300, 0, 0, 200, 300);
var bg_data = oc2.toDataURL('image/png');
if (bg_data) {
drawing_board.setImg(bg_data);
drawing_board.initHistory();
}
};
img1.src = oc.toDataURL('image/png');
}

Javascript Electron how to take screenshots from a video source

I'm using Electron Desktop Capturer https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md to capture a screenshot of this stream at regular intervals. I'm using this code but for some reason I get an error:
function takeScr(stream) {
const video = localStream.getVideoTracks()[0];
console.log(video)
const canvas = document.createElement('canvas');
canvas.getContext("2d").drawImage(video, 0, 0, 300, 300, 0, 0, 300, 300);
}
At the moment I'm simply pressing a button to activate this take screenshot function after the stream has started playing. The console log shows the video track no problem with output:
MediaStreamTrack {kind: "video", id: "7a19a94f-6077-4e3d-b534-03d138b3f300", label: "Screen", enabled: true, muted: false, …}
but the canvas.getContext function throws the error:
Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
There are many questions on here about this error but none seem to solve my problem and none are regarding video streams. Some solutions were that the image was not loaded when trying to draw to canvas but since I'm pressing a button several seconds after the stream has started I'm sure it must be loaded?
Maybe I'm doing this the wrong way and there is a better way to take screenshots of the video source from Desktop Capturer?
I found an example in the following question regarding taking snapshots from a video.
You could do something like this:
document.getElementById("snap").addEventListener("click", function() {
snap();
});
// 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);
}
<video width="400" controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
<source src="https://www.w3schools.com/html/mov_bbb.ogg" type="video/ogg">
Your browser does not support HTML5 video.
</video>
<canvas width="364" height="204"></canvas>
<button id="snap">Take screenshot</button>
JSFiddle

How to use canvas for parsing imageData from video without appending canvas to the page?

I have Webcam class, that uses for capturing data from webcam. This class streames data to video tag and also draw it on canvas., Also I have QRScanner class, that uses for parsing QR code from imageData of canvas.
class Webcam {
constructor(videoTag, canvasTag) {
// using for real-time video capture
this.videoTag = videoTag;
// qr scanner parses imageData from this element
this.canvasTag = canvasTag;
// waiting for qr code here
this.captureArea = {
x: 100,
y: 60,
width: 120,
height: 120
}
// getting access to webcam
navigator.mediaDevices
.getUserMedia({
video: true
})
.then(stream => this.videoTag.srcObject = stream)
.catch(console.log);
}
capture() {
setInterval(() => {
let ctx = this.canvasTag.getContext('2d');
ctx.drawImage(this.videoTag, 0, 0, 320, 240);
// drawing capture area
ctx.strokeStyle = 'red';
// this arguments also should be passed into qr scanner
ctx.strokeRect(
this.captureArea.x,
this.captureArea.y,
this.captureArea.width,
this.captureArea.height
);
}, 100);
}
}
class QRScanner {
constructor(canvas, router, captureArea) {
this.canvas = canvas;
this.router = router;
this.captureArea = captureArea;
this.qrCode = null;
}
scan() {
setInterval(() => {
let imageData = this.canvas
.getContext('2d')
.getImageData(
this.captureArea.x,
this.captureArea.y,
this.captureArea.width,
this.captureArea.height
).data;
// parsing qr code from canvas
this.qrCode = jsQR(imageData, this.captureArea.width, this.captureArea.height);
if (this.qrCode) {
router.redirect(Router.pages.result, this.qrCode);
let resultPage = document.querySelector('#result .qr-code-data');
resultPage.innerHTML = this.qrCode.data;
}
}, 100);
}
}
<div id="scan">
<video id="video" width="320" height="240" autoplay title="Real-time video stream from webcam"></video>
<canvas id="preview" width="320" height="240" title="Capture area for QR code"></canvas>
</div>
This works as expected, but when I removes canvas element from page and trying to use temporary canvas (using document.createElement('canvas') without appending to the page) - this solution do not work. Does it possible to using temporary canvas for getting video imageData without appending this canvas to the page?
P.S. I'm using https://github.com/cozmo/jsQR
Canvas elements have a default width height when they aren't explicitly set. Since you never set those for you created canvas it is going to default to 300 x 150, at least for Chrome might be different for other browsers implementations.
var canvas = document.createElement("canvas");
console.log(canvas.width,canvas.height)
And since this default size is different than the size that you are trying to draw the image/video to there is going to be some cropping going on and therefore effecting your QR library from properly reading the image.
Just set the width and height to what you need
var canvas = document.createElement('canvas')
canvas.width = 320;
canvas.height = 240;

scriptcam - scriptcam screenshot size not work

I'm using scriptcam and I have a problem with screenshot size only in IE10.
I init my webcam div with:
$("#webcam").scriptcam({
path: 'js/',
width: 640,
height: 480,
cornerRadius: 0
});
When I take a screenshot with
var image = new Image();
image.src = "data:image/png;base64," + $.scriptcam.getFrameAsBase64();
image.onload = function () {
ctx.drawImage(image, 0, 0);
};
my canvas has the photo resized at original value "320 pixels wide and 240 pixels high" and the other part is black.
How can I fix this problem??
Thanks for your help!
[UPDATE]
The problem is in the type of webcam: changing the webcam whit a HD cam, the size of the screenshot is ok. Is this a bug?

HTML5/ Javascript - Stretch image source

Hi I'm getting image from webcam and I save it on canvas, what I want to do is to stretch it by x and y cooridnates keeping same img dimensions, what I mean is, this is the original webcam picture:
and this is how I wanna it to be when stretched:
<canvas id="canvas" width="640" height="480" style="border:1px solid #d3d3d3;"></canvas>
this is the piece of code that shows source image to <img> element in html , so I have to stretch source image before to show it in html
function snap() {
if (localMediaStream) {
ctx.drawImage(video, 0, 0);
var oImg=document.createElement("img");
oImg.setAttribute('src', canvas.toDataURL());
oImg.setAttribute('alt', 'na');
oImg.setAttribute('width', '1300');
oImg.setAttribute('height', '1300');
var img = document.body.appendChild(oImg);
return img;
}
}
any idea on how to stretch the canvas.toDataUrL() source by x and y coordinates and return it stretched to the the src <img> element?
The real problem imo is how to stretch image without altering width
and height (as shown via example photos above), is this possible?
You can use the extended properties on context.drawImage that allow scaling/positioning.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=function(){
var w=img.width;
var h=img.height;
canvas.width=w; // set the canvas size to the original image size
canvas.height=h;
ctx.drawImage(img,
0,0,w,h, // start with the image at original size
-w/4,0,w*1.25,h // widen the original image by 1.25X
// and start drawing 1/4th off the left edge of the canvas
);
}
img.src="temp18.png";

Categories