Skip to content

Commit 9d91c2a

Browse files
fix(render): scale timeout for long video encodes (#2244)
1 parent 6878831 commit 9d91c2a

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

packages/producer/src/services/render/stages/encodeStage.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ describe("gif encode args", () => {
154154
});
155155

156156
describe("runEncodeStage config plumbing", () => {
157+
it("scales the encode timeout for long compositions", async () => {
158+
const { runEncodeStage } = await import("./encodeStage.js");
159+
160+
await runEncodeStage(
161+
makeInput({
162+
job: {
163+
...makeInput().job,
164+
duration: 754.8,
165+
},
166+
engineConfig: { ffmpegEncodeTimeout: 600_000 },
167+
}),
168+
);
169+
170+
expect(encodeFramesFromDirMock.mock.calls[0]?.[5]).toEqual({
171+
ffmpegEncodeTimeout: 3_019_200,
172+
});
173+
});
174+
157175
it("prefers engine config supplied by the orchestrator", async () => {
158176
const { runEncodeStage } = await import("./encodeStage.js");
159177
const orchestratorEngineConfig = { ffmpegEncodeTimeout: 54_321 };

packages/producer/src/services/render/stages/encodeStage.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,16 @@ export async function runEncodeStage(input: EncodeStageInput): Promise<EncodeSta
277277
// ── Stage 5: Encode ───────────────────────────────────────────────
278278
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
279279

280+
// ffmpegEncodeTimeout is a total wall-clock cap, not an inactivity timeout.
281+
// A fixed ten-minute cap reliably kills long high-quality disk-frame encodes
282+
// that are still making progress. Preserve larger operator overrides while
283+
// guaranteeing four seconds of encode budget per second of source video.
284+
const scaledEncodeTimeout = Math.ceil((job.duration ?? 0) * 4_000);
285+
const videoEngineCfg =
286+
scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout
287+
? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout }
288+
: engineCfg;
289+
280290
const frameExt = needsAlpha ? "png" : "jpg";
281291
const framePattern = `frame_%06d.${frameExt}`;
282292
const encoderOpts = {
@@ -305,15 +315,15 @@ export async function runEncodeStage(input: EncodeStageInput): Promise<EncodeSta
305315
encoderOpts,
306316
chunkedEncodeSize,
307317
abortSignal,
308-
engineCfg,
318+
videoEngineCfg,
309319
)
310320
: await encodeFramesFromDir(
311321
framesDir,
312322
framePattern,
313323
videoOnlyPath,
314324
encoderOpts,
315325
abortSignal,
316-
engineCfg,
326+
videoEngineCfg,
317327
);
318328
assertNotAborted();
319329

0 commit comments

Comments
 (0)