@hyperframes/engine 0.8.199 → 0.8.201
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/config.d.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/services/audioMixer.js +3 -9
- package/dist/services/browserManager.d.ts +3 -3
- package/dist/services/browserManager.js +37 -17
- package/dist/services/chunkEncoder.js +6 -12
- package/dist/services/frameCapture.d.ts +11 -1
- package/dist/services/frameCapture.js +186 -62
- package/dist/services/parallelCoordinator.js +12 -10
- package/dist/services/riskFrames.d.ts +30 -0
- package/dist/services/riskFrames.js +101 -0
- package/dist/services/screenshotService.d.ts +7 -0
- package/dist/services/screenshotService.js +4 -0
- package/dist/services/systemMemory.d.ts +2 -0
- package/dist/services/systemMemory.js +38 -1
- package/dist/services/threeDProjection.d.ts +14 -0
- package/dist/services/threeDProjection.js +117 -41
- package/dist/services/videoFrameExtractor.js +9 -5
- package/dist/services/videoFrameInjector.js +2 -1
- package/dist/types.d.ts +6 -2
- package/dist/utils/psnrFilterAvailability.d.ts +1 -0
- package/dist/utils/psnrFilterAvailability.js +17 -10
- package/dist/utils/runFfmpeg.d.ts +2 -3
- package/dist/utils/runFfmpeg.js +13 -16
- package/dist/utils/urlDownloader.js +21 -7
- package/package.json +3 -3
package/dist/config.d.ts
CHANGED
|
@@ -163,9 +163,9 @@ export interface EngineConfig {
|
|
|
163
163
|
* renders stream. Env: PRODUCER_STREAMING_ENCODE_DURATION_CAP_ENABLED.
|
|
164
164
|
*/
|
|
165
165
|
streamingEncodeDurationCapEnabled: boolean;
|
|
166
|
-
/**
|
|
166
|
+
/** How long a disk-frame encode may go without a new frame before it is stopped (ms). Default: 600_000 */
|
|
167
167
|
ffmpegEncodeTimeout: number;
|
|
168
|
-
/** FFmpeg
|
|
168
|
+
/** How long any other FFmpeg step (audio, mux, extraction) may go without progress (ms). Default: 300_000 */
|
|
169
169
|
ffmpegProcessTimeout: number;
|
|
170
170
|
/**
|
|
171
171
|
* Inactivity timeout for FFmpeg streaming encode (ms). The timer resets on
|
package/dist/index.d.ts
CHANGED
|
@@ -42,11 +42,12 @@ export { createCaptureSession, initializeSession, closeCaptureSession, captureFr
|
|
|
42
42
|
export { CaptureFailure, classifyCaptureFailure, isFatalCaptureFailure, type CaptureFailureKind, type CaptureWorkerDiagnostic, } from "./services/captureFailure.js";
|
|
43
43
|
export { createChromeMemorySampler, type ChromeMemoryStats, } from "./services/chromeMemorySampler.js";
|
|
44
44
|
export { installMediaRenderIdBridge } from "./services/mediaRenderIdBridge.js";
|
|
45
|
-
export { beginFrameCapture, pageScreenshotCapture, getCdpSession, injectVideoFramesBatch, syncVideoFrameVisibility, cdpSessionCache, probeBeginFrameLiveness, initTransparentBackground, captureAlphaPng, applyDomLayerMask, removeDomLayerMask, DOM_LAYER_MASK_STYLE_ID, type BeginFrameResult, } from "./services/screenshotService.js";
|
|
45
|
+
export { beginFrameCapture, pageScreenshotCapture, getCdpSession, injectVideoFramesBatch, syncVideoFrameVisibility, cdpSessionCache, probeBeginFrameLiveness, initTransparentBackground, capturesAlpha, captureAlphaPng, applyDomLayerMask, removeDomLayerMask, DOM_LAYER_MASK_STYLE_ID, type BeginFrameResult, } from "./services/screenshotService.js";
|
|
46
46
|
export { buildEncoderArgs, encodeFramesFromDir, buildConcatArgs, concatVideoFiles, encodeFramesChunkedConcat, muxVideoWithAudio, applyFaststart, packageHls, detectGpuEncoder, ENCODER_PRESETS, getEncoderPreset, HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, HLS_AUDIO_PLAYLIST, type GpuEncoder, type PackageHlsOptions, } from "./services/chunkEncoder.js";
|
|
47
47
|
export type { EncoderOptions, EncodeResult, MuxResult } from "./services/chunkEncoder.types.js";
|
|
48
48
|
export { spawnStreamingEncoder, createFrameReorderBuffer, type StreamingEncoder, type StreamingEncoderOptions, type StreamingEncoderResult, type FrameReorderBuffer, } from "./services/streamingEncoder.js";
|
|
49
49
|
export { parseVideoElements, parseImageElements, extractVideoFramesRange, extractAllVideoFrames, isVideoPastTimelineEnd, resolveTimelineExtractionWindow, resolveVideoExtractionWindow, rebaseVideoToWindow, resolveFinalFrameExtractionWindow, resolveFrameFormat, resolveVideoExtractionDuration, resolvePlayableVideoDuration, extractionFrameCountForDuration, resolveProjectRelativeSrc, getFrameAtTime, isVideoHiddenBeforeStreamStart, createFrameLookupTable, FrameLookupTable, analyzeClipMediaFit, classifyVideoExtractionError, isVideoSourceExtractionError, runVideoExtractionWithRetry, safeVideoExtractionSourceIdentity, VideoSourceExtractionError, type VideoElement, type ImageElement, type ExtractedFrames, type DeferredFrameRange, type FrameRange, type ExtractionOptions, type ExtractionResult, type ExtractionPhaseBreakdown, type TimelineExtractionWindow, type VideoExtractionFailure, type VideoExtractionFailureKind, type VideoExtractionFailureGroupDetails, type VideoExtractionFailureRetry, type VideoExtractionFailureStatusClass, type SafeVideoExtractionSourceIdentity, type VideoFrameFormat, VIDEO_FRAME_FORMATS, isVideoFrameFormat, EXTRACT_CACHE_MIN_AGE_MS, } from "./services/videoFrameExtractor.js";
|
|
50
|
+
export { isSelfOrAncestorHidden } from "./services/mediaHidden.js";
|
|
50
51
|
export { directorySizeBytes, gcExtractionCache } from "./services/extractionCache.js";
|
|
51
52
|
export { resolveReferencedStart, resolveReferencedDuration, type RefResolverEl, type RefResolverDoc, } from "./services/referenceResolver.js";
|
|
52
53
|
export { createVideoFrameInjector } from "./services/videoFrameInjector.js";
|
package/dist/index.js
CHANGED
|
@@ -45,12 +45,13 @@ export { CaptureFailure, classifyCaptureFailure, isFatalCaptureFailure, } from "
|
|
|
45
45
|
export { createChromeMemorySampler, } from "./services/chromeMemorySampler.js";
|
|
46
46
|
// ── Screenshot (BeginFrame) ─────────────────────────────────────────────────────
|
|
47
47
|
export { installMediaRenderIdBridge } from "./services/mediaRenderIdBridge.js";
|
|
48
|
-
export { beginFrameCapture, pageScreenshotCapture, getCdpSession, injectVideoFramesBatch, syncVideoFrameVisibility, cdpSessionCache, probeBeginFrameLiveness, initTransparentBackground, captureAlphaPng, applyDomLayerMask, removeDomLayerMask, DOM_LAYER_MASK_STYLE_ID, } from "./services/screenshotService.js";
|
|
48
|
+
export { beginFrameCapture, pageScreenshotCapture, getCdpSession, injectVideoFramesBatch, syncVideoFrameVisibility, cdpSessionCache, probeBeginFrameLiveness, initTransparentBackground, capturesAlpha, captureAlphaPng, applyDomLayerMask, removeDomLayerMask, DOM_LAYER_MASK_STYLE_ID, } from "./services/screenshotService.js";
|
|
49
49
|
// ── Encoding ───────────────────────────────────────────────────────────────────
|
|
50
50
|
export { buildEncoderArgs, encodeFramesFromDir, buildConcatArgs, concatVideoFiles, encodeFramesChunkedConcat, muxVideoWithAudio, applyFaststart, packageHls, detectGpuEncoder, ENCODER_PRESETS, getEncoderPreset, HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, HLS_AUDIO_PLAYLIST, } from "./services/chunkEncoder.js";
|
|
51
51
|
export { spawnStreamingEncoder, createFrameReorderBuffer, } from "./services/streamingEncoder.js";
|
|
52
52
|
// ── Media processing ───────────────────────────────────────────────────────────
|
|
53
53
|
export { parseVideoElements, parseImageElements, extractVideoFramesRange, extractAllVideoFrames, isVideoPastTimelineEnd, resolveTimelineExtractionWindow, resolveVideoExtractionWindow, rebaseVideoToWindow, resolveFinalFrameExtractionWindow, resolveFrameFormat, resolveVideoExtractionDuration, resolvePlayableVideoDuration, extractionFrameCountForDuration, resolveProjectRelativeSrc, getFrameAtTime, isVideoHiddenBeforeStreamStart, createFrameLookupTable, FrameLookupTable, analyzeClipMediaFit, classifyVideoExtractionError, isVideoSourceExtractionError, runVideoExtractionWithRetry, safeVideoExtractionSourceIdentity, VideoSourceExtractionError, VIDEO_FRAME_FORMATS, isVideoFrameFormat, EXTRACT_CACHE_MIN_AGE_MS, } from "./services/videoFrameExtractor.js";
|
|
54
|
+
export { isSelfOrAncestorHidden } from "./services/mediaHidden.js";
|
|
54
55
|
export { directorySizeBytes, gcExtractionCache } from "./services/extractionCache.js";
|
|
55
56
|
export { resolveReferencedStart, resolveReferencedDuration, } from "./services/referenceResolver.js";
|
|
56
57
|
export { createVideoFrameInjector } from "./services/videoFrameInjector.js";
|
|
@@ -339,7 +339,7 @@ function ffmpegFailure(stage, result, timeoutMs, elementId) {
|
|
|
339
339
|
reason = "cancelled";
|
|
340
340
|
owner = "user";
|
|
341
341
|
}
|
|
342
|
-
else if (result.terminationReason === "
|
|
342
|
+
else if (result.terminationReason === "inactivity") {
|
|
343
343
|
reason = "ffmpeg_timeout";
|
|
344
344
|
retryable = true;
|
|
345
345
|
}
|
|
@@ -692,14 +692,8 @@ async function generateSilence(outputPath, duration, signal, config) {
|
|
|
692
692
|
failure,
|
|
693
693
|
};
|
|
694
694
|
}
|
|
695
|
-
/** A mix graph's work grows with the film length (about 11 ms per film second at 96 inputs); so does its deadline. */
|
|
696
|
-
const MIX_TIMEOUT_MS_PER_FILM_SECOND = 100;
|
|
697
|
-
function mixProcessTimeout(config, totalDuration) {
|
|
698
|
-
const configured = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
699
|
-
return Math.max(configured, Math.ceil(totalDuration * MIX_TIMEOUT_MS_PER_FILM_SECOND));
|
|
700
|
-
}
|
|
701
695
|
async function mixAudioTracksFlat(tracks, outputPath, totalDuration, signal, config) {
|
|
702
|
-
const ffmpegProcessTimeout =
|
|
696
|
+
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
703
697
|
const masterOutputGain = config?.audioGain ?? DEFAULT_CONFIG.audioGain;
|
|
704
698
|
if (tracks.length === 0) {
|
|
705
699
|
const result = await generateSilence(outputPath, totalDuration, signal, config);
|
|
@@ -876,7 +870,7 @@ function groupNormalizeOptionUnsupported(stderr) {
|
|
|
876
870
|
* only runs when this ffmpeg build's `amix` rejects the option.
|
|
877
871
|
*/
|
|
878
872
|
async function mixGroupMembersFlat(memberTracks, outputPath, totalDuration, signal, config) {
|
|
879
|
-
const ffmpegProcessTimeout =
|
|
873
|
+
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
880
874
|
const outputDir = dirname(outputPath);
|
|
881
875
|
if (!existsSync(outputDir))
|
|
882
876
|
mkdirSync(outputDir, { recursive: true });
|
|
@@ -124,13 +124,13 @@ export declare function usesSoftwareWebGpu(requiresWebGpu: boolean | undefined,
|
|
|
124
124
|
export declare function buildChromeArgs(options: BuildChromeArgsOptions, config?: GpuConfig): string[];
|
|
125
125
|
/** Does the root, or a mounted root (data-hf-inner-root once bundled), declare `data-requires-webgpu`? */
|
|
126
126
|
export declare function compositionRequiresWebGpu(html: string): boolean;
|
|
127
|
-
/** No
|
|
127
|
+
/** No usable WebGPU device on this launch; distinct from a browser or navigation failure. */
|
|
128
128
|
export declare class WebGpuUnavailableError extends Error {
|
|
129
129
|
constructor();
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
|
-
* Confirms the already-navigated page can
|
|
133
|
-
* software WebGPU ({@link usesSoftwareWebGpu}); no-op otherwise.
|
|
132
|
+
* Confirms the already-navigated page can create a device on a hardware WebGPU adapter, or any adapter when
|
|
133
|
+
* the launch runs software WebGPU ({@link usesSoftwareWebGpu}); no-op otherwise.
|
|
134
134
|
* `page` must be on a secure-context origin, or navigator.gpu always reads absent.
|
|
135
135
|
*/
|
|
136
136
|
export declare function assertWebGpuAdapterAvailable(page: Page, requiresWebGpu: boolean, softwareWebGpu?: boolean): Promise<void>;
|
|
@@ -666,7 +666,7 @@ async function launchBrowser(fingerprint) {
|
|
|
666
666
|
});
|
|
667
667
|
const browserVersion = await browser.version().catch(() => "unknown");
|
|
668
668
|
const gpuFlags = fingerprint.args.filter((a) => a.startsWith("--use-gl=") || a.startsWith("--use-angle="));
|
|
669
|
-
console.
|
|
669
|
+
console.error(`[BrowserManager] Browser launched (${browserVersion}, ${captureMode}, gl=${gpuFlags.join(" ") || "default"}, headlessShell=${!!fingerprint.executablePath}, platform=${process.platform})`);
|
|
670
670
|
if (captureMode === "beginframe") {
|
|
671
671
|
const probe = await probeBeginFrameSupport(browser).catch((error) => ({
|
|
672
672
|
supported: true,
|
|
@@ -909,43 +909,63 @@ export function compositionRequiresWebGpu(html) {
|
|
|
909
909
|
}
|
|
910
910
|
return false;
|
|
911
911
|
}
|
|
912
|
-
/** No
|
|
912
|
+
/** No usable WebGPU device on this launch; distinct from a browser or navigation failure. */
|
|
913
913
|
export class WebGpuUnavailableError extends Error {
|
|
914
914
|
constructor() {
|
|
915
|
-
super("This composition declares data-requires-webgpu, but
|
|
916
|
-
"
|
|
915
|
+
super("This composition declares data-requires-webgpu, but a usable WebGPU device could not be " +
|
|
916
|
+
"created on this browser launch. Run on a host with a GPU, set " +
|
|
917
917
|
"PRODUCER_ALLOW_SOFTWARE_WEBGPU=true to render it on SwiftShader (slow), or remove " +
|
|
918
918
|
"data-requires-webgpu.");
|
|
919
919
|
this.name = "WebGpuUnavailableError";
|
|
920
920
|
}
|
|
921
921
|
}
|
|
922
|
-
const
|
|
922
|
+
const WEBGPU_DEVICE_PROBE_TIMEOUT_MS = 10_000;
|
|
923
923
|
/**
|
|
924
|
-
* Confirms the already-navigated page can
|
|
925
|
-
* software WebGPU ({@link usesSoftwareWebGpu}); no-op otherwise.
|
|
924
|
+
* Confirms the already-navigated page can create a device on a hardware WebGPU adapter, or any adapter when
|
|
925
|
+
* the launch runs software WebGPU ({@link usesSoftwareWebGpu}); no-op otherwise.
|
|
926
926
|
* `page` must be on a secure-context origin, or navigator.gpu always reads absent.
|
|
927
927
|
*/
|
|
928
928
|
export async function assertWebGpuAdapterAvailable(page, requiresWebGpu, softwareWebGpu = false) {
|
|
929
929
|
if (!requiresWebGpu)
|
|
930
930
|
return;
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
const hasUsableAdapter = await page.evaluate(async (timeoutMs, acceptFallback) => {
|
|
931
|
+
// An adapter can exist even when missing driver libraries make device creation fail.
|
|
932
|
+
// Both requests can hang; share one deadline and release late devices as well.
|
|
933
|
+
const hasUsableDevice = await page.evaluate(async (timeoutMs, acceptFallback) => {
|
|
935
934
|
if (typeof navigator === "undefined" || !navigator.gpu)
|
|
936
935
|
return false;
|
|
936
|
+
let expired = false;
|
|
937
|
+
let timer;
|
|
937
938
|
try {
|
|
938
|
-
const
|
|
939
|
-
navigator.gpu.requestAdapter()
|
|
940
|
-
|
|
939
|
+
const probe = async () => {
|
|
940
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
941
|
+
if (!adapter || expired || (!acceptFallback && adapter.info.isFallbackAdapter))
|
|
942
|
+
return false;
|
|
943
|
+
const device = await adapter.requestDevice();
|
|
944
|
+
try {
|
|
945
|
+
return !expired;
|
|
946
|
+
}
|
|
947
|
+
finally {
|
|
948
|
+
device.destroy();
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
return await Promise.race([
|
|
952
|
+
probe(),
|
|
953
|
+
new Promise((resolve) => {
|
|
954
|
+
timer = setTimeout(() => {
|
|
955
|
+
expired = true;
|
|
956
|
+
resolve(false);
|
|
957
|
+
}, timeoutMs);
|
|
958
|
+
}),
|
|
941
959
|
]);
|
|
942
|
-
return !!adapter && (acceptFallback || !adapter.info.isFallbackAdapter);
|
|
943
960
|
}
|
|
944
961
|
catch {
|
|
945
962
|
return false;
|
|
946
963
|
}
|
|
947
|
-
|
|
948
|
-
|
|
964
|
+
finally {
|
|
965
|
+
clearTimeout(timer);
|
|
966
|
+
}
|
|
967
|
+
}, WEBGPU_DEVICE_PROBE_TIMEOUT_MS, softwareWebGpu);
|
|
968
|
+
if (!hasUsableDevice)
|
|
949
969
|
throw new WebGpuUnavailableError();
|
|
950
970
|
}
|
|
951
971
|
function getBrowserGpuArgs(mode, platform) {
|
|
@@ -27,15 +27,9 @@ export const ENCODER_PRESETS = {
|
|
|
27
27
|
function appendEncodeTimeoutMessage(error, timedOut, timeoutMs) {
|
|
28
28
|
if (!timedOut)
|
|
29
29
|
return error;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// PRODUCER_ENABLE_CHUNKED_ENCODE themselves. Name both here instead of
|
|
34
|
-
// just stating what happened.
|
|
35
|
-
return (`${error}\nFFmpeg killed after exceeding ffmpegEncodeTimeout (${timeoutMs} ms). ` +
|
|
36
|
-
"Long or high-frame-count renders may need more time: set FFMPEG_ENCODE_TIMEOUT_MS " +
|
|
37
|
-
"to a higher value (ms), or set PRODUCER_ENABLE_CHUNKED_ENCODE=true to encode in " +
|
|
38
|
-
"smaller chunks instead of one long-running ffmpeg process.");
|
|
30
|
+
return (`${error}\nFFmpeg killed after writing no new frame for ffmpegEncodeTimeout (${timeoutMs} ms). ` +
|
|
31
|
+
"An encode that is still writing frames is never stopped; if this encoder legitimately " +
|
|
32
|
+
"pauses longer between frames, raise FFMPEG_ENCODE_TIMEOUT_MS (ms).");
|
|
39
33
|
}
|
|
40
34
|
function isAacSidecar(audioPath) {
|
|
41
35
|
return extname(audioPath).toLowerCase() === ".aac";
|
|
@@ -430,7 +424,7 @@ export async function encodeFramesFromDir(framesDir, framePattern, outputPath, o
|
|
|
430
424
|
framesEncoded: 0,
|
|
431
425
|
fileSize: 0,
|
|
432
426
|
error: appendEncodeTimeoutMessage(frameReadFailure(result.stderr, framePattern, 0, framesRead, frameCount) +
|
|
433
|
-
formatFfmpegError(result.exitCode, result.stderr), result.terminationReason === "
|
|
427
|
+
formatFfmpegError(result.exitCode, result.stderr), result.terminationReason === "inactivity", encodeTimeout),
|
|
434
428
|
failureReason: isExternalFfmpegInterruption(result) ? "external_interruption" : undefined,
|
|
435
429
|
};
|
|
436
430
|
}
|
|
@@ -492,7 +486,7 @@ export async function concatVideoFiles(inputPaths, outputPath, signal, config) {
|
|
|
492
486
|
return { success: true };
|
|
493
487
|
return {
|
|
494
488
|
success: false,
|
|
495
|
-
error: appendEncodeTimeoutMessage(`Chunk concat failed: ${result.stderr.slice(-400)}`, result.terminationReason === "
|
|
489
|
+
error: appendEncodeTimeoutMessage(`Chunk concat failed: ${result.stderr.slice(-400)}`, result.terminationReason === "inactivity", encodeTimeout),
|
|
496
490
|
externalInterruption: isExternalFfmpegInterruption(result),
|
|
497
491
|
};
|
|
498
492
|
}
|
|
@@ -566,7 +560,7 @@ export async function encodeFramesChunkedConcat(framesDir, framePattern, outputP
|
|
|
566
560
|
success: processResult.success,
|
|
567
561
|
error: processResult.success
|
|
568
562
|
? undefined
|
|
569
|
-
: appendEncodeTimeoutMessage(frameReadFailure(processResult.stderr, framePattern, startNumber, framesRead, framesInChunk) + `Chunk ${i} encode failed: ${processResult.stderr.slice(-400)}`, processResult.terminationReason === "
|
|
563
|
+
: appendEncodeTimeoutMessage(frameReadFailure(processResult.stderr, framePattern, startNumber, framesRead, framesInChunk) + `Chunk ${i} encode failed: ${processResult.stderr.slice(-400)}`, processResult.terminationReason === "inactivity", encodeTimeout),
|
|
570
564
|
};
|
|
571
565
|
if (!chunkResult.success) {
|
|
572
566
|
return {
|
|
@@ -19,7 +19,10 @@ export type { CaptureOptions, CaptureResult, CaptureBufferResult, CapturePerfSum
|
|
|
19
19
|
/** Called after seeking, before screenshot. Use for video frame injection or other pre-capture work.
|
|
20
20
|
* `heldVideoTime` (motion-blur samples only) is the output frame's time: a video on screen at
|
|
21
21
|
* both times shows its frame for `heldVideoTime`, so footage does not smear. */
|
|
22
|
-
export type BeforeCaptureHook = (page: Page, time: number, heldVideoTime?: number) => Promise<void
|
|
22
|
+
export type BeforeCaptureHook = ((page: Page, time: number, heldVideoTime?: number) => Promise<void>) & {
|
|
23
|
+
/** A hook with state of its own, for a second page; absent when the hook cannot be copied. */
|
|
24
|
+
fresh?: () => BeforeCaptureHook | null;
|
|
25
|
+
};
|
|
23
26
|
/** The per-frame wait a capture is in, worded for the stall error a user reads. */
|
|
24
27
|
type CaptureStep = "seeking the timeline" | "decoding CSS background images" | "injecting video frames" | "waiting for page seek handlers" | "loading color-grading LUTs" | "compositing the page" | "capturing the frame";
|
|
25
28
|
export interface CaptureSession {
|
|
@@ -161,6 +164,13 @@ export interface CaptureSession {
|
|
|
161
164
|
* Empty/undefined when the predictor produces no frames.
|
|
162
165
|
*/
|
|
163
166
|
clipBoundaryFrames?: Set<number>;
|
|
167
|
+
/**
|
|
168
|
+
* Frames drawElement cannot reproduce, for the caller to capture with a screenshot session.
|
|
169
|
+
* Set only when the caller can (worker-encode pipeline); drawElement capture throws on these.
|
|
170
|
+
*/
|
|
171
|
+
deRiskFrames?: Set<number>;
|
|
172
|
+
/** The effects behind {@link deRiskFrames}, for the capture log. */
|
|
173
|
+
deRiskReason?: string;
|
|
164
174
|
/** Rolling drawElement frame byte-sizes (last ~60), for silent-blank-drop detection:
|
|
165
175
|
* drawElement intermittently returns an anomalously small (blank) frame with no
|
|
166
176
|
* throw; a frame far below the running median is re-captured via screenshot. */
|