I want to display a stream of images in a browser component.
So far I've tried something like
function fetchData() {
fetch("data.png?")
.then((response) => response.blob())
.then((blob) => {
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height
myimage = new Image();
myimage.onload = function() {
ctx.drawImage(myimage, 0, 0,width,height);
}
const imageObjectURL = URL.createObjectURL(blob);
myimage.src = imageObjectURL;
}
...
setInterval(fetchData, 50);
This sort of works. But if I make the setInterval
period too short I start to get ERR_INSUFFICIENT_ RESOURCES error. I guess this is because fetch requests or responses pile up somewhere.
Ideally the drawing of a fetched image would trigger the next fetch. However I'm worried that sometimes no drawing will take place or a fetch might not complete in which case the process would stop.
Further I would like to have a few fetches in-flight to maximize performance and they should be completed in order, but without excessive buffering as the fetched data is interactive controlled from the browser so it is not like I would want to buffer a ten seconds worth of data.
So I'm looking for a general strategy implement above, I'm sure many people have worked on things like this before but I've not found examples.
setInterval
isn't appropriate because it doesn't count the fetch and image load time.
To get max FPS you should call fetchData
immediately after the drawing.
I suggest to draw with requestAnimationFrame
to avoid possible image render tearing.
You should have some stopped
flag to be able to stop the infinite cycle.
Also you should avoid requesting the context for each renders, it's expensive.
Also you could use more ES6+ features and avoid creating intermediate 1 line variables:
// get ctx outside
const ctx = canvas.getContext("2d");
let stopped = false;
const delay = (delay = 0) => new Promise(r => setTimeout(r, delay));
async function fetchData() {
const r = await fetch("data.png?");
if(stopped) return;
const [width, height] = canvas;
const myimage = new Image();
myimage.onload = () => {
requestAnimationFrame(async () => {
ctx.drawImage(myimage, 0, 0,width,height);
await delay(50); // if you don't need a delay and need max FPS, remove that and async
fetchData();
}
};
myimage.src = URL.createObjectURL(await r.blob());
}