saveFrames() does not honor frame rate
I am drawing an animation on canvas. function setup() { canvas = createCanvas(400, 450, WEBGL); frameRate(20);
I see faster speed when my laptop is charging. When I unplug the power cable, the draw on canvas is much slower.
This impacts the saveFrames function. How can I save the certain duration worth of frames if the duration depends on whether the device is plugged in or not? When plugged in, a lot more frames are saved than when not charging, even though the 'duration' is for example 2 seconds. saveFrames('frame', 'jpg', duration, fps)
So using saveFrames() how can I reliably save the certain amount of frames despite the speed change?
Works fine with saveGif('test', 2); I get exactly 2 seconds long video, 40 frames total, no matter what device, it does not depend on the device speed. It honors the frameRate() set in setup(), and p5.js pauses the sketch's draw loop during recording. It will capture exactly the number of frames implied by the duration × framerate, regardless of actual performance.
I need to save the 2 second duration amount of frames also using saveFrames(). Currently it seems to be buggy. It does not pause or slow down the draw loop, and records frames in real time for the given duration.
If the system is underpowered (on battery), draw() runs slower, resulting in fewer total frames captured than expected.
It looks like saveFrames sets two timers, one that runs n times to create the frames, and a second that downloads all of them. These two timers currently race each other. I think to avoid this race, the download needs to happen inside the first timer (e.g. by checking the length of the frames array and doing the download if it's at the expected length.)
https://github.com/processing/p5.js/blob/a64d54a2d1dcd337eae8cd0958f082a89705da91/src/image/image.js#L670-L685
In the mean time, as a workaround, you could try calling saveCanvas() in draw() if frameCount < n for some desired n.
Hi @davepagurek, I'm happy to help.
Thanks @wayneharish10! I'll assign this to you.
It looks like
saveFramessets two timers, one that runsntimes to create the frames, and a second that downloads all of them. These two timers currently race each other. I think to avoid this race, the download needs to happen inside the first timer (e.g. by checking the length of theframesarray and doing the download if it's at the expected length.)Lines 670 to 685 in a64d54a
const frameFactory = setInterval(() => { frames.push(makeFrame(fName + count, ext, cnv)); count++; }, 1000 / fps);
setTimeout(() => { clearInterval(frameFactory); if (callback) { callback(frames); } else { for (const f of frames) { p5.prototype.downloadFile(f.imageData, f.filename, f.ext); } } frames = []; // clear frames }, duration + 0.01); In the mean time, as a workaround, you could try calling
saveCanvas()indraw()ifframeCount < nfor some desiredn.
Hey @davepagurek , To add to what you've mentioned, because of the race condition between the timers, there is a possibility that setTimeout() be called before the frames are made in the frameFactory timer.
So, my proposed implementation would be calculating the total number of frames and clearing the frameFactory timer once attained, like so:
const totalFrames = (duration * fps) / 1000;
const makeFrame = p5.prototype._makeFrame;
const cnv = this._curElement.elt;
let frames = [];
const frameFactory = setInterval(() => {
if (count >= totalFrames) {
clearInterval(frameFactory);
if (callback) {
callback(frames);
} else {
for (const f of frames) {
p5.prototype.downloadFile(f.imageData, f.filename, f.ext);
}
}
frames = []; // clear frames
}
frames.push(makeFrame(fName + count, ext, cnv));
count++;
}, 1000 / fps);
Sorry for the delay @HarishVX2! that sounds good to me.
saveFrames() because of the way it is implemented has certain limits, if you'd want a more consistent output, do try p5.record.js that I've put together. There are still some stuff I want to implement for it but do have a try, it can capture image sequence at the frame rate that you decide (which can be different from the sketch frame rate) before downloading it all as a zip file.
Hey @davepagurek , is anyone still working on this issue? If not, then I would like to work on this issue. Please assign this to me.
@HarishVX2, are you still working on this?