@hyperframes/producer 0.8.179 → 0.8.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/distributed.d.ts +2 -2
- package/dist/distributed.js +52 -19
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +54 -54
- package/dist/index.js +51 -19
- package/dist/public-server.js +2 -1
- package/dist/services/distributed/assemble.d.ts +4 -2
- package/dist/services/distributed/chunkAudio.d.ts +8 -0
- package/dist/services/distributed/chunkFrames.d.ts +2 -1
- package/dist/services/distributed/planProtocol.d.ts +1 -0
- package/dist/services/distributed/planV2Execution.d.ts +4 -4
- package/dist/services/distributed/renderChunk.d.ts +23 -3
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -13927,6 +13927,7 @@ var FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
|
13927
13927
|
var PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
13928
13928
|
var MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
13929
13929
|
var CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
13930
|
+
var CHUNK_FRAME_RANGE_INVALID = "CHUNK_FRAME_RANGE_INVALID";
|
|
13930
13931
|
var MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
13931
13932
|
var RenderChunkValidationError = class extends Error {
|
|
13932
13933
|
code;
|
|
@@ -14178,7 +14179,8 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
14178
14179
|
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
14179
14180
|
acceptsLegacyV1WithoutDescriptor: true
|
|
14180
14181
|
})
|
|
14181
|
-
})
|
|
14182
|
+
}),
|
|
14183
|
+
chunkFrameRange: true
|
|
14182
14184
|
});
|
|
14183
14185
|
function getDistributedRenderCapabilities() {
|
|
14184
14186
|
return DISTRIBUTED_RENDER_CAPABILITIES;
|
|
@@ -14893,6 +14895,18 @@ function audioCutForChunk(grid, chunks, index, fps) {
|
|
|
14893
14895
|
const durationSeconds = end === grid.packetCount ? null : cutSeconds(end) - (seekSeconds ?? 0);
|
|
14894
14896
|
return { seekSeconds, durationSeconds, firstPacket: first, packetCount: end - first };
|
|
14895
14897
|
}
|
|
14898
|
+
function chunksWithSubRange(chunks, index, range) {
|
|
14899
|
+
const slice = chunks[index];
|
|
14900
|
+
const parts = [
|
|
14901
|
+
{ startFrame: slice.startFrame, endFrame: range.startFrame },
|
|
14902
|
+
range,
|
|
14903
|
+
{ startFrame: range.endFrame, endFrame: slice.endFrame }
|
|
14904
|
+
].filter((part) => part.endFrame > part.startFrame);
|
|
14905
|
+
const split = [...chunks.slice(0, index), ...parts, ...chunks.slice(index + 1)].map(
|
|
14906
|
+
(chunk, i) => ({ index: i, startFrame: chunk.startFrame, endFrame: chunk.endFrame })
|
|
14907
|
+
);
|
|
14908
|
+
return { chunks: split, index: range.startFrame > slice.startFrame ? index + 1 : index };
|
|
14909
|
+
}
|
|
14896
14910
|
async function probeAacPacketGrid(audioPath) {
|
|
14897
14911
|
const probe = await runFfprobeJson([
|
|
14898
14912
|
"-v",
|
|
@@ -18234,11 +18248,12 @@ function cfrRateControlArgs(locked) {
|
|
|
18234
18248
|
}
|
|
18235
18249
|
return ["-preset", preset.preset, "-crf", String(locked.crf ?? preset.quality)];
|
|
18236
18250
|
}
|
|
18237
|
-
async function assemble(planDir,
|
|
18251
|
+
async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
18238
18252
|
const start = Date.now();
|
|
18239
18253
|
const log = options?.logger ?? defaultLogger;
|
|
18240
18254
|
const cfr = options?.cfr === true;
|
|
18241
|
-
const plan2 = readAssemblePlan(planDir,
|
|
18255
|
+
const plan2 = readAssemblePlan(planDir, chunkOutputs);
|
|
18256
|
+
const chunkPaths = chunkOutputs.flat();
|
|
18242
18257
|
if (plan2.dimensions.format === "png-sequence") {
|
|
18243
18258
|
return mergePngFrameDirs(chunkPaths, outputPath, plan2.totalFrames, audioPath, start);
|
|
18244
18259
|
}
|
|
@@ -18321,7 +18336,12 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
18321
18336
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
18322
18337
|
);
|
|
18323
18338
|
}
|
|
18324
|
-
for (const
|
|
18339
|
+
for (const [index, entry] of chunkPaths.entries()) {
|
|
18340
|
+
if (typeof entry !== "string" && entry.length === 0) {
|
|
18341
|
+
throw new Error(`[assemble] chunk ${index} has an empty list of parts`);
|
|
18342
|
+
}
|
|
18343
|
+
}
|
|
18344
|
+
for (const path of chunkPaths.flat()) {
|
|
18325
18345
|
if (!existsSync27(path)) {
|
|
18326
18346
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
18327
18347
|
}
|
|
@@ -18540,6 +18560,17 @@ function validatePlanVideosForChunk(value) {
|
|
|
18540
18560
|
);
|
|
18541
18561
|
}
|
|
18542
18562
|
}
|
|
18563
|
+
function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
|
|
18564
|
+
if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
|
|
18565
|
+
const { startFrame, endFrame } = frameRange;
|
|
18566
|
+
if (!Number.isSafeInteger(startFrame) || !Number.isSafeInteger(endFrame) || startFrame < slice.startFrame || endFrame > slice.endFrame || endFrame <= startFrame) {
|
|
18567
|
+
throw new RenderChunkValidationError(
|
|
18568
|
+
CHUNK_FRAME_RANGE_INVALID,
|
|
18569
|
+
`[renderChunk] frameRange ${JSON.stringify(frameRange)} must be a non-empty integer range inside chunk ${chunkIndex}'s slice [${slice.startFrame}, ${slice.endFrame})`
|
|
18570
|
+
);
|
|
18571
|
+
}
|
|
18572
|
+
return { startFrame, endFrame };
|
|
18573
|
+
}
|
|
18543
18574
|
var distributedCaptureSessionDependencies = {
|
|
18544
18575
|
createCaptureSession: createCaptureSession10,
|
|
18545
18576
|
assertSwiftShader,
|
|
@@ -18610,7 +18641,7 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
|
|
|
18610
18641
|
if (lockedEncoder.encoder !== "libvpx-vp9-software") return void 0;
|
|
18611
18642
|
return lockedEncoder.vp9CpuUsed ?? LEGACY_DISTRIBUTED_VP9_CPU_USED;
|
|
18612
18643
|
}
|
|
18613
|
-
async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
18644
|
+
async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
18614
18645
|
const start = Date.now();
|
|
18615
18646
|
const log = defaultLogger;
|
|
18616
18647
|
const planJsonPath = join42(planDir, "plan.json");
|
|
@@ -18664,13 +18695,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18664
18695
|
if (slice === void 0) {
|
|
18665
18696
|
throw new RenderChunkValidationError(CHUNK_INDEX_OUT_OF_RANGE, "[renderChunk] missing slice");
|
|
18666
18697
|
}
|
|
18667
|
-
const
|
|
18668
|
-
if (
|
|
18698
|
+
const sliceFrames = slice.endFrame - slice.startFrame;
|
|
18699
|
+
if (sliceFrames <= 0) {
|
|
18669
18700
|
throw new RenderChunkValidationError(
|
|
18670
18701
|
CHUNK_INDEX_OUT_OF_RANGE,
|
|
18671
|
-
`[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${
|
|
18702
|
+
`[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${sliceFrames}`
|
|
18672
18703
|
);
|
|
18673
18704
|
}
|
|
18705
|
+
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
18706
|
+
const framesInChunk = range.endFrame - range.startFrame;
|
|
18674
18707
|
const compiledDir = join42(planDir, "compiled");
|
|
18675
18708
|
if (!existsSync28(compiledDir)) {
|
|
18676
18709
|
throw new RenderChunkValidationError(
|
|
@@ -18735,10 +18768,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18735
18768
|
const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
|
|
18736
18769
|
planDir,
|
|
18737
18770
|
planVideos,
|
|
18738
|
-
frames: Array.from(
|
|
18739
|
-
{ length: slice.endFrame - slice.startFrame },
|
|
18740
|
-
(_, i) => slice.startFrame + i
|
|
18741
|
-
),
|
|
18771
|
+
frames: Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i),
|
|
18742
18772
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
18743
18773
|
outputRoot: videoFramesDir,
|
|
18744
18774
|
cfg,
|
|
@@ -18894,7 +18924,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18894
18924
|
abortSignal: void 0,
|
|
18895
18925
|
assertNotAborted: () => {
|
|
18896
18926
|
},
|
|
18897
|
-
frameRange:
|
|
18927
|
+
frameRange: range
|
|
18898
18928
|
});
|
|
18899
18929
|
const observedModes = new Set(capturePerfs.map((perf) => perf.captureMode));
|
|
18900
18930
|
if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
|
|
@@ -18988,12 +19018,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18988
19018
|
});
|
|
18989
19019
|
if (encoder.chunksCarryAudio) {
|
|
18990
19020
|
const audioScratchPath = `${outputChunkPath}.audio.m4a`;
|
|
19021
|
+
const audioChunks = options?.frameRange ? chunksWithSubRange(chunks, chunkIndex, range) : { chunks, index: chunkIndex };
|
|
18991
19022
|
await muxChunkWithAudio({
|
|
18992
19023
|
videoPath: videoOnlyPath,
|
|
18993
19024
|
audioPath: chunkAudioSource(planDir, chunkIndex, audioScratchPath),
|
|
18994
19025
|
outputPath: outputChunkPath,
|
|
18995
|
-
chunks,
|
|
18996
|
-
chunkIndex,
|
|
19026
|
+
chunks: audioChunks.chunks,
|
|
19027
|
+
chunkIndex: audioChunks.index,
|
|
18997
19028
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
18998
19029
|
timeoutMs: cfg.ffmpegProcessTimeout
|
|
18999
19030
|
});
|
|
@@ -19022,8 +19053,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
19022
19053
|
const perfPayload = {
|
|
19023
19054
|
planHash: plan2.planHash,
|
|
19024
19055
|
chunkIndex,
|
|
19025
|
-
startFrame:
|
|
19026
|
-
endFrame:
|
|
19056
|
+
startFrame: range.startFrame,
|
|
19057
|
+
endFrame: range.endFrame,
|
|
19027
19058
|
framesEncoded,
|
|
19028
19059
|
durationMs,
|
|
19029
19060
|
planHashMs,
|
|
@@ -19052,6 +19083,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
19052
19083
|
outputPath: outputChunkPath,
|
|
19053
19084
|
outputKind,
|
|
19054
19085
|
framesEncoded,
|
|
19086
|
+
frameRange: range,
|
|
19055
19087
|
sha256,
|
|
19056
19088
|
durationMs,
|
|
19057
19089
|
planHashMs,
|
|
@@ -19068,12 +19100,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
19068
19100
|
}
|
|
19069
19101
|
|
|
19070
19102
|
// src/services/distributed/planV2Execution.ts
|
|
19071
|
-
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
|
|
19103
|
+
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
19072
19104
|
const workRoot = mkdtempSync9(join43(tmpdir5(), "hf-plan-v2-chunk-"));
|
|
19073
19105
|
const materializedPlanDir = join43(workRoot, "plan");
|
|
19074
19106
|
try {
|
|
19075
19107
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
19076
|
-
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath);
|
|
19108
|
+
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
19077
19109
|
} finally {
|
|
19078
19110
|
rmSync17(workRoot, { recursive: true, force: true });
|
|
19079
19111
|
}
|
package/dist/public-server.js
CHANGED
|
@@ -13845,7 +13845,8 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
13845
13845
|
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
13846
13846
|
acceptsLegacyV1WithoutDescriptor: true
|
|
13847
13847
|
})
|
|
13848
|
-
})
|
|
13848
|
+
}),
|
|
13849
|
+
chunkFrameRange: true
|
|
13849
13850
|
});
|
|
13850
13851
|
|
|
13851
13852
|
// src/services/render/stages/planHash.ts
|
|
@@ -31,6 +31,7 @@ export interface AssembleResult {
|
|
|
31
31
|
framesEncoded: number;
|
|
32
32
|
fileSize: number;
|
|
33
33
|
}
|
|
34
|
+
export type AssembleChunkPaths = readonly (string | readonly string[])[];
|
|
34
35
|
type CfrRateControlSource = Partial<Pick<LockedRenderConfig, "quality" | "crf" | "bitrate">>;
|
|
35
36
|
export declare function cfrRateControlArgs(locked: CfrRateControlSource): string[];
|
|
36
37
|
/**
|
|
@@ -39,7 +40,8 @@ export declare function cfrRateControlArgs(locked: CfrRateControlSource): string
|
|
|
39
40
|
* @param planDir — absolute path to the planDir produced by `plan()`.
|
|
40
41
|
* @param chunkPaths — ordered chunk outputs, length === `chunks.json` length.
|
|
41
42
|
* For mp4/mov each entry is a path to an encoded chunk file; for
|
|
42
|
-
* png-sequence each entry is a path to a directory of frames.
|
|
43
|
+
* png-sequence each entry is a path to a directory of frames. An entry
|
|
44
|
+
* may instead be the ordered parts of that chunk (see {@link AssembleChunkPaths}).
|
|
43
45
|
* @param audioPath — `<planDir>/<MIXED_AUDIO_FILENAME>` for mux'd formats. Pass `null`
|
|
44
46
|
* when the composition has no audio (or `assemble` is being called for a
|
|
45
47
|
* format whose audio is muxed elsewhere). `assemble` always normalizes
|
|
@@ -48,7 +50,7 @@ export declare function cfrRateControlArgs(locked: CfrRateControlSource): string
|
|
|
48
50
|
* @param outputPath — final on-disk output (file for mp4/mov; directory
|
|
49
51
|
* for png-sequence — created if missing).
|
|
50
52
|
*/
|
|
51
|
-
export declare function assemble(planDir: string,
|
|
53
|
+
export declare function assemble(planDir: string, chunkOutputs: AssembleChunkPaths, audioPath: string | null, outputPath: string, options?: {
|
|
52
54
|
logger?: ProducerLogger;
|
|
53
55
|
abortSignal?: AbortSignal;
|
|
54
56
|
/**
|
|
@@ -15,6 +15,14 @@ interface AudioCut {
|
|
|
15
15
|
packetCount: number;
|
|
16
16
|
}
|
|
17
17
|
export declare function audioCutForChunk(grid: AacPacketGrid, chunks: readonly ChunkSliceJson[], index: number, fps: Fps): AudioCut | null;
|
|
18
|
+
/** Chunk `index` split around `range`, so `audioCutForChunk` treats `range` as a chunk. */
|
|
19
|
+
export declare function chunksWithSubRange(chunks: readonly ChunkSliceJson[], index: number, range: {
|
|
20
|
+
startFrame: number;
|
|
21
|
+
endFrame: number;
|
|
22
|
+
}): {
|
|
23
|
+
chunks: ChunkSliceJson[];
|
|
24
|
+
index: number;
|
|
25
|
+
};
|
|
18
26
|
export declare function chunkAudioSlicePath(index: number): string;
|
|
19
27
|
/** Which chunks need a sliced-audio file: `"all"`, one index, or `null` if it is not one. */
|
|
20
28
|
export declare function chunkAudioSliceTarget(path: string): "all" | number | null;
|
|
@@ -7,9 +7,10 @@ export declare const FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
|
7
7
|
export declare const PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
8
8
|
export declare const MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
9
9
|
export declare const CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
10
|
+
export declare const CHUNK_FRAME_RANGE_INVALID = "CHUNK_FRAME_RANGE_INVALID";
|
|
10
11
|
export declare const MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
11
12
|
export { INVALID_VIDEO_METADATA };
|
|
12
|
-
export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
|
|
13
|
+
export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof CHUNK_FRAME_RANGE_INVALID | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
|
|
13
14
|
/** A malformed planDir or a worker that does not match the plan's fingerprint; adapters key retries off `code`. */
|
|
14
15
|
export declare class RenderChunkValidationError extends Error {
|
|
15
16
|
readonly code: RenderChunkValidationCode;
|
|
@@ -53,6 +53,7 @@ export interface DistributedRenderCapabilities {
|
|
|
53
53
|
chunk: Readonly<PlanProtocolConsumerCapabilities>;
|
|
54
54
|
assembler: Readonly<PlanProtocolConsumerCapabilities>;
|
|
55
55
|
}>;
|
|
56
|
+
readonly chunkFrameRange?: boolean;
|
|
56
57
|
}
|
|
57
58
|
/** Serializable capability payload for fleet rollout and worker handshakes. */
|
|
58
59
|
export declare const DISTRIBUTED_RENDER_CAPABILITIES: Readonly<DistributedRenderCapabilities>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { assemble, type AssembleResult } from "./assemble.js";
|
|
2
|
-
import { type EffectiveChunkResult } from "./renderChunk.js";
|
|
1
|
+
import { assemble, type AssembleChunkPaths, type AssembleResult } from "./assemble.js";
|
|
2
|
+
import { type EffectiveChunkResult, type RenderChunkOptions } from "./renderChunk.js";
|
|
3
3
|
/**
|
|
4
4
|
* Direct v2 chunk-role entry point. Storage adapters may instead download the
|
|
5
5
|
* selected blobs themselves and call `materializePlanV2Target` + `renderChunk`.
|
|
6
6
|
*/
|
|
7
|
-
export declare function renderChunkV2(planV2Dir: string, chunkIndex: number, outputChunkPath: string): Promise<EffectiveChunkResult>;
|
|
7
|
+
export declare function renderChunkV2(planV2Dir: string, chunkIndex: number, outputChunkPath: string, options?: RenderChunkOptions): Promise<EffectiveChunkResult>;
|
|
8
8
|
/**
|
|
9
9
|
* Direct v2 assembler-role entry point. Audio is resolved exclusively from
|
|
10
10
|
* the assembler dependency set and never materialized by chunk workers.
|
|
11
11
|
*/
|
|
12
|
-
export declare function assembleV2(planV2Dir: string, chunkPaths:
|
|
12
|
+
export declare function assembleV2(planV2Dir: string, chunkPaths: AssembleChunkPaths, outputPath: string, options?: Parameters<typeof assemble>[4]): Promise<AssembleResult>;
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
* In-process renders (`executeRenderJob`) leave them off.
|
|
35
35
|
*/
|
|
36
36
|
import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
|
|
37
|
-
import { type LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
37
|
+
import { type ChunkSliceJson, type LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
38
38
|
import { type PlanVideosJson } from "./shared.js";
|
|
39
|
-
export { CHUNK_INDEX_OUT_OF_RANGE, createChunkVideoFrameInjectorFactory, extractDeferredVideoFramesForChunk, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, rebuildExtractedFramesFromPlanDir, RenderChunkValidationError, } from "./chunkFrames.js";
|
|
39
|
+
export { CHUNK_FRAME_RANGE_INVALID, CHUNK_INDEX_OUT_OF_RANGE, createChunkVideoFrameInjectorFactory, extractDeferredVideoFramesForChunk, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, rebuildExtractedFramesFromPlanDir, RenderChunkValidationError, } from "./chunkFrames.js";
|
|
40
40
|
/** Validate the shared video contract before any v1 chunk can inject frames. */
|
|
41
41
|
export declare function validatePlanVideosForChunk(value: unknown): PlanVideosJson;
|
|
42
42
|
/**
|
|
@@ -51,6 +51,7 @@ export interface ChunkResult {
|
|
|
51
51
|
/** `"file"` for mp4/mov; `"frame-dir"` for png-sequence. */
|
|
52
52
|
outputKind: "file" | "frame-dir";
|
|
53
53
|
framesEncoded: number;
|
|
54
|
+
frameRange?: ChunkFrameRange;
|
|
54
55
|
sha256: string;
|
|
55
56
|
durationMs: number;
|
|
56
57
|
/**
|
|
@@ -92,7 +93,26 @@ export interface ChunkResult {
|
|
|
92
93
|
/** Result returned by the built-in renderer, which always observes its mode. */
|
|
93
94
|
export interface EffectiveChunkResult extends ChunkResult {
|
|
94
95
|
captureMode: CaptureMode;
|
|
96
|
+
frameRange: ChunkFrameRange;
|
|
95
97
|
}
|
|
98
|
+
/** Absolute composition frame indices, end exclusive, like a chunk's slice. */
|
|
99
|
+
export interface ChunkFrameRange {
|
|
100
|
+
startFrame: number;
|
|
101
|
+
endFrame: number;
|
|
102
|
+
}
|
|
103
|
+
export interface RenderChunkOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Render only these frames of the chunk, e.g. to retry a slow chunk as
|
|
106
|
+
* parts rendered in parallel. Must be a non-empty range inside the chunk's
|
|
107
|
+
* slice. Each part is a self-contained output (its own IDR keyframe, frame
|
|
108
|
+
* names from zero, the audio packets it owns); pass the parts of a chunk,
|
|
109
|
+
* in order, as one `chunkPaths` entry to `assemble`. Omitted renders the
|
|
110
|
+
* whole slice.
|
|
111
|
+
*/
|
|
112
|
+
frameRange?: ChunkFrameRange;
|
|
113
|
+
}
|
|
114
|
+
/** The frames a chunk renders: its slice, or a validated sub-range of it. */
|
|
115
|
+
export declare function resolveChunkFrameRange(slice: ChunkSliceJson, chunkIndex: number, frameRange: ChunkFrameRange | undefined): ChunkFrameRange;
|
|
96
116
|
/** Compatibility-safe adapter seam for built-in or injected chunk renderers. */
|
|
97
117
|
export type ChunkRenderer = (planDir: string, chunkIndex: number, outputChunkPath: string) => Promise<ChunkResult>;
|
|
98
118
|
interface DistributedCaptureSessionDependencies {
|
|
@@ -159,4 +179,4 @@ export declare function resolveLockedVp9CpuUsed(lockedEncoder: Pick<LockedRender
|
|
|
159
179
|
* outputs — the caller picks the right shape based on `meta/encoder.json`.
|
|
160
180
|
* `renderChunk` enforces the same choice via `outputKind` on the result.
|
|
161
181
|
*/
|
|
162
|
-
export declare function renderChunk(planDir: string, chunkIndex: number, outputChunkPath: string): Promise<EffectiveChunkResult>;
|
|
182
|
+
export declare function renderChunk(planDir: string, chunkIndex: number, outputChunkPath: string, options?: RenderChunkOptions): Promise<EffectiveChunkResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.180",
|
|
4
4
|
"description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"postcss": "^8.4.0",
|
|
36
36
|
"puppeteer": "^25.2.1",
|
|
37
37
|
"puppeteer-core": "^25.2.1",
|
|
38
|
-
"@hyperframes/
|
|
39
|
-
"@hyperframes/
|
|
40
|
-
"@hyperframes/
|
|
41
|
-
"@hyperframes/
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/engine": "0.8.180",
|
|
39
|
+
"@hyperframes/lint": "0.8.180",
|
|
40
|
+
"@hyperframes/parsers": "0.8.180",
|
|
41
|
+
"@hyperframes/core": "0.8.180",
|
|
42
|
+
"@hyperframes/studio-server": "0.8.180"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.0.10",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.11",
|
|
51
|
-
"@hyperframes/aws-lambda": "0.8.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.8.180"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|