diff --git a/packages/aws-lambda/src/handler.test.ts b/packages/aws-lambda/src/handler.test.ts index 77333dd44b..d09a166ffa 100644 --- a/packages/aws-lambda/src/handler.test.ts +++ b/packages/aws-lambda/src/handler.test.ts @@ -767,8 +767,11 @@ describe("handler dispatch", () => { } const options = renderChunkMock.mock.calls.map((call) => (call as unknown[])[3]); expect(options).toEqual([ - { maxCaptureWorkers: undefined }, - { maxCaptureWorkers: Math.max(1, Math.min(5, availableParallelism() - 1)) }, + { maxCaptureWorkers: undefined, interleaveCapture: true }, + { + maxCaptureWorkers: Math.max(1, Math.min(5, availableParallelism() - 1)), + interleaveCapture: true, + }, ]); const captures = logged .filter((line) => line.includes('"chunk_capture"')) diff --git a/packages/aws-lambda/src/handler.ts b/packages/aws-lambda/src/handler.ts index d7d0e56fc4..2c2c1cd861 100644 --- a/packages/aws-lambda/src/handler.ts +++ b/packages/aws-lambda/src/handler.ts @@ -647,6 +647,7 @@ async function handleRenderChunk( const result: ChunkResult = await primitive(planDir, event.ChunkIndex, chunkOutputBase, { maxCaptureWorkers: lambdaMaxCaptureWorkers(), + interleaveCapture: true, }); logEvent({ event: "chunk_capture", @@ -703,6 +704,7 @@ async function handleRenderChunkV2( ); const result = await primitive(planDir, event.ChunkIndex, chunkOutputBase, { maxCaptureWorkers: lambdaMaxCaptureWorkers(), + interleaveCapture: true, }); logEvent({ event: "chunk_capture", diff --git a/packages/producer/src/services/distributed/renderChunk.test.ts b/packages/producer/src/services/distributed/renderChunk.test.ts index 305fad3323..e195c0e113 100644 --- a/packages/producer/src/services/distributed/renderChunk.test.ts +++ b/packages/producer/src/services/distributed/renderChunk.test.ts @@ -55,6 +55,7 @@ import { CHUNK_INDEX_OUT_OF_RANGE, distributedCaptureSessionDependencies, extractDeferredVideoFramesForChunk, + interleavesCapture, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, @@ -1105,6 +1106,18 @@ describe("resolvePresetForLockedEncoder", () => { }); }); +describe("interleavesCapture", () => { + it("is off for a caller that does not ask, so CPU-billed callers get contiguous ranges", () => { + expect(interleavesCapture(true, undefined)).toBe(false); + expect(interleavesCapture(true, { maxCaptureWorkers: 4 })).toBe(false); + }); + + it("is on only when the caller asks and the encoder overlaps", () => { + expect(interleavesCapture(true, { interleaveCapture: true })).toBe(true); + expect(interleavesCapture(false, { interleaveCapture: true })).toBe(false); + }); +}); + describe("resolveChunkCaptureWorkers", () => { it("keeps the machine-wide sizing when the runtime sets no budget", () => { expect(resolveChunkCaptureWorkers(121, 3, undefined)).toBe(3); diff --git a/packages/producer/src/services/distributed/renderChunk.ts b/packages/producer/src/services/distributed/renderChunk.ts index 9e689d80b3..381cbb754a 100644 --- a/packages/producer/src/services/distributed/renderChunk.ts +++ b/packages/producer/src/services/distributed/renderChunk.ts @@ -221,6 +221,11 @@ export interface RenderChunkOptions { * this many, one browser per 20 frames; never lowers it. */ maxCaptureWorkers?: number; + interleaveCapture?: boolean; +} + +export function interleavesCapture(overlapping: boolean, options?: RenderChunkOptions): boolean { + return overlapping && options?.interleaveCapture === true; } /** A browser starts in about 3 s, a small share of 20 frames' capture. */ @@ -999,7 +1004,7 @@ export async function renderChunk( assertNotAborted: () => {}, frameRange: range, onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame), - interleaveFrames: overlap.current !== null, + interleaveFrames: interleavesCapture(overlap.current !== null, options), }).catch(async (captureError: unknown) => { await overlap.current?.abort(); overlap.current = null;