I'm constructing an application that allows multiple devices to stream their webcam, through the browser, to another device. Each streaming device takes a DataURL image from the webcam and sends it over WebSocket to a server, which will then distribute the array of images to viewers. The viewer will parse the message and display each image on its own individual <canvas>.
It works fine when one user is streaming, but when a second device begins to stream, the first one freezes. It appears the last image in the array is the only one to actually be rendered.
I've been reviewing this code for several hours and can't understand what I'm missing here.
var socket = new WebSocket("wss://my_server:8443");
socket.onmessage = function(msg) {
//parse the incoming message into a usable array
msg = JSON.parse(msg.data);
var div = document.getElementById("streams");
//for every incoming stream
for (var i in msg) {
//create a new Image, then render it onto a canvas element.
var img = new Image();
img.onload = function() {
//if there isn't a canvas element to render it onto already, create one (for when a stream is created)
if (document.getElementById(i) == null) {
var c = document.createElement("canvas");
c.width = img.width;
c.height = img.height;
c.setAttribute("id", i);
div.appendChild(c);
}
//draw the image onto the canvas
var canvas = document.getElementById(i);
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
}
//set the Data URL as the image's SRC
img.src = msg[i];
}
}
I originally just added <img> elements to the <div>, but it ended up taking too long to load, creating short flashes in between each frame.
What's wrong with my code? It seems to freeze all active streams except for the one most recently started (last in the array).
Related
My question might be stupid, but I'm stuck with a simple task.
I'm writing a web browser game where I load images from a local folder to a 2d canvas, then, at some time, redraw it with getImageData().
The code sample is the following
let img = new Image()
// img.crossOrigin = "anonymous"
img.src = "assets/hidden.png"
let ctx = document.getElementById("canvas").getContext("2d")
img.onload = () => {
console.log("loaded img", img.src)
ctx.drawImage(img, 0, 0)
let c = ctx.getImageData(0, 0, 10, 10)
}
The original problem was that calling getImageData() causes Uncaught DOMException: The operation is insecure.
So I added img.crossOrigin = "anonymous" (context.getImageData() operation is insecure) to overcome this, but now the image is not loaded at all. How do I normally use images in canvas?
I am trying to paint an image on a canvas before I get its dataURL(), but the data returned is like empty.
When I check it in the console, I see there is a lot of A in the string : ("data:image/png;base64,iVBO..some random chars... bQhfoAAAAAAAAAA... a lot of A ...AAAASUVORK5CYII=")
When I try to append the canvas to the document, nothing is drawn either and I don't have any error thrown in the console.
What is the problem here ?
Here is my code :
var img = new Image();
img.src = "http://somerandomWebsite/picture.png";
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0,0); // this doesn't seem to work
var dataURL = canvas.toDataURL(); // this will give me a lot of "A"
doSomething(dataURL);
Also, when doing a quick refresh, the image gets drawn correctly onto the canvas but I've got an error message in the console and dataURL is empty.
The message in Firefox is : "SecurityError: The operation is insecure.",
in Chrome it is "Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.",
and on IE I just get "SecurityError".
What does it mean ?
You have to wait that your image has loaded before you can paint it on the canvas.
To do so, simply use the load event handler of your <img> element :
// create a new image
var img = new Image();
// declare a function to call once the image has loaded
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0,0);
var dataURL = canvas.toDataURL();
// now you can do something with the dataURL
doSomething(dataURL);
}
// now set the image's src
img.src = "http://somerandomWebsite/picture.png";
Also, for the canvas' context.toDataURL() and context.getImageData to work properly, you have to get your image resource in a cross-origin compliant way, otherwise the canvas is "tainted" which means that any method to get pixels data will be blocked.
If your image comes from the same server, no problem.
If your image is served from an external server, be sure that it allows yours in its cross-origin headers and set the img.crossOrigin to "use-credentials".
If the server does allow anonymous requests, you can set the img.crossOrigin to "anonymous".
Nota Bene : CORS header is sent by the server, the cross-origin attribute will only let it know that you want to use CORS to get the image data, in no way you can circumvent it if the server is not set properly.
Also some UserAgents (IE & Safari) still haven't implemented this attribute.
Edge Case : If some of your images are from your server and some are from a CORS compliant one, then you may want to use the onerror event handler which should fire if you set the cross-origin attribute to "anonymous" on a non CORS server.
function corsError(){
this.crossOrigin='';
this.src='';
this.removeEventListener('error', corsError, false);
}
img.addEventListener('error', corsError, false);
Trying to do image optimization(reduce the image size) and add watermaker to multiple images before uploading to cloud env(S3).
Approaches:
browser-image-compression - Used browser-image-compression image optimization working perfectly, but watermark unable to add.
watermark.js - Used watermark.js without image optimization, the size of image getting increased to 2x.
const compressedFile = await imageCompression(file, options)
let compressedFile1 = await watermark([compressedFile]).blob(watermark.text.center('watermark.js',
'300px serif', '#fff', 0.5))
Performance is also the key aspect of both operations.
Don't want to perform any image manipulation serverside or in a cloud environment(serverless image manipulation).
Any idea on how to add a watermark and also, optimize the image without losing the quality of the image?
Thanks
I have a similar use case where users need to take pictures and then these have to be uploaded to a server with a watermark.
First, I add the watermark and then I change the format to JPEG, which has good quality and does not take as much space as the PNG output of watermarkJS.
This is the function called on the output of the watermark (as dataURL):
async convertImageToJpeg(image):Promise<string>{
return new Promise(function (resolved,rejected) {
var i = new Image();
i.onload = function(){
var canvas = document.createElement("canvas");
canvas.width = i.width;
canvas.height = i.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(i,0,0);
var dataUrl = canvas.toDataURL('image/jpeg', 0.3);
resolved(dataUrl);
}
i.src = image;
})
}
I have the same image and the same size of canvas, but the output is different. I want the same output, how should I do it?
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL();
setBreakpoint(dataURL);
callback.call(this, dataURL);
canvas = null;
};
img.src = url;
Images drawn onto a canvas are decoded before being drawn, then reencoded when the toDataURL method is called.
This process will produce some variations in every browser (e.g some will be able to decode color-profiles embedded in the image while others won't), and even every machine (look at canvas fingerprinting and this post by #Oriol which concern images with transparency). Add to that that every browser will use different encoders/settings for a different tradeoff between speed, size and quality and you arrive at a situation where you can't expect two users to produce the same result from the same input image.
But since all you do with that canvas is to draw an image, you should rather use a FileReader and its method readAsDataURL(). For external files, you can still use it by first fetching the resource as a Blob.
This will work directly on the binary data of the file, encoding each byte to its base64 representation, and thus you will be sure to have the same result in every browser.
Here is a snippet which will test your browser's conversion against mine's.
fetch("https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png")
.then((resp) => resp.ok && resp.blob())
.then((blob) => {
const reader = new FileReader();
reader.onload = (evt) => {
if (reader.result === imageDataURL) {
console.log("same result");
}
else {
console.log("please post a comment stating which browser has such a bug");
}
};
reader.readAsDataURL(blob);
});
var imageDataURL = "data:image/png;base64,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"
However for the ones using the canvas for real drawing, as we said above, you can't really have the same results between different UAs. Every method from drawing ones to export ones may produce different results and you would have to actually rewrite all these methods entirely in order to have the exact same results.
For the ones that really need this, a project like node-canvas could help, though it would obviously be a lot less performant than native implementations.
I have been searching this website for answers to this question, but I couldn't seem to find any. So I want to have the client provide an image to be loaded into a canvas for processing and that's it. So I don't want to save it on the server or on a cloud, but I just want to copy the image to an HTML5 Canvas to be processed from there. Is there a way I can do that without actually saving the file?
I'm not sure if I understand your question. You want that the user can open an image from the client and you load it into a html5 canvas. correct?
If so: you can use an input field of type file. In your code you use URL.createObjectUrl to create object urls from the local selected images. With "Image" you can load the image and in the onload event you draw it to the canvas.
var file = document.getElementById('file'); // the input element of type file
file.onchange = function(e) {
var ctx = document.getElementById('canvas').getContext('2d'); // load context of canvas
var img = new Image();
img.src = URL.createObjectURL(e.target.files[0]); // use first selected image from input element
img.onload = function() {
ctx.drawImage(img, 0, 0); // draw the image to the canvas
}
}