fix(render): reject incomplete captured frames (#2293)

This commit is contained in:
Miguel Ángel
2026-07-12 22:17:41 -04:00
committed by GitHub
parent c830aa83c5
commit 995885484f
2 changed files with 23 additions and 3 deletions
@@ -230,7 +230,7 @@ describe("executeDiskCaptureWithAdaptiveRetry — transient Target-closed single
const writeAllFrames = (framesDir: string, totalFrames: number): void => {
for (let i = 0; i < totalFrames; i++) {
writeFileSync(join(framesDir, formatCaptureFrameName(i, "jpg")), "x");
writeFileSync(join(framesDir, formatCaptureFrameName(i, "jpg")), "captured-frame");
}
};
@@ -1432,7 +1432,10 @@ describe("adaptive missing-frame retry helpers", () => {
it("finds contiguous missing frame ranges from captured disk frames", () => {
const framesDir = makeFramesDir();
for (const frameIndex of [0, 1, 4]) {
writeFileSync(join(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.jpg`), "x");
writeFileSync(
join(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.jpg`),
"captured-frame",
);
}
expect(findMissingFrameRanges(6, framesDir, "jpg")).toEqual([
@@ -1441,6 +1444,18 @@ describe("adaptive missing-frame retry helpers", () => {
]);
});
it("retries a worker placeholder instead of accepting a truncated sequence", () => {
const framesDir = makeFramesDir();
for (let frameIndex = 0; frameIndex < 4; frameIndex++) {
writeFileSync(
join(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.jpg`),
frameIndex === 2 ? "x" : "captured-frame",
);
}
expect(findMissingFrameRanges(4, framesDir, "jpg")).toEqual([{ startFrame: 2, endFrame: 3 }]);
});
it("builds retry batches that cap active workers per attempt", () => {
const batches = buildMissingFrameRetryBatches(
[
@@ -603,7 +603,12 @@ export function findMissingFrameRanges(
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
const framePath = join(framesDir, formatCaptureFrameName(frameIndex, frameExt));
const missing = !existsSync(framePath);
// A capture worker can leave a zero/one-byte placeholder behind when it
// exits between creating the destination and writing the image. FFmpeg's
// image2 demuxer treats that as end-of-sequence but still exits 0, which
// used to let a truncated video be reported as successful. Real JPEG and
// PNG captures are necessarily larger than their 8-byte file signatures.
const missing = !existsSync(framePath) || statSync(framePath).size <= 8;
if (missing && rangeStart === null) {
rangeStart = frameIndex;
} else if (!missing && rangeStart !== null) {