HTML5 Video with Dynamic Thumbnails - javascript

I have a problem, when creating thumbnails. The cross-domain problem I solved with the help of html2canvas PHP proxy.
No error message in the Console. But that Thumnbnails unfortunately are not visible, transparent or white.
Output cut in the source code:
<img src="data:image/png;base64,iVBORw0KGgoAAAA.......Output cut in the source code:NSUhEUgAABN8AAAS4=" width="120">
The script:
<script>
var video = document.getElementById("thumb");
video.addEventListener("loadedmetadata", initScreenshot);
video.addEventListener("playing", startScreenshot);
video.addEventListener("pause", stopScreenshot);
video.addEventListener("ended", stopScreenshot);
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var ssContainer = document.getElementById("screenShots");
var videoHeight, videoWidth;
var drawTimer = null;
function initScreenshot() {
videoHeight = video.videoHeight;
videoWidth = video.videoWidth;
}
function startScreenshot() {
if (drawTimer == null) {
drawTimer = setInterval(grabScreenshot, 1000);
}
}
function stopScreenshot() {
if (drawTimer) {
clearInterval(drawTimer);
drawTimer = null;
}
}
function grabScreenshot() {
ctx.drawImage(video, 0, 0, videoWidth, videoHeight);
convert(document.getElementById("thumb-parent"));
}
function convert(target) {
html2canvas(target, {
"proxy": "../html2canvasproxy.php",
"logging": true, //Enable log (use Web Console for get Errors and Warnings)
"onrendered": function(canvas) {
var img = new Image();
img.onload = function () {
img.onload = null;
img.width = 120;
document.getElementById("screenShots").appendChild(img);
};
img.src = canvas.toDataURL("image/png");
}
});
}

From the looks of things, it's because the browser considers your canvas 'tainted' - using the example you provided above, I've let the video run a little, then tried to log the toDataURL output:
console.log(canvas.toDataURL());
VM1344:2 Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
My suspicion is that is becuase the video is being loaded from a third party URL.
Try loading the video from the same domain as the HTML code, and see if that works

Related

Can't figure out how to fix this: "Uncaught TypeError (webcam.snap is not a function)" <-- javascript

I have a JavaScript script that I am getting an error for that I can't figure out. I am trying to take a picture of the webcam feed using JavaScript
The error is:
Uncaught TypeError: webcam.snap is not a function
I am using webcam.js to take the snapshot.
Here is my JavaScript code:
<script>
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");
const jsQR = require("jsqr");
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();
}
// 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: "invertFirst",
});
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;
takeSnapShot();
}
else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
}
}
requestAnimationFrame(tick);
}
// TAKE A SNAPSHOT.
takeSnapShot = function () {
webcam.snap(function (data_uri) {
downloadImage('video', data_uri);
});
}
// DOWNLOAD THE IMAGE.
downloadImage = function (name, datauri) {
var a = document.createElement('a');
a.setAttribute('download', name + '.png');
a.setAttribute('href', datauri);
a.click();
}
</script>
This is the first line that causes a problem:
webcam.snap(function (data_uri) {
downloadImage('video', data_uri);
});
This is the second line that causes a problem:
takeSnapShot();
how do I correct this properly?
****** UPDATE ******
The version of webcam.js I am using is WebcamJS v1.0.26. My application is a Django application that launches the HTML file as defined in main.js.
snap: function(user_callback, user_canvas) {
// use global callback and canvas if not defined as parameter
if (!user_callback) user_callback = this.params.user_callback;
if (!user_canvas) user_canvas = this.params.user_canvas;
// take snapshot and return image data uri
var self = this;
var params = this.params;
if (!this.loaded) return this.dispatch('error', new WebcamError("Webcam is not loaded yet"));
// if (!this.live) return this.dispatch('error', new WebcamError("Webcam is not live yet"));
if (!user_callback) return this.dispatch('error', new WebcamError("Please provide a callback function or canvas to snap()"));
// if we have an active preview freeze, use that
if (this.preview_active) {
this.savePreview( user_callback, user_canvas );
return null;
}
// create offscreen canvas element to hold pixels
var canvas = document.createElement('canvas');
canvas.width = this.params.dest_width;
canvas.height = this.params.dest_height;
var context = canvas.getContext('2d');
// flip canvas horizontally if desired
if (this.params.flip_horiz) {
context.translate( params.dest_width, 0 );
context.scale( -1, 1 );
}
// create inline function, called after image load (flash) or immediately (native)
var func = function() {
// render image if needed (flash)
if (this.src && this.width && this.height) {
context.drawImage(this, 0, 0, params.dest_width, params.dest_height);
}
// crop if desired
if (params.crop_width && params.crop_height) {
var crop_canvas = document.createElement('canvas');
crop_canvas.width = params.crop_width;
crop_canvas.height = params.crop_height;
var crop_context = crop_canvas.getContext('2d');
crop_context.drawImage( canvas,
Math.floor( (params.dest_width / 2) - (params.crop_width / 2) ),
Math.floor( (params.dest_height / 2) - (params.crop_height / 2) ),
params.crop_width,
params.crop_height,
0,
0,
params.crop_width,
params.crop_height
);
// swap canvases
context = crop_context;
canvas = crop_canvas;
}
// render to user canvas if desired
if (user_canvas) {
var user_context = user_canvas.getContext('2d');
user_context.drawImage( canvas, 0, 0 );
}
// fire user callback if desired
user_callback(
user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ),
canvas,
context
);
};
// grab image frame from userMedia or flash movie
if (this.userMedia) {
// native implementation
context.drawImage(this.video, 0, 0, this.params.dest_width, this.params.dest_height);
// fire callback right away
func();
}
else if (this.iOS) {
var div = document.getElementById(this.container.id+'-ios_div');
var img = document.getElementById(this.container.id+'-ios_img');
var input = document.getElementById(this.container.id+'-ios_input');
// function for handle snapshot event (call user_callback and reset the interface)
iFunc = function(event) {
func.call(img);
img.removeEventListener('load', iFunc);
div.style.backgroundImage = 'none';
img.removeAttribute('src');
input.value = null;
};
if (!input.value) {
// No image selected yet, activate input field
img.addEventListener('load', iFunc);
input.style.display = 'block';
input.focus();
input.click();
input.style.display = 'none';
} else {
// Image already selected
iFunc(null);
}
}
else {
// flash fallback
var raw_data = this.getMovie()._snap();
// render to image, fire callback when complete
var img = new Image();
img.onload = func;
img.src = 'data:image/'+this.params.image_format+';base64,' + raw_data;
}
return null;
},
Your implementation doesn't need Webcamjs, because you're using navigator media devices.
You can either use WebcamJS by initializing it at first and attaching it to some canvas, like in the following code
Webcam.set({
width: 320,
height: 240,
image_format: 'jpeg',
jpeg_quality: 90
});
Webcam.attach( '#my_camera' );
Or you can update your takeSnapShot function to the following :
takeSnapShot = function () {
downloadImage('video',canvasElement.toDataURL())
// Webcam.snap(function (data_uri) {
// downloadImage('video', data_uri);
// });
}
Here's a working example based on your code https://codepen.io/majdsalloum/pen/RwVKBbK
It seems like either:
the webcam's code are missing (not imported)
in this case you need to first call the script from the URL and add it with script tag
<script src="WEBCAM_JS_SOURCE">
or
they are imported, but used with typo. From the webcam source code it is defined as:
var Webcam = {
version: '1.0.26',
// globals
...
};
so you should use with a capital one.

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.

Downloading dynamically created canvas is blank

I have trouble downloading a dynamically created canvas. I want the user to be able to create an SVG image and download it as a bitmap. To achieve this, I create a canvas and draw my SVG image onto it. Then I want to download it. However, the canvas is blank. Now I know that this is probably because the canvas has not yet fully loaded, but attaching an onload listener on canvas didn't work as it never fired.
Clicking the link a second time downloads the correct image, another hint to me that it's a timing issue. Can someone tell me why the onload is never fired? Maybe I'm misunderstanding how the event is handled or what causes it.
document.getElementById('downloadDirectly').addEventListener('click', function () {
downloadDirectly();
});
document.getElementById('downloadOnLoad').addEventListener('click', function () {
downloadOnLoad();
});
function createCanvas() {
var img = new Image();
img.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000"><line x1="0" y1="0" x2="500" y2="500" stroke="black" stroke-width="5"/></svg>';
var canvas = document.createElement('canvas');
canvas.width = 1000;
canvas.height = 1000;
canvas.getContext('2d').drawImage(img, 0, 0, 1000, 1000, 0, 0, 1000, 1000);
return canvas;
}
function downloadDirectly() {
let canvas = {};
canvas = createCanvas();
let el = document.getElementById('downloadDirectly');
el.href = canvas.toDataURL();
console.log('This downloads, but canvas is not ready');
el.download = name + ".png";
}
function downloadOnLoad() {
let canvas = {};
canvas = createCanvas();
canvas.onload = function() {
console.log('This is never reached :(');
let el = document.getElementById('downloadOnLoad');
el.href = canvas.toDataURL();
el.download = name + ".png";
};
}
<p><a id="downloadDirectly">Download directly</a></p>
<p><a id="downloadOnLoad">Download using onLoad</a></p>

How do I load a local image using JS?

I want to load two separate images in my script. I've accomplished it using:
<img src="iphone4.png" id="img1">
<img src="screenshot.png" id="img2">
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
</script>
Problem here though is that the images should not be visible on the page but are when loaded using markup. I simply want to load them through the script without first having to add them in the markup. I realize this is an extremely trivial problem, but searching for a solution has given me nothing.
I tried this approach:
window.onload = function () {
var img1 = "iphone4.png";
var img2 = "screenshot.png";
But this did not work.
Can someone with some common JS sense please give me some input on this issue.
EDIT :
So this is how the markup/JS looks now, the images are still displayed and the final merge of the images won't show. The error I get is:
IndexSizeError: Index or size is negative or greater than the allowed amount
[Stanna vid fel]
var image1 = context.getImageData(0, 0, width, height);
And this is the syntax:
<body>
<img src="" id="img1">
<img src="" id="img2">
<p>Blended image<br><canvas id="canvas"></canvas></p>
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
img1.src = "iphone4.png";
img2.src = "screenshot.png";
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 73, 265);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0 + imageData2[pixels] * 1;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0);
};
</script>
You can create an Image without having the actual tag in the markup:
var img = new Image();
img.src = 'iphone4.png';
//use img however you want
Hope this helps.
window.onload = function () {
var img1 = new Image();
var img2 = new Image();
//EDIT2 you can hide img, or simply not add them to the DOM...
img1.style.display = "none";
img2.style.display = "none";
img1.src = "iphone4.png";
img2.src = "screenshot.png";
EDIT: DO NOT DO THAT and your images won't be displayed
document.body.append(img1);
OR
document.getElementById("myID").append(img2);
"What I'm doing is merging two images using JS"
Your problem is probably due to the fact that you are trying to draw images that have not been loaded yet. To circumvent this issue, you could create the images dynamically and set their src attribute to start loading the image and listen to the image's load event to know when they are fully loaded so that you can perform the merge safely.
I have not tested the code, but it should give you the idea.
var images = [
'iphone4.png',
'screenshot.png'
],
len = images.length,
i = 0,
loadedCount = 0,
img;
for (; i < len; i++) {
img = document.createElement('img');
//listener has to be added before setting the src attribute in case the image is cached
img.addEventListener('load', imgLoadHandler);
img.src = images[i];
images[i] = img;
}
function mergeImages() {
var img1 = images[0],
img2 = images[1];
//do the merging stuff
}
function imgLoadHandler() {
if (++loadedCount === len) {
mergeImages();
}
}
There is a way with HTML5, but it would still require the user to have dropped the file into a drop target or use a box.
Using the File API you can read files, and potentially decode them.
Actually reading the file blob and displaying it locally may be tricky though. You may be able to use the FileReader.readAsDataURL method to set the content as a data: URL for the image tag.
example:
$('#f').on('change', function(ev) {
var f = ev.target.files[0];
var fr = new FileReader();
fr.onload = function(ev2) {
console.dir(ev2);
$('#i').attr('src', ev2.target.result);
};
fr.readAsDataURL(f);
});​
see the working fiddle here :
http://jsfiddle.net/alnitak/Qszjg/
using jquery:
$('#my_image').attr('src','image.jpg');
using javasript:
document.getElementById("my_image").src="image.jpg";
just check path to your image
Write the below code in head block
<script>
window.onload = function () {
document.getElementById("img1").src="iphone4.png";
document.getElementById("img2").src="screenshot.png";
}
</script>
This will work
Thanks

How can I draw an image from the HTML5 File API on Canvas?

I would like to draw an image opened with the HTML5 File API on a canvas.
In the handleFiles(e) method, I can access the File with e.target.files[0] but I can't draw that image directly using drawImage. How do I draw an image from the File API on HTML5 canvas?
Here is the code I have used:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script>
window.onload = function() {
var input = document.getElementById('input');
input.addEventListener('change', handleFiles);
}
function handleFiles(e) {
var ctx = document.getElementById('canvas').getContext('2d');
ctx.drawImage(e.target.files[0], 20,20);
alert('the image is drawn');
}
</script>
</head>
<body>
<h1>Test</h1>
<input type="file" id="input"/>
<canvas width="400" height="300" id="canvas"/>
</body>
</html>
You have a File instance which is not an image.
To get an image, use new Image(). The src needs to be an URL referencing to the selected File. You can use URL.createObjectURL to get an URL referencing to a Blob (a File is also a Blob): http://jsfiddle.net/t7mv6/86/.
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image;
img.onload = function() {
ctx.drawImage(img, 20,20);
alert('the image is drawn');
}
img.src = URL.createObjectURL(e.target.files[0]);
Note: be sure to revoke the object url when you are done with it otherwise you'll leak memory. If you're not doing anything too crazy, you can just stick a URL.revokeObjectURL(img.src) in the img.onload function.
References:
https://developer.mozilla.org/en/DOM/File
http://html5demos.com/file-api
Live Example
function handleFiles(e) {
var ctx = document.getElementById('canvas').getContext('2d');
var url = URL.createObjectURL(e.target.files[0]);
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 20, 20);
}
img.src = url;
}
window.URL.createObjectUrldocs
You could also use the FileReader instead to create the object URL.
The FileReader has slightly better browser support.
The FileReader approach works in FF6 / Chrome. I'm not certain whether setting Img.src to a Blob is valid and cross-browser though.
Creating object urls is the correct way to do it.
Edit:
As mentioned in the commment window.URL support whilst offline seems unavailable in FF6/Chrome.
Here is a complete example (Fiddle) using FileReader (which has better browser support as mentioned by Raynos). In this example I also scale Canvas to fit the image.
In real life example you might scale the image to some maximum so that your form will not blow up ;-). Here is an example with scaling (Fiddle).
var URL = window.webkitURL || window.URL;
window.onload = function() {
var input = document.getElementById('input');
input.addEventListener('change', handleFiles, false);
// set original canvas dimensions as max
var canvas = document.getElementById('canvas');
canvas.dataMaxWidth = canvas.width;
canvas.dataMaxHeight = canvas.height;
}
function handleFiles(e) {
var ctx = document.getElementById('canvas').getContext('2d');
var reader = new FileReader();
var file = e.target.files[0];
// load to image to get it's width/height
var img = new Image();
img.onload = function() {
// setup scaled dimensions
var scaled = getScaledDim(img, ctx.canvas.dataMaxWidth, ctx.canvas.dataMaxHeight);
// scale canvas to image
ctx.canvas.width = scaled.width;
ctx.canvas.height = scaled.height;
// draw image
ctx.drawImage(img, 0, 0
, ctx.canvas.width, ctx.canvas.height
);
}
// this is to setup loading the image
reader.onloadend = function () {
img.src = reader.result;
}
// this is to read the file
reader.readAsDataURL(file);
}
// returns scaled dimensions object
function getScaledDim(img, maxWidth, maxHeight) {
var scaled = {
ratio: img.width / img.height,
width: img.width,
height: img.height
}
if (scaled.width > maxWidth) {
scaled.width = maxWidth;
scaled.height = scaled.width / scaled.ratio;
}
if (scaled.height > maxHeight) {
scaled.height = maxHeight;
scaled.width = scaled.height / scaled.ratio;
}
return scaled;
}
canvas {
border:1px solid black
}
<input type="file" id="input"/>
<div>
<canvas width="400" height="300" id="canvas"/>
</div>

Categories