@hyperframes/producer 0.8.188 → 0.8.190-hdr.1
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.js +45 -21
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +62 -62
- package/dist/index.js +46 -25
- package/dist/public-server.js +9 -4
- package/dist/services/distributed/assemble.d.ts +1 -0
- package/dist/services/distributed/chunkFrames.d.ts +2 -2
- package/dist/services/distributed/renderChunk.d.ts +7 -1
- package/dist/services/render/frameCacheBudget.d.ts +3 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1188,6 +1188,14 @@ function materializeExtractedFramesForCompiledDir(extracted, compiledDir, option
|
|
|
1188
1188
|
}
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
|
+
// src/services/render/frameCacheBudget.ts
|
|
1192
|
+
function frameCacheOptions(cfg, workers) {
|
|
1193
|
+
return {
|
|
1194
|
+
frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
|
|
1195
|
+
frameDataUriCacheBytesLimitMb: Math.floor(cfg.frameDataUriCacheBytesLimitMb / (workers || 1))
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1191
1199
|
// src/services/render/cleanup.ts
|
|
1192
1200
|
import { freemem } from "node:os";
|
|
1193
1201
|
|
|
@@ -12515,10 +12523,7 @@ async function executeRenderPipeline(input) {
|
|
|
12515
12523
|
});
|
|
12516
12524
|
}
|
|
12517
12525
|
});
|
|
12518
|
-
const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup,
|
|
12519
|
-
frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
|
|
12520
|
-
frameDataUriCacheBytesLimitMb: cfg.frameDataUriCacheBytesLimitMb
|
|
12521
|
-
});
|
|
12526
|
+
const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup, frameCacheOptions(cfg, workerCount));
|
|
12522
12527
|
let captureCalibration;
|
|
12523
12528
|
const htmlInCanvasDetected = compiled.renderModeHints.reasons.some(
|
|
12524
12529
|
(r) => r.code === "htmlInCanvas"
|
|
@@ -14172,12 +14177,13 @@ var RenderChunkValidationError = class extends Error {
|
|
|
14172
14177
|
this.code = code;
|
|
14173
14178
|
}
|
|
14174
14179
|
};
|
|
14175
|
-
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
14176
|
-
if (!(frameLookup instanceof Promise))
|
|
14180
|
+
function createChunkVideoFrameInjectorFactory(frameLookup, options) {
|
|
14181
|
+
if (!(frameLookup instanceof Promise))
|
|
14182
|
+
return () => createVideoFrameInjector2(frameLookup, options);
|
|
14177
14183
|
return () => {
|
|
14178
14184
|
let hook;
|
|
14179
14185
|
return async (page, time, heldVideoTime) => {
|
|
14180
|
-
hook ??= createVideoFrameInjector2(await frameLookup);
|
|
14186
|
+
hook ??= createVideoFrameInjector2(await frameLookup, options);
|
|
14181
14187
|
await hook?.(page, time, heldVideoTime);
|
|
14182
14188
|
};
|
|
14183
14189
|
};
|
|
@@ -18591,7 +18597,8 @@ import {
|
|
|
18591
18597
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME4,
|
|
18592
18598
|
muxVideoWithAudio as muxVideoWithAudio2,
|
|
18593
18599
|
resolveConfig as resolveConfig6,
|
|
18594
|
-
runFfmpeg as runFfmpeg7
|
|
18600
|
+
runFfmpeg as runFfmpeg7,
|
|
18601
|
+
appendSoftwareEncoderThreadsArg
|
|
18595
18602
|
} from "@hyperframes/engine";
|
|
18596
18603
|
import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
|
|
18597
18604
|
function cfrRateControlArgs(locked) {
|
|
@@ -18705,10 +18712,9 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
18705
18712
|
function videoInputArgs(ctx, videoSources) {
|
|
18706
18713
|
return videoSources.length === 1 ? ["-i", videoSources[0]] : concatDemuxerInput(videoSources, ctx.workDir, ctx.fpsArg);
|
|
18707
18714
|
}
|
|
18708
|
-
|
|
18709
|
-
const
|
|
18710
|
-
|
|
18711
|
-
...videoInputArgs(ctx, chunkPaths),
|
|
18715
|
+
function cfrEncodeArgs(inputArgs, encoder, fpsArg) {
|
|
18716
|
+
const args = [
|
|
18717
|
+
...inputArgs,
|
|
18712
18718
|
"-c:v",
|
|
18713
18719
|
"libx264",
|
|
18714
18720
|
...cfrRateControlArgs(encoder),
|
|
@@ -18717,8 +18723,14 @@ async function reencodeCfr(ctx, chunkPaths, encoder) {
|
|
|
18717
18723
|
"-fps_mode",
|
|
18718
18724
|
"cfr",
|
|
18719
18725
|
"-r",
|
|
18720
|
-
|
|
18726
|
+
fpsArg
|
|
18721
18727
|
];
|
|
18728
|
+
appendSoftwareEncoderThreadsArg(args, "libx264");
|
|
18729
|
+
return args;
|
|
18730
|
+
}
|
|
18731
|
+
async function reencodeCfr(ctx, chunkPaths, encoder) {
|
|
18732
|
+
const cfrOutputPath = join40(ctx.workDir, `cfr.${ctx.format}`);
|
|
18733
|
+
const cfrArgs = cfrEncodeArgs(videoInputArgs(ctx, chunkPaths), encoder, ctx.fpsArg);
|
|
18722
18734
|
appendRenderProvenanceArgs2(cfrArgs, cfrOutputPath);
|
|
18723
18735
|
cfrArgs.push("-y", cfrOutputPath);
|
|
18724
18736
|
await ctx.timed(
|
|
@@ -18969,6 +18981,22 @@ async function createVerifiedDistributedCaptureSession(serverUrl, framesDir, cap
|
|
|
18969
18981
|
function isCaptureMode(value) {
|
|
18970
18982
|
return value === "beginframe" || value === "screenshot" || value === "drawelement";
|
|
18971
18983
|
}
|
|
18984
|
+
var MixedCaptureModesError = class extends Error {
|
|
18985
|
+
constructor(modes) {
|
|
18986
|
+
super(`[renderChunk] capture workers reported inconsistent modes: ${modes.join(",")}`);
|
|
18987
|
+
this.modes = modes;
|
|
18988
|
+
this.name = "MixedCaptureModesError";
|
|
18989
|
+
}
|
|
18990
|
+
};
|
|
18991
|
+
function observedChunkCaptureMode(perfs) {
|
|
18992
|
+
const modes = [...new Set(perfs.map((perf) => perf.captureMode))];
|
|
18993
|
+
if (modes.length > 1) throw new MixedCaptureModesError(modes);
|
|
18994
|
+
const [mode] = modes;
|
|
18995
|
+
if (!mode || !isCaptureMode(mode)) {
|
|
18996
|
+
throw new Error(`[renderChunk] capture workers reported an invalid mode: ${mode ?? "<none>"}`);
|
|
18997
|
+
}
|
|
18998
|
+
return mode;
|
|
18999
|
+
}
|
|
18972
19000
|
function shouldRetryChunkCaptureWithScreenshot(error) {
|
|
18973
19001
|
const failure = classifyCaptureFailure4(error);
|
|
18974
19002
|
if (failure.kind === "cancelled" || failure.kind === "memory_exhaustion") return false;
|
|
@@ -19266,7 +19294,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19266
19294
|
) : null;
|
|
19267
19295
|
videoFrameLookup?.catch(() => {
|
|
19268
19296
|
});
|
|
19269
|
-
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(
|
|
19297
|
+
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(
|
|
19298
|
+
videoFrameLookup,
|
|
19299
|
+
frameCacheOptions(cfg, chunkWorkerCount)
|
|
19300
|
+
);
|
|
19270
19301
|
const captureStarted = Date.now();
|
|
19271
19302
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
19272
19303
|
forceScreenshot: forceScreenshotForChunk,
|
|
@@ -19320,17 +19351,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19320
19351
|
},
|
|
19321
19352
|
frameRange: range
|
|
19322
19353
|
});
|
|
19323
|
-
|
|
19324
|
-
if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
|
|
19325
|
-
throw new Error(
|
|
19326
|
-
`[renderChunk] capture workers reported invalid or inconsistent modes: ${[...observedModes].join(",") || "<none>"}`
|
|
19327
|
-
);
|
|
19328
|
-
}
|
|
19329
|
-
const [observedMode] = observedModes;
|
|
19330
|
-
if (!observedMode || !isCaptureMode(observedMode)) {
|
|
19331
|
-
throw new Error("[renderChunk] capture completed without an observed mode");
|
|
19332
|
-
}
|
|
19333
|
-
return observedMode;
|
|
19354
|
+
return observedChunkCaptureMode(capturePerfs);
|
|
19334
19355
|
},
|
|
19335
19356
|
resetForScreenshotRetry: () => {
|
|
19336
19357
|
rmSync17(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
package/dist/public-server.js
CHANGED
|
@@ -1208,6 +1208,14 @@ function materializeExtractedFramesForCompiledDir(extracted, compiledDir, option
|
|
|
1208
1208
|
}
|
|
1209
1209
|
}
|
|
1210
1210
|
|
|
1211
|
+
// src/services/render/frameCacheBudget.ts
|
|
1212
|
+
function frameCacheOptions(cfg, workers) {
|
|
1213
|
+
return {
|
|
1214
|
+
frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
|
|
1215
|
+
frameDataUriCacheBytesLimitMb: Math.floor(cfg.frameDataUriCacheBytesLimitMb / (workers || 1))
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1211
1219
|
// src/services/render/cleanup.ts
|
|
1212
1220
|
import { freemem } from "node:os";
|
|
1213
1221
|
|
|
@@ -12498,10 +12506,7 @@ async function executeRenderPipeline(input) {
|
|
|
12498
12506
|
});
|
|
12499
12507
|
}
|
|
12500
12508
|
});
|
|
12501
|
-
const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup,
|
|
12502
|
-
frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
|
|
12503
|
-
frameDataUriCacheBytesLimitMb: cfg.frameDataUriCacheBytesLimitMb
|
|
12504
|
-
});
|
|
12509
|
+
const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup, frameCacheOptions(cfg, workerCount));
|
|
12505
12510
|
let captureCalibration;
|
|
12506
12511
|
const htmlInCanvasDetected = compiled.renderModeHints.reasons.some(
|
|
12507
12512
|
(r) => r.code === "htmlInCanvas"
|
|
@@ -68,6 +68,7 @@ export declare function assemble(planDir: string, chunkOutputs: AssembleChunkPat
|
|
|
68
68
|
*/
|
|
69
69
|
cfr?: boolean;
|
|
70
70
|
}): Promise<AssembleResult>;
|
|
71
|
+
export declare function cfrEncodeArgs(inputArgs: string[], encoder: CfrRateControlSource, fpsArg: string): string[];
|
|
71
72
|
/** Mp4 chunks carrying audio, or with it as a separate file, stitch under one index like S3's. */
|
|
72
73
|
export declare function chunksAreStitchedMp4(planDir: string, cfr: boolean): boolean;
|
|
73
74
|
export declare function stitchesSeparateAudio(planDir: string): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, type EngineConfig, type ExtractedFrames, type FrameLookupTable, type ShownBox } from "@hyperframes/engine";
|
|
1
|
+
import { type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, createVideoFrameInjector, type EngineConfig, type ExtractedFrames, type FrameLookupTable, type ShownBox } from "@hyperframes/engine";
|
|
2
2
|
import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
3
3
|
import type { PlanV2RemoteSources } from "./planV2.js";
|
|
4
4
|
import { type DistributedFormat, INVALID_VIDEO_METADATA, type PlanVideosJson } from "./shared.js";
|
|
@@ -17,7 +17,7 @@ export declare class RenderChunkValidationError extends Error {
|
|
|
17
17
|
constructor(code: RenderChunkValidationCode, message: string);
|
|
18
18
|
}
|
|
19
19
|
/** One injector per browser session: a hook reused on a fresh page can skip its first injection. */
|
|
20
|
-
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | Promise<FrameLookupTable> | null): () => BeforeCaptureHook | null;
|
|
20
|
+
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | Promise<FrameLookupTable> | null, options?: Parameters<typeof createVideoFrameInjector>[1]): () => BeforeCaptureHook | null;
|
|
21
21
|
/**
|
|
22
22
|
* Rebuilds `ExtractedFrames[]` from `<planDir>/video-frames/<videoId>/`. `framePaths` keys are 0-based
|
|
23
23
|
* frame indexes (what `getFrameAtTime` reads); any other key drops every video's first-paint frame.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* Every determinism toggle above is opt-in — only this primitive enables them.
|
|
34
34
|
* In-process renders (`executeRenderJob`) leave them off.
|
|
35
35
|
*/
|
|
36
|
-
import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
|
|
36
|
+
import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CapturePerfSummary, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
|
|
37
37
|
import { type ChunkSliceJson, type LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
38
38
|
import { type PlanVideosJson } from "./shared.js";
|
|
39
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";
|
|
@@ -136,6 +136,12 @@ export declare const distributedCaptureSessionDependencies: DistributedCaptureSe
|
|
|
136
136
|
* assertion, including fresh screenshot browsers created after a fallback.
|
|
137
137
|
*/
|
|
138
138
|
export declare function createVerifiedDistributedCaptureSession(serverUrl: string, framesDir: string, captureOptions: CaptureOptions, cfg: EngineConfig, dependencies?: DistributedCaptureSessionDependencies): Promise<CaptureSession>;
|
|
139
|
+
/** Workers of one chunk launched in different modes: one worker's BeginFrame check failed and it fell back alone. */
|
|
140
|
+
export declare class MixedCaptureModesError extends Error {
|
|
141
|
+
readonly modes: readonly string[];
|
|
142
|
+
constructor(modes: readonly string[]);
|
|
143
|
+
}
|
|
144
|
+
export declare function observedChunkCaptureMode(perfs: readonly Pick<CapturePerfSummary, "captureMode">[]): CaptureMode;
|
|
139
145
|
/**
|
|
140
146
|
* Only BeginFrame-specific failures are safe to retry in screenshot mode.
|
|
141
147
|
* Cancellation, memory exhaustion, and unrelated authoring/IO failures must
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { EngineConfig } from "@hyperframes/engine";
|
|
2
|
+
/** Cache options for one of `workers` injectors that share the configured budget on the one Node heap. */
|
|
3
|
+
export declare function frameCacheOptions(cfg: Pick<EngineConfig, "frameDataUriCacheLimit" | "frameDataUriCacheBytesLimitMb">, workers: number | undefined): Pick<EngineConfig, "frameDataUriCacheLimit" | "frameDataUriCacheBytesLimitMb">;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.190-hdr.1",
|
|
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/parsers": "0.8.
|
|
40
|
-
"@hyperframes/lint": "0.8.
|
|
41
|
-
"@hyperframes/
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/core": "0.8.190-hdr.1",
|
|
39
|
+
"@hyperframes/parsers": "0.8.190-hdr.1",
|
|
40
|
+
"@hyperframes/lint": "0.8.190-hdr.1",
|
|
41
|
+
"@hyperframes/studio-server": "0.8.190-hdr.1",
|
|
42
|
+
"@hyperframes/engine": "0.8.190-hdr.1"
|
|
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.190-hdr.1"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|