@hyperframes/engine 0.8.144 → 0.8.146
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 +0 -1
- package/dist/config.js +1 -2
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/services/__fixtures__/jpeg.d.ts +0 -1
- package/dist/services/__fixtures__/jpeg.js +0 -1
- package/dist/services/audioFxRender.d.ts +0 -1
- package/dist/services/audioFxRender.js +0 -1
- package/dist/services/audioMixer.d.ts +0 -1
- package/dist/services/audioMixer.js +0 -1
- package/dist/services/audioMixer.types.d.ts +0 -1
- package/dist/services/audioMixer.types.js +0 -1
- package/dist/services/audioVolumeEnvelope.d.ts +0 -1
- package/dist/services/audioVolumeEnvelope.js +0 -1
- package/dist/services/browserLeasePool.d.ts +0 -1
- package/dist/services/browserLeasePool.js +0 -1
- package/dist/services/browserManager.d.ts +0 -1
- package/dist/services/browserManager.js +2 -3
- package/dist/services/captureFailure.d.ts +0 -1
- package/dist/services/captureFailure.js +0 -1
- package/dist/services/captureWarning.d.ts +0 -1
- package/dist/services/captureWarning.js +0 -1
- package/dist/services/chromeHostCeiling.d.ts +0 -1
- package/dist/services/chromeHostCeiling.js +0 -1
- package/dist/services/chromeMemorySampler.d.ts +0 -1
- package/dist/services/chromeMemorySampler.js +0 -1
- package/dist/services/chunkEncoder.d.ts +0 -1
- package/dist/services/chunkEncoder.js +4 -5
- package/dist/services/chunkEncoder.types.d.ts +0 -1
- package/dist/services/chunkEncoder.types.js +0 -1
- package/dist/services/drawElementCaptureError.d.ts +0 -1
- package/dist/services/drawElementCaptureError.js +0 -1
- package/dist/services/drawElementService.d.ts +0 -1
- package/dist/services/drawElementService.js +1 -2
- package/dist/services/extractedFrameIndex.d.ts +0 -1
- package/dist/services/extractedFrameIndex.js +0 -1
- package/dist/services/extractionCache.d.ts +3 -1
- package/dist/services/extractionCache.js +17 -4
- package/dist/services/fileServer.d.ts +0 -1
- package/dist/services/fileServer.js +0 -1
- package/dist/services/frameCapture.d.ts +2 -2
- package/dist/services/frameCapture.js +33 -33
- package/dist/services/hdrCapture.d.ts +0 -1
- package/dist/services/hdrCapture.js +0 -1
- package/dist/services/mediaHidden.d.ts +0 -1
- package/dist/services/mediaHidden.js +0 -1
- package/dist/services/mediaRenderIdBridge.d.ts +0 -1
- package/dist/services/mediaRenderIdBridge.js +0 -1
- package/dist/services/mediaTimelineWindow.d.ts +0 -1
- package/dist/services/mediaTimelineWindow.js +0 -1
- package/dist/services/motionBlur.d.ts +0 -1
- package/dist/services/motionBlur.js +0 -1
- package/dist/services/pageNavigationTimeoutErrorHint.d.ts +0 -1
- package/dist/services/pageNavigationTimeoutErrorHint.js +0 -1
- package/dist/services/parallelCoordinator.d.ts +0 -1
- package/dist/services/parallelCoordinator.js +2 -3
- package/dist/services/protocolTimeoutErrorHint.d.ts +0 -1
- package/dist/services/protocolTimeoutErrorHint.js +0 -1
- package/dist/services/referenceResolver.d.ts +0 -1
- package/dist/services/referenceResolver.js +0 -1
- package/dist/services/screenshotService.d.ts +0 -1
- package/dist/services/screenshotService.js +19 -20
- package/dist/services/streamingEncoder.d.ts +0 -1
- package/dist/services/streamingEncoder.js +0 -1
- package/dist/services/systemMemory.d.ts +0 -1
- package/dist/services/systemMemory.js +2 -3
- package/dist/services/threeDProjection.d.ts +0 -1
- package/dist/services/threeDProjection.js +2 -3
- package/dist/services/videoFrameExtractor.d.ts +3 -1
- package/dist/services/videoFrameExtractor.js +47 -13
- package/dist/services/videoFrameInjector.d.ts +0 -1
- package/dist/services/videoFrameInjector.js +0 -1
- package/dist/services/vp9Options.d.ts +0 -1
- package/dist/services/vp9Options.js +0 -1
- package/dist/services/wavChunks.d.ts +0 -1
- package/dist/services/wavChunks.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/dist/utils/alphaBlit.d.ts +0 -1
- package/dist/utils/alphaBlit.js +0 -1
- package/dist/utils/alphaPlaneProbe.d.ts +0 -1
- package/dist/utils/alphaPlaneProbe.js +0 -1
- package/dist/utils/assertSwiftShader.d.ts +0 -1
- package/dist/utils/assertSwiftShader.js +0 -1
- package/dist/utils/concurrencyLimit.d.ts +5 -0
- package/dist/utils/concurrencyLimit.js +39 -0
- package/dist/utils/crc32.d.ts +0 -1
- package/dist/utils/crc32.js +0 -1
- package/dist/utils/evenDimensions.d.ts +0 -1
- package/dist/utils/evenDimensions.js +0 -1
- package/dist/utils/ffmpegBinaries.d.ts +0 -1
- package/dist/utils/ffmpegBinaries.js +0 -1
- package/dist/utils/ffprobe.d.ts +0 -1
- package/dist/utils/ffprobe.js +2 -2
- package/dist/utils/gpuEncoder.d.ts +0 -1
- package/dist/utils/gpuEncoder.js +0 -1
- package/dist/utils/gpuParityDiff.d.ts +0 -1
- package/dist/utils/gpuParityDiff.js +4 -5
- package/dist/utils/hdr.d.ts +0 -1
- package/dist/utils/hdr.js +1 -2
- package/dist/utils/htmlTemplate.d.ts +0 -1
- package/dist/utils/htmlTemplate.js +0 -1
- package/dist/utils/jpegInput.d.ts +0 -1
- package/dist/utils/jpegInput.js +0 -1
- package/dist/utils/layerCompositor.d.ts +0 -1
- package/dist/utils/layerCompositor.js +0 -1
- package/dist/utils/managedChildProcess.d.ts +0 -1
- package/dist/utils/managedChildProcess.js +0 -1
- package/dist/utils/notMediaPayload.d.ts +0 -1
- package/dist/utils/notMediaPayload.js +0 -1
- package/dist/utils/parityContract.d.ts +0 -1
- package/dist/utils/parityContract.js +0 -1
- package/dist/utils/processRss.d.ts +0 -1
- package/dist/utils/processRss.js +0 -1
- package/dist/utils/processTracker.d.ts +0 -1
- package/dist/utils/processTracker.js +0 -1
- package/dist/utils/psnr.d.ts +0 -1
- package/dist/utils/psnr.js +0 -1
- package/dist/utils/psnrFilterAvailability.d.ts +0 -1
- package/dist/utils/psnrFilterAvailability.js +0 -1
- package/dist/utils/readWebGlVendorInfoFromCanvas.d.ts +0 -1
- package/dist/utils/readWebGlVendorInfoFromCanvas.js +0 -1
- package/dist/utils/renderProvenance.d.ts +0 -1
- package/dist/utils/renderProvenance.js +0 -1
- package/dist/utils/runFfmpeg.d.ts +2 -1
- package/dist/utils/runFfmpeg.js +21 -1
- package/dist/utils/sdrCaptureColor.d.ts +0 -1
- package/dist/utils/sdrCaptureColor.js +0 -1
- package/dist/utils/shaderTransitions.d.ts +0 -1
- package/dist/utils/shaderTransitions.js +0 -1
- package/dist/utils/urlDownloader.d.ts +0 -1
- package/dist/utils/urlDownloader.js +0 -1
- package/package.json +3 -3
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/__fixtures__/jpeg.d.ts.map +0 -1
- package/dist/services/__fixtures__/jpeg.js.map +0 -1
- package/dist/services/audioFxRender.d.ts.map +0 -1
- package/dist/services/audioFxRender.js.map +0 -1
- package/dist/services/audioMixer.d.ts.map +0 -1
- package/dist/services/audioMixer.js.map +0 -1
- package/dist/services/audioMixer.types.d.ts.map +0 -1
- package/dist/services/audioMixer.types.js.map +0 -1
- package/dist/services/audioVolumeEnvelope.d.ts.map +0 -1
- package/dist/services/audioVolumeEnvelope.js.map +0 -1
- package/dist/services/browserLeasePool.d.ts.map +0 -1
- package/dist/services/browserLeasePool.js.map +0 -1
- package/dist/services/browserManager.d.ts.map +0 -1
- package/dist/services/browserManager.js.map +0 -1
- package/dist/services/captureFailure.d.ts.map +0 -1
- package/dist/services/captureFailure.js.map +0 -1
- package/dist/services/captureWarning.d.ts.map +0 -1
- package/dist/services/captureWarning.js.map +0 -1
- package/dist/services/chromeHostCeiling.d.ts.map +0 -1
- package/dist/services/chromeHostCeiling.js.map +0 -1
- package/dist/services/chromeMemorySampler.d.ts.map +0 -1
- package/dist/services/chromeMemorySampler.js.map +0 -1
- package/dist/services/chunkEncoder.d.ts.map +0 -1
- package/dist/services/chunkEncoder.js.map +0 -1
- package/dist/services/chunkEncoder.types.d.ts.map +0 -1
- package/dist/services/chunkEncoder.types.js.map +0 -1
- package/dist/services/drawElementCaptureError.d.ts.map +0 -1
- package/dist/services/drawElementCaptureError.js.map +0 -1
- package/dist/services/drawElementService.d.ts.map +0 -1
- package/dist/services/drawElementService.js.map +0 -1
- package/dist/services/extractedFrameIndex.d.ts.map +0 -1
- package/dist/services/extractedFrameIndex.js.map +0 -1
- package/dist/services/extractionCache.d.ts.map +0 -1
- package/dist/services/extractionCache.js.map +0 -1
- package/dist/services/fileServer.d.ts.map +0 -1
- package/dist/services/fileServer.js.map +0 -1
- package/dist/services/frameCapture.d.ts.map +0 -1
- package/dist/services/frameCapture.js.map +0 -1
- package/dist/services/hdrCapture.d.ts.map +0 -1
- package/dist/services/hdrCapture.js.map +0 -1
- package/dist/services/mediaHidden.d.ts.map +0 -1
- package/dist/services/mediaHidden.js.map +0 -1
- package/dist/services/mediaRenderIdBridge.d.ts.map +0 -1
- package/dist/services/mediaRenderIdBridge.js.map +0 -1
- package/dist/services/mediaTimelineWindow.d.ts.map +0 -1
- package/dist/services/mediaTimelineWindow.js.map +0 -1
- package/dist/services/motionBlur.d.ts.map +0 -1
- package/dist/services/motionBlur.js.map +0 -1
- package/dist/services/pageNavigationTimeoutErrorHint.d.ts.map +0 -1
- package/dist/services/pageNavigationTimeoutErrorHint.js.map +0 -1
- package/dist/services/parallelCoordinator.d.ts.map +0 -1
- package/dist/services/parallelCoordinator.js.map +0 -1
- package/dist/services/protocolTimeoutErrorHint.d.ts.map +0 -1
- package/dist/services/protocolTimeoutErrorHint.js.map +0 -1
- package/dist/services/referenceResolver.d.ts.map +0 -1
- package/dist/services/referenceResolver.js.map +0 -1
- package/dist/services/screenshotService.d.ts.map +0 -1
- package/dist/services/screenshotService.js.map +0 -1
- package/dist/services/streamingEncoder.d.ts.map +0 -1
- package/dist/services/streamingEncoder.js.map +0 -1
- package/dist/services/systemMemory.d.ts.map +0 -1
- package/dist/services/systemMemory.js.map +0 -1
- package/dist/services/threeDProjection.d.ts.map +0 -1
- package/dist/services/threeDProjection.js.map +0 -1
- package/dist/services/videoFrameExtractor.d.ts.map +0 -1
- package/dist/services/videoFrameExtractor.js.map +0 -1
- package/dist/services/videoFrameInjector.d.ts.map +0 -1
- package/dist/services/videoFrameInjector.js.map +0 -1
- package/dist/services/vp9Options.d.ts.map +0 -1
- package/dist/services/vp9Options.js.map +0 -1
- package/dist/services/wavChunks.d.ts.map +0 -1
- package/dist/services/wavChunks.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/alphaBlit.d.ts.map +0 -1
- package/dist/utils/alphaBlit.js.map +0 -1
- package/dist/utils/alphaPlaneProbe.d.ts.map +0 -1
- package/dist/utils/alphaPlaneProbe.js.map +0 -1
- package/dist/utils/assertSwiftShader.d.ts.map +0 -1
- package/dist/utils/assertSwiftShader.js.map +0 -1
- package/dist/utils/crc32.d.ts.map +0 -1
- package/dist/utils/crc32.js.map +0 -1
- package/dist/utils/evenDimensions.d.ts.map +0 -1
- package/dist/utils/evenDimensions.js.map +0 -1
- package/dist/utils/ffmpegBinaries.d.ts.map +0 -1
- package/dist/utils/ffmpegBinaries.js.map +0 -1
- package/dist/utils/ffprobe.d.ts.map +0 -1
- package/dist/utils/ffprobe.js.map +0 -1
- package/dist/utils/gpuEncoder.d.ts.map +0 -1
- package/dist/utils/gpuEncoder.js.map +0 -1
- package/dist/utils/gpuParityDiff.d.ts.map +0 -1
- package/dist/utils/gpuParityDiff.js.map +0 -1
- package/dist/utils/hdr.d.ts.map +0 -1
- package/dist/utils/hdr.js.map +0 -1
- package/dist/utils/htmlTemplate.d.ts.map +0 -1
- package/dist/utils/htmlTemplate.js.map +0 -1
- package/dist/utils/jpegInput.d.ts.map +0 -1
- package/dist/utils/jpegInput.js.map +0 -1
- package/dist/utils/layerCompositor.d.ts.map +0 -1
- package/dist/utils/layerCompositor.js.map +0 -1
- package/dist/utils/managedChildProcess.d.ts.map +0 -1
- package/dist/utils/managedChildProcess.js.map +0 -1
- package/dist/utils/notMediaPayload.d.ts.map +0 -1
- package/dist/utils/notMediaPayload.js.map +0 -1
- package/dist/utils/parityContract.d.ts.map +0 -1
- package/dist/utils/parityContract.js.map +0 -1
- package/dist/utils/processRss.d.ts.map +0 -1
- package/dist/utils/processRss.js.map +0 -1
- package/dist/utils/processTracker.d.ts.map +0 -1
- package/dist/utils/processTracker.js.map +0 -1
- package/dist/utils/psnr.d.ts.map +0 -1
- package/dist/utils/psnr.js.map +0 -1
- package/dist/utils/psnrFilterAvailability.d.ts.map +0 -1
- package/dist/utils/psnrFilterAvailability.js.map +0 -1
- package/dist/utils/readWebGlVendorInfoFromCanvas.d.ts.map +0 -1
- package/dist/utils/readWebGlVendorInfoFromCanvas.js.map +0 -1
- package/dist/utils/renderProvenance.d.ts.map +0 -1
- package/dist/utils/renderProvenance.js.map +0 -1
- package/dist/utils/runFfmpeg.d.ts.map +0 -1
- package/dist/utils/runFfmpeg.js.map +0 -1
- package/dist/utils/sdrCaptureColor.d.ts.map +0 -1
- package/dist/utils/sdrCaptureColor.js.map +0 -1
- package/dist/utils/shaderTransitions.d.ts.map +0 -1
- package/dist/utils/shaderTransitions.js.map +0 -1
- package/dist/utils/urlDownloader.d.ts.map +0 -1
- package/dist/utils/urlDownloader.js.map +0 -1
|
@@ -9,7 +9,9 @@ import { audioGroupsById, isMemberGroupHidden, isSelfOrAncestorHidden } from "./
|
|
|
9
9
|
import { copyFileSync, existsSync, linkSync, mkdirSync, rmSync } from "fs";
|
|
10
10
|
import { join } from "path";
|
|
11
11
|
import { cpus } from "os";
|
|
12
|
+
import { createConcurrencyLimit } from "../utils/concurrencyLimit.js";
|
|
12
13
|
import { parseHTML } from "linkedom";
|
|
14
|
+
import { isPathInside } from "@hyperframes/parsers/asset-paths";
|
|
13
15
|
import { resolveProjectRelativeSrc } from "@hyperframes/parsers/asset-resolution";
|
|
14
16
|
import { MEDIA_RENDER_ID_ATTR, isAudibleVideoElement, fpsToFfmpegArg, fpsToNumber, MEDIA_DURATION_CLAMP_EPSILON_SECONDS, normalizeRateSpec, readElementRateSpec, shiftRateLane, sourceTimeAt, timeAtSourceTime, exportClipWindow, hasClipStarted, isInClipWindow, parseStrictFiniteTimingNumber, readMediaStart, toFps, firstFrameColourArgs, hdrToSdrToneMapFilter, parseFirstFrameColour, } from "@hyperframes/core";
|
|
15
17
|
import { resolveReferencedStart } from "./referenceResolver.js";
|
|
@@ -22,7 +24,7 @@ import { runFfmpeg, runFfmpegPipeline } from "../utils/runFfmpeg.js";
|
|
|
22
24
|
import { isFfmpegFilterAvailable } from "../utils/psnrFilterAvailability.js";
|
|
23
25
|
import { DEFAULT_CONFIG } from "../config.js";
|
|
24
26
|
import { unwrapTemplate } from "../utils/htmlTemplate.js";
|
|
25
|
-
import { FRAME_FILENAME_PREFIX, gcExtractionCache, gcSweepDue, lookupCacheEntry, partialCacheEntryDir, publishCacheEntry, readKeyStat, rehydrateCacheEntry, touchCacheEntry, } from "./extractionCache.js";
|
|
27
|
+
import { FRAME_FILENAME_PREFIX, gcExtractionCache, gcSweepDue, lookupCacheEntry, partialCacheEntryDir, publishCacheEntry, readContentSha256, readKeyStat, rehydrateCacheEntry, touchCacheEntry, } from "./extractionCache.js";
|
|
26
28
|
import { framePathsFromDirectory } from "./extractedFrameIndex.js";
|
|
27
29
|
export { resolveProjectRelativeSrc };
|
|
28
30
|
/**
|
|
@@ -105,6 +107,7 @@ export function extractionFrameCountForDuration(durationSeconds, fps, isVFR) {
|
|
|
105
107
|
const frames = Number(frameCount);
|
|
106
108
|
return Math.max(1, Number.isSafeInteger(frames) ? frames : Number.MAX_SAFE_INTEGER);
|
|
107
109
|
}
|
|
110
|
+
const DOWNLOAD_SUBDIR = "_downloads";
|
|
108
111
|
export const EXTRACT_CACHE_MIN_AGE_MS = 60 * 60 * 1000;
|
|
109
112
|
const GC_STALENESS_MS = 24 * 60 * 60 * 1000;
|
|
110
113
|
const SDR_TO_HDR_COLORSPACE_FILTER = "colorspace=all=bt2020:iall=bt709:range=tv";
|
|
@@ -487,6 +490,9 @@ function restoreSourceColourFilter(metadata) {
|
|
|
487
490
|
* same absolute times as a single pass, so their frames are byte-identical to it.
|
|
488
491
|
*/
|
|
489
492
|
const EXTRACTION_SEGMENT_SECONDS = 120;
|
|
493
|
+
// Caps extraction ffmpeg runs across all clips; uncapped, dozens of clips starved each other into timeouts.
|
|
494
|
+
const EXTRACTION_SLOTS = Math.max(1, Math.floor(cpus().length / 2));
|
|
495
|
+
const inExtractionSlot = createConcurrencyLimit(EXTRACTION_SLOTS);
|
|
490
496
|
/** Split runs so no single ffmpeg process extracts more than `segmentFrames` frames. */
|
|
491
497
|
function splitFrameRanges(ranges, segmentFrames) {
|
|
492
498
|
const segments = [];
|
|
@@ -514,7 +520,7 @@ async function runSegmentedExtraction(job) {
|
|
|
514
520
|
while (next < segmentCount && !signal.aborted) {
|
|
515
521
|
const index = next++;
|
|
516
522
|
const { firstFrame, frames } = segments[index];
|
|
517
|
-
const result = await runFfmpeg([
|
|
523
|
+
const result = await inExtractionSlot(() => runFfmpeg([
|
|
518
524
|
...decodeArgs,
|
|
519
525
|
"-noaccurate_seek",
|
|
520
526
|
"-ss",
|
|
@@ -529,13 +535,13 @@ async function runSegmentedExtraction(job) {
|
|
|
529
535
|
"-start_number",
|
|
530
536
|
String(firstFrame + 1),
|
|
531
537
|
...filterAndEncodeArgs,
|
|
532
|
-
], { ...job.runOptions, signal });
|
|
538
|
+
], { ...job.runOptions, signal }), signal);
|
|
533
539
|
results[index] = result;
|
|
534
540
|
if (!result.success)
|
|
535
541
|
failed.abort();
|
|
536
542
|
}
|
|
537
543
|
};
|
|
538
|
-
const workers = Math.min(segmentCount,
|
|
544
|
+
const workers = Math.min(segmentCount, EXTRACTION_SLOTS);
|
|
539
545
|
await Promise.all(Array.from({ length: workers }, worker));
|
|
540
546
|
const ran = results.filter(Boolean);
|
|
541
547
|
// Report the segment that failed, not the ones stopped because of it.
|
|
@@ -667,7 +673,7 @@ outputDirOverride) {
|
|
|
667
673
|
NUT_RAW_PIXEL_FORMATS.has(metadata.pixelFormat ?? "")) {
|
|
668
674
|
// ffmpeg <6.1 ignores frame durations in CFR resampling once any -vf is set,
|
|
669
675
|
// cutting a trailing still short, so the SDR filters run in a second process.
|
|
670
|
-
processResult = await runFfmpegPipeline([...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"], [
|
|
676
|
+
processResult = await inExtractionSlot(() => runFfmpegPipeline([...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"], [
|
|
671
677
|
"-f",
|
|
672
678
|
"nut",
|
|
673
679
|
"-i",
|
|
@@ -677,7 +683,7 @@ outputDirOverride) {
|
|
|
677
683
|
"-fps_mode",
|
|
678
684
|
"passthrough",
|
|
679
685
|
...encodeArgs,
|
|
680
|
-
], runOptions);
|
|
686
|
+
], runOptions), signal);
|
|
681
687
|
}
|
|
682
688
|
else {
|
|
683
689
|
const filterArgs = vfFilters.length > 0 ? ["-vf", vfFilters.join(",")] : [];
|
|
@@ -694,7 +700,7 @@ outputDirOverride) {
|
|
|
694
700
|
segments: splitFrameRanges(options.frameRanges ?? [{ firstFrame: 0, frames: totalFrames }], segmentFrames),
|
|
695
701
|
runOptions,
|
|
696
702
|
})
|
|
697
|
-
: await runFfmpeg([...args, ...filterArgs, ...encodeArgs], runOptions);
|
|
703
|
+
: await inExtractionSlot(() => runFfmpeg([...args, ...filterArgs, ...encodeArgs], runOptions), signal);
|
|
698
704
|
}
|
|
699
705
|
if (processResult.failureReason === "external_interruption") {
|
|
700
706
|
throw new VideoSourceExtractionError("external_interruption", true, "Video frame extraction interrupted by host lifecycle", `FFmpeg exited with code ${processResult.exitCode}: ${processResult.stderr.slice(-500)}`);
|
|
@@ -801,6 +807,9 @@ function canHoldFinalFramePastEof(video) {
|
|
|
801
807
|
// deliberately below any supported output frame interval: coverage expects
|
|
802
808
|
// one frame, while FFmpeg seeks to the separately probed real frame timestamp.
|
|
803
809
|
const FINAL_FRAME_LOGICAL_DURATION_SECONDS = 1e-6;
|
|
810
|
+
export function isVideoPastTimelineEnd(video, timelineEnd) {
|
|
811
|
+
return video.start >= timelineEnd;
|
|
812
|
+
}
|
|
804
813
|
/** Move a clip to its extraction window; a rate lane is shifted so it keeps integrating from the new origin. */
|
|
805
814
|
export function rebaseVideoToWindow(video, window) {
|
|
806
815
|
if (window.preserveTimelinePhase)
|
|
@@ -1166,15 +1175,16 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1166
1175
|
for (const video of videos) {
|
|
1167
1176
|
if (signal?.aborted)
|
|
1168
1177
|
break;
|
|
1169
|
-
if (options.timelineEnd !== undefined && video
|
|
1178
|
+
if (options.timelineEnd !== undefined && isVideoPastTimelineEnd(video, options.timelineEnd)) {
|
|
1170
1179
|
continue;
|
|
1180
|
+
}
|
|
1171
1181
|
try {
|
|
1172
1182
|
let videoPath = video.src;
|
|
1173
1183
|
if (!isHttpUrl(videoPath)) {
|
|
1174
1184
|
videoPath = resolveProjectRelativeSrc(video.src, baseDir, compiledDir);
|
|
1175
1185
|
}
|
|
1176
1186
|
if (isHttpUrl(videoPath)) {
|
|
1177
|
-
const downloadDir = join(options.outputDir,
|
|
1187
|
+
const downloadDir = join(options.outputDir, DOWNLOAD_SUBDIR);
|
|
1178
1188
|
mkdirSync(downloadDir, { recursive: true });
|
|
1179
1189
|
videoPath = await downloadToTemp(videoPath, downloadDir, undefined, signal, () => recordTransientRetries(1), { onTelemetry: writeUrlDownloadTelemetry });
|
|
1180
1190
|
}
|
|
@@ -1218,6 +1228,11 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1218
1228
|
// HDR preflight. Without this, every render would write a new
|
|
1219
1229
|
// normalized file with a fresh mtime → fresh cache key → perpetual misses.
|
|
1220
1230
|
// Phase 3 updates mediaStart after trimming any invisible negative preroll.
|
|
1231
|
+
// Downloads land at a new path and mtime every render, so they are keyed by content instead.
|
|
1232
|
+
const contentKeyedDirs = [
|
|
1233
|
+
join(options.outputDir, DOWNLOAD_SUBDIR),
|
|
1234
|
+
...(options.contentKeyedDirs ?? []),
|
|
1235
|
+
];
|
|
1221
1236
|
const cacheKeyInputs = resolvedVideos.map(({ video, videoPath }) => {
|
|
1222
1237
|
const stat = readKeyStat(videoPath);
|
|
1223
1238
|
// Missing files return null — skip the cache path for that entry. The
|
|
@@ -1231,6 +1246,7 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1231
1246
|
mtimeMs: stat.mtimeMs,
|
|
1232
1247
|
size: stat.size,
|
|
1233
1248
|
mediaStart: video.mediaStart,
|
|
1249
|
+
contentKeyed: contentKeyedDirs.some((dir) => isPathInside(videoPath, dir)),
|
|
1234
1250
|
};
|
|
1235
1251
|
});
|
|
1236
1252
|
// Phase 2: Probe color spaces and normalize if mixed HDR/SDR
|
|
@@ -1410,12 +1426,30 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1410
1426
|
});
|
|
1411
1427
|
return { ...rehydrated, ownedByLookup: true };
|
|
1412
1428
|
}
|
|
1429
|
+
// Hashed only for works that reach a lookup (deferred ranges never do), once per file.
|
|
1430
|
+
const contentSha256ByPath = new Map();
|
|
1431
|
+
async function hashContentKeyedSources(works) {
|
|
1432
|
+
if (!cacheRootDir)
|
|
1433
|
+
return;
|
|
1434
|
+
const paths = new Set();
|
|
1435
|
+
for (const work of works) {
|
|
1436
|
+
const keyInput = cacheKeyInputs[work.index];
|
|
1437
|
+
if (keyInput?.contentKeyed)
|
|
1438
|
+
paths.add(keyInput.videoPath);
|
|
1439
|
+
}
|
|
1440
|
+
await Promise.all([...paths].map(async (path) => contentSha256ByPath.set(path, await readContentSha256(path, signal))));
|
|
1441
|
+
}
|
|
1413
1442
|
function lookupCacheFor(work) {
|
|
1414
1443
|
if (!cacheRootDir)
|
|
1415
1444
|
return { work };
|
|
1416
1445
|
const keyInput = cacheKeyInputs[work.index];
|
|
1417
1446
|
if (!keyInput)
|
|
1418
1447
|
return { work };
|
|
1448
|
+
const contentSha256 = keyInput.contentKeyed
|
|
1449
|
+
? (contentSha256ByPath.get(keyInput.videoPath) ?? null)
|
|
1450
|
+
: undefined;
|
|
1451
|
+
if (contentSha256 === null)
|
|
1452
|
+
return { work };
|
|
1419
1453
|
const transformParts = [
|
|
1420
1454
|
work.sdrToHdrTransfer ? sdrToHdrTransformKey(work.sdrToHdrTransfer) : undefined,
|
|
1421
1455
|
work.hdrToSdrTransformKey,
|
|
@@ -1427,6 +1461,7 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1427
1461
|
mtimeMs: keyInput.mtimeMs,
|
|
1428
1462
|
size: keyInput.size,
|
|
1429
1463
|
mediaStart: keyInput.mediaStart,
|
|
1464
|
+
contentSha256,
|
|
1430
1465
|
duration: work.videoDuration,
|
|
1431
1466
|
fps: fpsKey,
|
|
1432
1467
|
format: work.format,
|
|
@@ -1657,9 +1692,9 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
1657
1692
|
});
|
|
1658
1693
|
}
|
|
1659
1694
|
const cacheMisses = [];
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1695
|
+
const worksToLookUp = [...uniqueWorks.values()].filter((work) => !uniqueOutcomes.has(work.dedupeKey));
|
|
1696
|
+
await hashContentKeyedSources(worksToLookUp);
|
|
1697
|
+
for (const work of worksToLookUp) {
|
|
1663
1698
|
const lookup = lookupCacheFor(work);
|
|
1664
1699
|
if ("work" in lookup) {
|
|
1665
1700
|
cacheMisses.push(lookup);
|
|
@@ -1929,4 +1964,3 @@ export function createFrameLookupTable(videos, extracted, fps) {
|
|
|
1929
1964
|
}
|
|
1930
1965
|
return table;
|
|
1931
1966
|
}
|
|
1932
|
-
//# sourceMappingURL=videoFrameExtractor.js.map
|
|
@@ -3,4 +3,3 @@ export declare const MIN_VP9_CPU_USED = -8;
|
|
|
3
3
|
export declare const MAX_VP9_CPU_USED = 8;
|
|
4
4
|
export declare function normalizeVp9CpuUsed(value: number | undefined): number;
|
|
5
5
|
export declare function appendVp9CpuUsedArg(args: string[], value: number | undefined): void;
|
|
6
|
-
//# sourceMappingURL=vp9Options.d.ts.map
|
|
@@ -17,4 +17,3 @@ export interface RiffChunk {
|
|
|
17
17
|
export declare function riffChunks(buffer: Buffer): Generator<RiffChunk>;
|
|
18
18
|
/** Canonical codec tag, or null for a truncated or unsupported extensible header. */
|
|
19
19
|
export declare function wavFormatTag(buffer: Buffer, body: number, size: number): number | null;
|
|
20
|
-
//# sourceMappingURL=wavChunks.d.ts.map
|
package/dist/types.d.ts
CHANGED
package/dist/types.js
CHANGED
package/dist/utils/alphaBlit.js
CHANGED
|
@@ -6,4 +6,3 @@ export declare function sampledRgbaAlphaIsFullyOpaque(buf: Buffer): boolean | un
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function probeInputAlphaPlane(videoPath: string, decoder: string, signal?: AbortSignal): Promise<boolean | undefined>;
|
|
8
8
|
export declare function inputAlphaOpaqueWarning(src: string): string;
|
|
9
|
-
//# sourceMappingURL=alphaPlaneProbe.d.ts.map
|
|
@@ -103,4 +103,3 @@ export async function assertSwiftShader(page, readInfo = readWebGlVendorInfo) {
|
|
|
103
103
|
`Ensure Chrome was launched with --use-gl=swiftshader --use-angle=swiftshader and that the ` +
|
|
104
104
|
`SwiftShader libraries are present in the runtime image.`, vendor, renderer);
|
|
105
105
|
}
|
|
106
|
-
//# sourceMappingURL=assertSwiftShader.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs at most `limit` tasks at once; later tasks wait, in order, for a free slot. A waiter whose
|
|
3
|
+
* `signal` aborts stops waiting and runs at once without a slot, so a cancel never queues behind others.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createConcurrencyLimit(limit: number): <T>(task: () => Promise<T>, signal?: AbortSignal) => Promise<T>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs at most `limit` tasks at once; later tasks wait, in order, for a free slot. A waiter whose
|
|
3
|
+
* `signal` aborts stops waiting and runs at once without a slot, so a cancel never queues behind others.
|
|
4
|
+
*/
|
|
5
|
+
export function createConcurrencyLimit(limit) {
|
|
6
|
+
let active = 0;
|
|
7
|
+
const waiting = [];
|
|
8
|
+
const waitForSlot = (signal) => new Promise((resolve) => {
|
|
9
|
+
const granted = () => {
|
|
10
|
+
signal?.removeEventListener("abort", aborted);
|
|
11
|
+
resolve(true);
|
|
12
|
+
};
|
|
13
|
+
const aborted = () => {
|
|
14
|
+
waiting.splice(waiting.indexOf(granted), 1);
|
|
15
|
+
resolve(false);
|
|
16
|
+
};
|
|
17
|
+
waiting.push(granted);
|
|
18
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
19
|
+
});
|
|
20
|
+
return async (task, signal) => {
|
|
21
|
+
if (signal?.aborted)
|
|
22
|
+
return task();
|
|
23
|
+
if (active < limit)
|
|
24
|
+
active++;
|
|
25
|
+
else if (!(await waitForSlot(signal)))
|
|
26
|
+
return task();
|
|
27
|
+
try {
|
|
28
|
+
return await task();
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
// Hand the slot straight to the next waiter so a newcomer cannot take it first.
|
|
32
|
+
const next = waiting.shift();
|
|
33
|
+
if (next)
|
|
34
|
+
next();
|
|
35
|
+
else
|
|
36
|
+
active--;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
package/dist/utils/crc32.d.ts
CHANGED
package/dist/utils/crc32.js
CHANGED
|
@@ -32,4 +32,3 @@ export declare function requiresEvenDimensions(pixelFormat: string): boolean;
|
|
|
32
32
|
* returns just the pad when there is no existing chain.
|
|
33
33
|
*/
|
|
34
34
|
export declare function withEvenDimensionPad(vfChain: string, pixelFormat: string, width?: number, height?: number): string;
|
|
35
|
-
//# sourceMappingURL=evenDimensions.d.ts.map
|
package/dist/utils/ffprobe.d.ts
CHANGED
|
@@ -154,4 +154,3 @@ export interface KeyframeAnalysis {
|
|
|
154
154
|
*/
|
|
155
155
|
export declare function analyzeKeyframeIntervals(filePath: string, metadata: Pick<VideoMetadata, "videoStreamDurationSeconds" | "videoStreamStartSeconds">): Promise<KeyframeAnalysis>;
|
|
156
156
|
export {};
|
|
157
|
-
//# sourceMappingURL=ffprobe.d.ts.map
|
package/dist/utils/ffprobe.js
CHANGED
|
@@ -141,6 +141,7 @@ const AUDIO_DURATION_PROBE_MARGIN_SECONDS = 0.05;
|
|
|
141
141
|
// How far before the container's claimed end the decode probe starts reading,
|
|
142
142
|
// so it decodes only the tail instead of the whole stream.
|
|
143
143
|
const AUDIO_DURATION_PROBE_TAIL_SECONDS = 1;
|
|
144
|
+
const FFPROBE_READ_INTERVAL_EXPLICIT_FAR_END = "+2147483647";
|
|
144
145
|
const STILL_IMAGE_DEMUXERS = new Set([
|
|
145
146
|
"apng",
|
|
146
147
|
"bmp_pipe",
|
|
@@ -845,7 +846,7 @@ async function probeDecodedAudioDuration(filePath, sampleRate, startSeconds, con
|
|
|
845
846
|
}
|
|
846
847
|
};
|
|
847
848
|
const tailStart = Math.max(startSeconds, startSeconds + containerDurationSeconds - AUDIO_DURATION_PROBE_TAIL_SECONDS);
|
|
848
|
-
const lastFrame = (await probe(`${tailStart}
|
|
849
|
+
const lastFrame = (await probe(`${tailStart}%${FFPROBE_READ_INTERVAL_EXPLICIT_FAR_END}`)) ?? (await probe());
|
|
849
850
|
if (!lastFrame)
|
|
850
851
|
return null;
|
|
851
852
|
return lastFrame.timestamp + lastFrame.nbSamples / sampleRate - startSeconds;
|
|
@@ -928,4 +929,3 @@ async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSecond
|
|
|
928
929
|
isProblematic: maxInterval > PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS,
|
|
929
930
|
};
|
|
930
931
|
}
|
|
931
|
-
//# sourceMappingURL=ffprobe.js.map
|
|
@@ -31,4 +31,3 @@ export declare function getProbeArgs(encoder: ConcreteGpuEncoder): string[];
|
|
|
31
31
|
* `-preset` entirely or accept the libx264 vocabulary).
|
|
32
32
|
*/
|
|
33
33
|
export declare function mapPresetForGpuEncoder(encoder: GpuEncoder, preset: string): string;
|
|
34
|
-
//# sourceMappingURL=gpuEncoder.d.ts.map
|
package/dist/utils/gpuEncoder.js
CHANGED
|
@@ -139,4 +139,3 @@ export declare function diffGpuParityPngs(pngA: Buffer, pngB: Buffer, options?:
|
|
|
139
139
|
* metrics without a second pass.
|
|
140
140
|
*/
|
|
141
141
|
export declare function verifyGpuParity(a: RgbaFrame, b: RgbaFrame, options?: GpuParityDiffOptions): VerifyGpuParityResult;
|
|
142
|
-
//# sourceMappingURL=gpuParityDiff.d.ts.map
|
|
@@ -49,7 +49,7 @@ const DEFAULT_BLACK_ONLY_FRACTION_THRESHOLD = 0.001;
|
|
|
49
49
|
*/
|
|
50
50
|
export function diffGpuParityFrames(a, b, options = {}) {
|
|
51
51
|
if (a.width !== b.width || a.height !== b.height) {
|
|
52
|
-
throw new Error(`diffGpuParityFrames: frame size mismatch
|
|
52
|
+
throw new Error(`diffGpuParityFrames: frame size mismatch: A is ${a.width}x${a.height}, ` +
|
|
53
53
|
`B is ${b.width}x${b.height}`);
|
|
54
54
|
}
|
|
55
55
|
const expectedLen = a.width * a.height * 4;
|
|
@@ -66,7 +66,7 @@ export function diffGpuParityFrames(a, b, options = {}) {
|
|
|
66
66
|
const contentSum = options.contentSumThreshold ?? DEFAULT_CONTENT_SUM_THRESHOLD;
|
|
67
67
|
if (contentSum <= blackSum) {
|
|
68
68
|
throw new Error(`diffGpuParityFrames: contentSumThreshold (${contentSum}) must be strictly greater ` +
|
|
69
|
-
`than blackSumThreshold (${blackSum})
|
|
69
|
+
`than blackSumThreshold (${blackSum}): overlapping thresholds would double-classify ` +
|
|
70
70
|
`borderline pixels`);
|
|
71
71
|
}
|
|
72
72
|
const total = a.width * a.height;
|
|
@@ -183,7 +183,7 @@ export function verifyGpuParity(a, b, options = {}) {
|
|
|
183
183
|
ok: false,
|
|
184
184
|
reason: `hardware-GPU frame has ${diff.blackOnlyInA.pixels} pixel(s) ` +
|
|
185
185
|
`(${(diff.blackOnlyInA.fraction * 100).toFixed(3)}%) that are solid-black ` +
|
|
186
|
-
`while software-GPU frame has content there${region}
|
|
186
|
+
`while software-GPU frame has content there${region}, likely a shape-dependent ` +
|
|
187
187
|
`hardware-GPU capture bug`,
|
|
188
188
|
diff,
|
|
189
189
|
};
|
|
@@ -195,11 +195,10 @@ export function verifyGpuParity(a, b, options = {}) {
|
|
|
195
195
|
ok: false,
|
|
196
196
|
reason: `software-GPU frame has ${diff.blackOnlyInB.pixels} pixel(s) ` +
|
|
197
197
|
`(${(diff.blackOnlyInB.fraction * 100).toFixed(3)}%) that are solid-black ` +
|
|
198
|
-
`while hardware-GPU frame has content there${region}
|
|
198
|
+
`while hardware-GPU frame has content there${region}, an unexpected inverse ` +
|
|
199
199
|
`pattern, worth investigating`,
|
|
200
200
|
diff,
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
return { ok: true, reason: "", diff };
|
|
204
204
|
}
|
|
205
|
-
//# sourceMappingURL=gpuParityDiff.js.map
|
package/dist/utils/hdr.d.ts
CHANGED
|
@@ -93,4 +93,3 @@ export declare function formatHdrAutoPromotionWarning(promotion: HdrAutoPromotio
|
|
|
93
93
|
* contains HDR content and what the dominant transfer function is.
|
|
94
94
|
*/
|
|
95
95
|
export declare function analyzeCompositionHdr(colorSpaces: Array<VideoColorSpace | null>): CompositionHdrInfo;
|
|
96
|
-
//# sourceMappingURL=hdr.d.ts.map
|
package/dist/utils/hdr.js
CHANGED
|
@@ -81,7 +81,7 @@ export function findHdrAutoPromotion(sources) {
|
|
|
81
81
|
: null;
|
|
82
82
|
}
|
|
83
83
|
export function formatHdrAutoPromotionWarning(promotion) {
|
|
84
|
-
return `[Render] HDR auto-promotion triggered by "${promotion.triggeringAsset}"
|
|
84
|
+
return `[Render] HDR auto-promotion triggered by "${promotion.triggeringAsset}", output: ${promotion.output.colorSpace} / ${promotion.output.codec}`;
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Analyze a set of video color spaces to determine if the composition
|
|
@@ -103,4 +103,3 @@ export function analyzeCompositionHdr(colorSpaces) {
|
|
|
103
103
|
const dominantTransfer = hasPq ? "pq" : "hlg";
|
|
104
104
|
return { hasHdr: true, dominantTransfer };
|
|
105
105
|
}
|
|
106
|
-
//# sourceMappingURL=hdr.js.map
|
package/dist/utils/jpegInput.js
CHANGED
|
@@ -61,4 +61,3 @@ export declare function isNotMediaPayload(filePath: string): Promise<boolean>;
|
|
|
61
61
|
* through its `svg_pipe` demuxer, so image sources must not be sniffed.
|
|
62
62
|
*/
|
|
63
63
|
export declare function assertMediaPayload(filePath: string, elementId: string): Promise<void>;
|
|
64
|
-
//# sourceMappingURL=notMediaPayload.d.ts.map
|
|
@@ -20,4 +20,3 @@ export declare function parseProcStatusRss(pid: number, status: string): Process
|
|
|
20
20
|
* rejects; a pid that exited between discovery and sampling is simply absent.
|
|
21
21
|
*/
|
|
22
22
|
export declare function sampleProcessRss(pids: readonly number[], exec?: ExecFileLike, platform?: NodeJS.Platform, readStatus?: ReadFileLike): Promise<ProcessRssSample[]>;
|
|
23
|
-
//# sourceMappingURL=processRss.d.ts.map
|
package/dist/utils/processRss.js
CHANGED
package/dist/utils/psnr.d.ts
CHANGED
package/dist/utils/psnr.js
CHANGED
|
@@ -11,4 +11,3 @@ export declare function isPsnrFilterAvailable(): Promise<boolean>;
|
|
|
11
11
|
export declare function isFfmpegFilterAvailable(name: string): Promise<boolean>;
|
|
12
12
|
/** Test-only: drop the memoized probe result. */
|
|
13
13
|
export declare function resetPsnrFilterAvailabilityCache(): void;
|
|
14
|
-
//# sourceMappingURL=psnrFilterAvailability.d.ts.map
|
|
@@ -55,4 +55,3 @@ export interface RenderProvenance {
|
|
|
55
55
|
* case-insensitive — a case-sensitive read works on mp4 and misses every webm.
|
|
56
56
|
*/
|
|
57
57
|
export declare function readRenderProvenance(tags: Record<string, string | undefined> | undefined): RenderProvenance | null;
|
|
58
|
-
//# sourceMappingURL=renderProvenance.d.ts.map
|
|
@@ -47,10 +47,11 @@ export declare function ffmpegStatsReader(onStats: (stats: FfmpegStats) => void)
|
|
|
47
47
|
* exactly which option to fix.
|
|
48
48
|
*/
|
|
49
49
|
export declare function formatFfmpegError(exitCode: number | null, stderr: string, tailLines?: number): string;
|
|
50
|
+
/** formatFfmpegError, led by the timeout when this process was killed at its deadline. */
|
|
51
|
+
export declare function describeFfmpegFailure(result: Pick<RunFfmpegResult, "exitCode" | "stderr" | "terminationReason">, timeoutMs: number): string;
|
|
50
52
|
export declare function runFfmpeg(args: string[], opts?: RunFfmpegOptions): Promise<RunFfmpegResult>;
|
|
51
53
|
/**
|
|
52
54
|
* Runs `ffmpeg <producerArgs> | ffmpeg <consumerArgs>`. Fails when either side
|
|
53
55
|
* fails and reports the first failure, its stderr last so error tails show it.
|
|
54
56
|
*/
|
|
55
57
|
export declare function runFfmpegPipeline(producerArgs: string[], consumerArgs: string[], opts?: RunFfmpegOptions): Promise<RunFfmpegResult>;
|
|
56
|
-
//# sourceMappingURL=runFfmpeg.d.ts.map
|