@hyperframes/engine 0.8.185 → 0.8.186
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/index.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export { createFileServer, type FileServerOptions, type FileServerHandle, } from
|
|
|
58
58
|
export { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes/core";
|
|
59
59
|
export { frameFileExtension } from "./services/frameCapture.js";
|
|
60
60
|
export type { MotionBlurOptions, MotionBlurBlendSpace } from "./services/motionBlur.js";
|
|
61
|
+
export type { PageMotionBlurOff } from "./types.js";
|
|
61
62
|
export { assertSwiftShader, readWebGlVendorInfo, SwiftShaderAssertionError, BROWSER_GPU_NOT_SOFTWARE, } from "./utils/assertSwiftShader.js";
|
|
62
63
|
export { readWebGlVendorInfoFromCanvas } from "./utils/readWebGlVendorInfoFromCanvas.js";
|
|
63
64
|
export { extractMediaMetadata, extractVideoMetadata, extractFinalVideoFrameTimestamp, extractAudioMetadata, probeMediaProfile, analyzeKeyframeIntervals, type VideoMetadata, type AudioMetadata, type MediaProbeProfile, type KeyframeAnalysis, } from "./utils/ffprobe.js";
|
|
@@ -13,6 +13,7 @@ import { COMPOSITION_SOURCE_URL, quantizeSeekTime, quantizeTimeToFrame, fpsToNum
|
|
|
13
13
|
import { DrawElementCaptureError } from "./drawElementCaptureError.js";
|
|
14
14
|
import { encodePng } from "../utils/alphaBlit.js";
|
|
15
15
|
import { DEFAULT_SAMPLES_PER_FRAME, MotionBlurAccumulator, SPATIAL_TWEEN_PROPERTIES, adaptiveSampleCount, motionBlurProbeTimes, motionBlurSampleTimes, motionBlurWindowIsStatic, probeDiffMagnitude, resolveMotionBlurPlan, } from "./motionBlur.js";
|
|
16
|
+
import { describePageMotionBlur, describePageMotionBlurAtEnd, installPageMotionBlur, } from "./pageMotionBlur.js";
|
|
16
17
|
// ── Extracted modules ───────────────────────────────────────────────────────
|
|
17
18
|
import { acquireBrowser, releaseBrowser, forceReleaseBrowser, buildChromeArgs, resolveBrowserGpuMode, captureWithoutWaylandDisplay, resolveHeadlessShellPath, compositionRequiresWebGpu, assertWebGpuAdapterAvailable, usesSoftwareWebGpu, } from "./browserManager.js";
|
|
18
19
|
import { beginFrameCapture, ensureRenderFrameSiblings, getCdpSession, pageContentExceedsCaptureHeight, pageScreenshotCapture, initTransparentBackground, shouldDefaultCaptureBeyondViewport, } from "./screenshotService.js";
|
|
@@ -946,6 +947,7 @@ async function constructCaptureSession(input) {
|
|
|
946
947
|
}
|
|
947
948
|
}, variablesJson);
|
|
948
949
|
}
|
|
950
|
+
await installPageMotionBlur(page, options);
|
|
949
951
|
const browserVersion = await browser.version();
|
|
950
952
|
const sessionOptions = resolveCaptureSessionOptions(options, browserVersion);
|
|
951
953
|
const expectedMajor = config?.expectedChromiumMajor;
|
|
@@ -2462,6 +2464,7 @@ export async function createStaticVerificationPage(session) {
|
|
|
2462
2464
|
JSON.parse(json);
|
|
2463
2465
|
}, variablesJson);
|
|
2464
2466
|
}
|
|
2467
|
+
await installPageMotionBlur(page, session.options);
|
|
2465
2468
|
await page.setViewport({
|
|
2466
2469
|
width: session.options.width,
|
|
2467
2470
|
height: session.options.height,
|
|
@@ -3192,6 +3195,11 @@ async function captureFrameCore(session, frameIndex, time) {
|
|
|
3192
3195
|
session.capturePerf.screenshotMs += screenshotMs;
|
|
3193
3196
|
session.capturePerf.totalMs += captureTimeMs;
|
|
3194
3197
|
session.capturePerf.frameMs.push(captureTimeMs);
|
|
3198
|
+
if (session.capturePerf.frames === 1) {
|
|
3199
|
+
const blur = await describePageMotionBlur(session.page).catch(() => null);
|
|
3200
|
+
if (blur)
|
|
3201
|
+
console.log(blur);
|
|
3202
|
+
}
|
|
3195
3203
|
// Retain this freshly-captured buffer so the following static frames can reuse it.
|
|
3196
3204
|
if (session.staticFrames) {
|
|
3197
3205
|
session.lastFrameBuffer = buffer;
|
|
@@ -3527,6 +3535,11 @@ export async function closeCaptureSession(session) {
|
|
|
3527
3535
|
// point up to `intervalMs` earlier. Best effort; the page may be gone.
|
|
3528
3536
|
await session.chromeMemory.sampleOnce();
|
|
3529
3537
|
}
|
|
3538
|
+
if (session.page && !session.pageReleased) {
|
|
3539
|
+
const blur = await describePageMotionBlurAtEnd(session.page);
|
|
3540
|
+
if (blur)
|
|
3541
|
+
console.log(blur);
|
|
3542
|
+
}
|
|
3530
3543
|
// Realized static-dedup telemetry: how much the cache actually helped this
|
|
3531
3544
|
// render (vs the prediction logged at arm time). Both capture paths
|
|
3532
3545
|
// (sequential orchestrator + parallel workers) close their session here, so
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tells the page's capture motion blur (`data-motion-blur="on"`) to stand down, before any of
|
|
3
|
+
* its scripts run: when the engine blurs (it would average frames that are already blurred) or
|
|
4
|
+
* when the render must match a Studio preview that could not blur (`pageMotionBlur: "off"`).
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from "puppeteer-core";
|
|
7
|
+
import type { CaptureOptions } from "../types.js";
|
|
8
|
+
export declare function installPageMotionBlur(page: Pick<Page, "evaluateOnNewDocument">, options: Pick<CaptureOptions, "motionBlur" | "pageMotionBlur" | "deviceScaleFactor">): Promise<void>;
|
|
9
|
+
/** One line saying what the page's blur did for this session; null when the page never asked for blur. */
|
|
10
|
+
export declare function describePageMotionBlur(page: Pick<Page, "evaluate">): Promise<string | null>;
|
|
11
|
+
/** The session's closing line, only when frames fell back to plain DOM: the first-frame line cannot know. */
|
|
12
|
+
export declare function describePageMotionBlurAtEnd(page: Pick<Page, "evaluate">, timeoutMs?: number): Promise<string | null>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tells the page's capture motion blur (`data-motion-blur="on"`) to stand down, before any of
|
|
3
|
+
* its scripts run: when the engine blurs (it would average frames that are already blurred) or
|
|
4
|
+
* when the render must match a Studio preview that could not blur (`pageMotionBlur: "off"`).
|
|
5
|
+
*/
|
|
6
|
+
import { PAGE_DEVICE_SCALE_GLOBAL, PAGE_MOTION_BLUR_OFF_GLOBAL, acceleratedCanvasTrackerSource, } from "@hyperframes/core";
|
|
7
|
+
function pageOffReason(options) {
|
|
8
|
+
if (options.motionBlur)
|
|
9
|
+
return "engine-motion-blur";
|
|
10
|
+
if (options.pageMotionBlur === "off")
|
|
11
|
+
return "preview-unsupported";
|
|
12
|
+
return options.pageMotionBlur ?? null;
|
|
13
|
+
}
|
|
14
|
+
export async function installPageMotionBlur(page, options) {
|
|
15
|
+
await page.evaluateOnNewDocument(acceleratedCanvasTrackerSource());
|
|
16
|
+
const scale = options.deviceScaleFactor ?? 1;
|
|
17
|
+
if (scale > 1) {
|
|
18
|
+
await page.evaluateOnNewDocument((key, v) => {
|
|
19
|
+
window[key] = v;
|
|
20
|
+
}, PAGE_DEVICE_SCALE_GLOBAL, scale);
|
|
21
|
+
}
|
|
22
|
+
const off = pageOffReason(options);
|
|
23
|
+
if (!off)
|
|
24
|
+
return;
|
|
25
|
+
await page.evaluateOnNewDocument((key, v) => {
|
|
26
|
+
window[key] = v;
|
|
27
|
+
}, PAGE_MOTION_BLUR_OFF_GLOBAL, off);
|
|
28
|
+
}
|
|
29
|
+
async function readPageMotionBlur(page) {
|
|
30
|
+
return page.evaluate(() => {
|
|
31
|
+
const api = Reflect.get(window, "__hfMotionBlur");
|
|
32
|
+
const report = api && typeof api === "object" ? Reflect.get(api, "report") : null;
|
|
33
|
+
const got = typeof report === "function" ? report() : null;
|
|
34
|
+
if (!got || typeof got !== "object")
|
|
35
|
+
return null;
|
|
36
|
+
return {
|
|
37
|
+
state: String(Reflect.get(got, "state")),
|
|
38
|
+
code: String(Reflect.get(got, "reasonCode")),
|
|
39
|
+
movers: Number(Reflect.get(got, "movers")),
|
|
40
|
+
failedFrames: Number(Reflect.get(got, "failedFrames")) || 0,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const formatPageMotionBlur = (r) => `[motion-blur] ${r.state} reasonCode=${r.code} movers=${r.movers} failedFrames=${r.failedFrames}`;
|
|
45
|
+
/** One line saying what the page's blur did for this session; null when the page never asked for blur. */
|
|
46
|
+
export async function describePageMotionBlur(page) {
|
|
47
|
+
const r = await readPageMotionBlur(page);
|
|
48
|
+
return r && formatPageMotionBlur(r);
|
|
49
|
+
}
|
|
50
|
+
/** The session's closing line, only when frames fell back to plain DOM: the first-frame line cannot know. */
|
|
51
|
+
export async function describePageMotionBlurAtEnd(page, timeoutMs = 1500) {
|
|
52
|
+
let timer;
|
|
53
|
+
const stuck = new Promise((resolve) => {
|
|
54
|
+
timer = setTimeout(() => resolve(null), timeoutMs);
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
const r = await Promise.race([readPageMotionBlur(page), stuck]);
|
|
58
|
+
return r && r.failedFrames > 0 ? `${formatPageMotionBlur(r)} (session end)` : null;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { AudioFailureDiagnostic } from "./services/audioMixer.types.js";
|
|
|
8
8
|
import type { Fps } from "@hyperframes/core";
|
|
9
9
|
import type { ChromeMemoryStats } from "./services/chromeMemorySampler.js";
|
|
10
10
|
import type { MotionBlurOptions } from "./services/motionBlur.js";
|
|
11
|
+
export type PageMotionBlurOff = "off" | "hdr-render" | "layered-composite" | "distributed-render";
|
|
11
12
|
/**
|
|
12
13
|
* Outcome of waiting for a sub-composition's GSAP timelines to register.
|
|
13
14
|
* Threaded string-typed through `CapturePerfSummary` / `RenderPerfSummary` /
|
|
@@ -151,6 +152,8 @@ export interface CaptureOptions {
|
|
|
151
152
|
* screenshot capture mode; see `services/motionBlur.ts`.
|
|
152
153
|
*/
|
|
153
154
|
motionBlur?: MotionBlurOptions;
|
|
155
|
+
/** Why the page's capture motion blur stands down: the preview could not blur, or the route cannot host it. */
|
|
156
|
+
pageMotionBlur?: PageMotionBlurOff;
|
|
154
157
|
deviceScaleFactor?: number;
|
|
155
158
|
/**
|
|
156
159
|
* Opt into Chrome's capture-beyond-viewport screenshot path. Leave undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/engine",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.186",
|
|
4
4
|
"description": "Seekable web page to video rendering engine (Puppeteer + FFmpeg)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"linkedom": "^0.18.12",
|
|
51
51
|
"puppeteer": "^25.8.0",
|
|
52
52
|
"puppeteer-core": "^25.8.0",
|
|
53
|
-
"@hyperframes/core": "^0.8.
|
|
54
|
-
"@hyperframes/parsers": "^0.8.
|
|
53
|
+
"@hyperframes/core": "^0.8.186",
|
|
54
|
+
"@hyperframes/parsers": "^0.8.186"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^25.0.10",
|