Terminate saveFrames() capture by frame count instead of wall-clock d… - #9012
Terminate saveFrames() capture by frame count instead of wall-clock d…#9012harshiltewari2004 wants to merge 3 commits into
Conversation
…uration The download timer raced the capture timer, so a throttled device captured fewer frames than duration x fps. Termination now happens inside the capture interval once the target frame count is reached. Resolves processing#7958
|
Gentle bump — the workflows on this PR and on #9031 are still awaiting |
|
Hi @ksen0 I'm so sorry to tag directly — this one's been open about a month and The sibling PR (#9031) got merged yesterday and touched the same file, so |
Resolves #7958
Changes
saveFrames()used two timers: one capturing a frame every1000/fpsms, and asetTimeoutthat cleared it afterdurationms. Because the cutoff waswall-clock based, a throttled device (battery saver, background tab) fired the
capture interval fewer times inside that window and captured fewer frames than
duration x fps.Termination is now based on frame count: the capture interval stops itself once
countreachestotalFrames, and the download/callback happens there. ThesetTimeoutis removed.This is @HarishVX2's approach from the issue thread — credit to him. The one
difference is ordering: the frame is captured before the count is checked, so
the download fires on the same tick as the final frame. Checking first would
capture one extra frame into the already-cleared array.
Notes
totalFramesis(duration * fps) / 1000becausedurationhas already beenconverted to milliseconds a few lines above.
duration = 0orfps = 0,totalFramesis 0, so one frame is capturedbefore termination where the old code captured none. Happy to add a guard if
you'd prefer. I checked in the browser console that
setIntervalwith a delayof
Infinity(what1000 / 0gives) fires immediately, sofps = 0terminates on the first tick rather than hanging.
Tests
Added a count assertion in
test/unit/image/downloading.js. The existingsaveFramestests iteratearr.lengthwithout asserting it, so they don'tcatch this.
Parameters are
duration = 0.4,fps = 7.93, givingtotalFrames = 3.172.That fractional value is deliberate: when
totalFramesis a whole number thefinal tick lands exactly on the old timeout and the result races. With a
fractional target the two implementations differ by exactly one frame every run.
Verified by stashing the source change and re-running: the test fails with
expected 3 to equal 4on unfixed code, and passes with the fix. Full file:12 passed.
AI assistance
I used Claude to investigate the timer behaviour and review my approach. I read
the source and traced the race myself, wrote the fix and the test, and verified
both locally.
PR Checklist
npm run lintpasses