@hyperframes/producer 0.8.191 → 0.8.193
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 +1 -0
- package/dist/distributed.js +300 -109
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +62 -62
- package/dist/index.js +503 -456
- package/dist/public-server.js +333 -299
- package/dist/services/distributed/plan.d.ts +2 -0
- package/dist/services/distributed/probeFacts.d.ts +4 -0
- package/dist/services/render/perfSummary.d.ts +6 -0
- package/dist/services/render/stages/audioStage.d.ts +3 -1
- package/dist/services/render/stages/extractVideosStage.d.ts +17 -2
- package/dist/services/renderOrchestrator.d.ts +2 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1364,18 +1364,9 @@ var RenderExecutionContext = class {
|
|
|
1364
1364
|
};
|
|
1365
1365
|
|
|
1366
1366
|
// src/services/render/artifactTransaction.ts
|
|
1367
|
-
import {
|
|
1368
|
-
closeSync as closeSync2,
|
|
1369
|
-
existsSync as existsSync5,
|
|
1370
|
-
fstatSync as fstatSync2,
|
|
1371
|
-
openSync as openSync2,
|
|
1372
|
-
readSync,
|
|
1373
|
-
readdirSync as readdirSync2,
|
|
1374
|
-
renameSync,
|
|
1375
|
-
rmSync as rmSync3
|
|
1376
|
-
} from "node:fs";
|
|
1367
|
+
import { closeSync as closeSync2, existsSync as existsSync5, fstatSync as fstatSync2, openSync as openSync2, readSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
|
|
1377
1368
|
import { basename as basename3, dirname as dirname4, extname as extname2, join as join5, resolve as resolve4 } from "node:path";
|
|
1378
|
-
import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST } from "@hyperframes/engine";
|
|
1369
|
+
import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, renameWithRetry } from "@hyperframes/engine";
|
|
1379
1370
|
|
|
1380
1371
|
// src/utils/ffprobe.ts
|
|
1381
1372
|
import {
|
|
@@ -1495,7 +1486,7 @@ function createRenderWorkDir(prefix, outputDir) {
|
|
|
1495
1486
|
// src/services/render/artifactTransaction.ts
|
|
1496
1487
|
var defaultFileSystem = {
|
|
1497
1488
|
existsSync: existsSync5,
|
|
1498
|
-
renameSync,
|
|
1489
|
+
renameSync: (source, destination) => renameWithRetry(source, destination),
|
|
1499
1490
|
rmSync: rmSync3
|
|
1500
1491
|
};
|
|
1501
1492
|
async function defaultArtifactDurationProbe(path) {
|
|
@@ -1894,58 +1885,10 @@ import {
|
|
|
1894
1885
|
|
|
1895
1886
|
// src/services/render/captureStageError.ts
|
|
1896
1887
|
import { CaptureFailure, classifyCaptureFailure } from "@hyperframes/engine";
|
|
1897
|
-
var CaptureStageError = class extends CaptureFailure {
|
|
1898
|
-
browserConsole;
|
|
1899
|
-
constructor(input) {
|
|
1900
|
-
const classified = classifyCaptureFailure(input.cause);
|
|
1901
|
-
super({
|
|
1902
|
-
kind: classified.kind,
|
|
1903
|
-
message: normalizeErrorMessage(input.cause),
|
|
1904
|
-
cause: input.cause,
|
|
1905
|
-
workerDiagnostics: classified.workerDiagnostics
|
|
1906
|
-
});
|
|
1907
|
-
this.name = "CaptureStageError";
|
|
1908
|
-
this.browserConsole = input.browserConsole.slice();
|
|
1909
|
-
if (input.cause instanceof Error && input.cause.stack) {
|
|
1910
|
-
this.stack = input.cause.stack;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
};
|
|
1914
|
-
function wrapCaptureStageError(error, browserConsole) {
|
|
1915
|
-
if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
|
|
1916
|
-
return new CaptureStageError({ cause: error, browserConsole });
|
|
1917
|
-
}
|
|
1918
|
-
function getCaptureStageBrowserConsole(error) {
|
|
1919
|
-
if (error instanceof CaptureStageError) return error.browserConsole;
|
|
1920
|
-
return [];
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
// src/services/render/stages/captureHdrFrameShared.ts
|
|
1924
|
-
import {
|
|
1925
|
-
applyDomLayerMask as applyDomLayerMask2,
|
|
1926
|
-
blitRgba8OverRgb48le as blitRgba8OverRgb48le2,
|
|
1927
|
-
captureAlphaPng as captureAlphaPng2,
|
|
1928
|
-
decodePng as decodePng2,
|
|
1929
|
-
queryElementStacking as queryElementStacking2,
|
|
1930
|
-
removeDomLayerMask as removeDomLayerMask2
|
|
1931
|
-
} from "@hyperframes/engine";
|
|
1932
1888
|
|
|
1933
|
-
// src/services/
|
|
1934
|
-
import {
|
|
1935
|
-
import {
|
|
1936
|
-
import {
|
|
1937
|
-
captureAlphaPng,
|
|
1938
|
-
applyDomLayerMask,
|
|
1939
|
-
removeDomLayerMask,
|
|
1940
|
-
decodePng,
|
|
1941
|
-
blitRgba8OverRgb48le,
|
|
1942
|
-
blitRgb48leRegion,
|
|
1943
|
-
groupIntoLayers,
|
|
1944
|
-
blitRgb48leAffine,
|
|
1945
|
-
parseTransformMatrix,
|
|
1946
|
-
convertTransfer,
|
|
1947
|
-
isVideoHiddenBeforeStreamStart
|
|
1948
|
-
} from "@hyperframes/engine";
|
|
1889
|
+
// src/services/render/perfSummary.ts
|
|
1890
|
+
import { arch, cpus, platform, totalmem } from "node:os";
|
|
1891
|
+
import { fpsToNumber as fpsToNumber3 } from "@hyperframes/core";
|
|
1949
1892
|
|
|
1950
1893
|
// src/services/render/hdrPerf.ts
|
|
1951
1894
|
function createHdrPerfCollector() {
|
|
@@ -2051,7 +1994,280 @@ function finalizeHdrPerf(perf) {
|
|
|
2051
1994
|
};
|
|
2052
1995
|
}
|
|
2053
1996
|
|
|
1997
|
+
// src/services/render/perfSummary.ts
|
|
1998
|
+
function worstSubTimelineWaitOutcome(perfs) {
|
|
1999
|
+
const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
|
|
2000
|
+
if (outcomes.length === 0) return void 0;
|
|
2001
|
+
if (outcomes.includes("script_failure")) return "script_failure";
|
|
2002
|
+
if (outcomes.includes("timeout")) return "timeout";
|
|
2003
|
+
return "ready";
|
|
2004
|
+
}
|
|
2005
|
+
function pushWorkerDedupPerfs(results, sink) {
|
|
2006
|
+
for (const r of results) {
|
|
2007
|
+
if (r.perf) sink.push(r.perf);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function pushWorkerPageErrors(results, sink) {
|
|
2011
|
+
for (const result of results) {
|
|
2012
|
+
for (const line of result.pageErrorLines ?? []) if (!sink.includes(line)) sink.push(line);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
function withWorkerPageErrors(consoleLines, workerLines) {
|
|
2016
|
+
return [...consoleLines, ...workerLines.filter((line) => !consoleLines.includes(line))];
|
|
2017
|
+
}
|
|
2018
|
+
function roundDb(value) {
|
|
2019
|
+
return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
|
|
2020
|
+
}
|
|
2021
|
+
function aggregateDrawElement(perfs, de) {
|
|
2022
|
+
if (perfs.length === 0) return void 0;
|
|
2023
|
+
const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
|
|
2024
|
+
const gateReasons = [
|
|
2025
|
+
...new Set(
|
|
2026
|
+
[de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
|
|
2027
|
+
)
|
|
2028
|
+
].sort();
|
|
2029
|
+
const gpuRenderers = [
|
|
2030
|
+
...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
|
|
2031
|
+
].sort();
|
|
2032
|
+
const drain = de.drainStats;
|
|
2033
|
+
return {
|
|
2034
|
+
mode: modes.join("|") || "unknown",
|
|
2035
|
+
compileGate: de.compileGate,
|
|
2036
|
+
clampReason: de.clampReason,
|
|
2037
|
+
workerInversion: de.workerInversion ?? "none",
|
|
2038
|
+
preInversionWorkers: de.preInversionWorkers,
|
|
2039
|
+
compositionElementCount: de.compositionElementCount,
|
|
2040
|
+
compositionElementCountSource: de.compositionElementCountSource,
|
|
2041
|
+
compositionElementTags: de.compositionElementTags,
|
|
2042
|
+
arollVideoCount: de.arollVideoCount,
|
|
2043
|
+
heygenVideoCount: de.heygenVideoCount,
|
|
2044
|
+
adaptersUsed: de.adaptersUsed,
|
|
2045
|
+
audioCount: de.audioCount,
|
|
2046
|
+
imageCount: de.imageCount,
|
|
2047
|
+
subCompositionCount: de.subCompositionCount,
|
|
2048
|
+
audioGroupCount: de.audioGroupCount,
|
|
2049
|
+
colorGradingCount: de.colorGradingCount,
|
|
2050
|
+
hasLut: de.hasLut,
|
|
2051
|
+
rootBodyMismatch: de.rootBodyMismatch,
|
|
2052
|
+
rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
|
|
2053
|
+
vfxHostCount: de.vfxHostCount,
|
|
2054
|
+
vfxCapture: de.vfxCapture,
|
|
2055
|
+
vfxTypes: de.vfxTypes,
|
|
2056
|
+
shortBand: de.shortBand,
|
|
2057
|
+
parallelRouter: de.parallelRouter ?? "none",
|
|
2058
|
+
preRouterWorkers: de.preRouterWorkers,
|
|
2059
|
+
gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
|
|
2060
|
+
gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
|
|
2061
|
+
workerEncode: perfs.some((p) => p.deWorkerEncode),
|
|
2062
|
+
verifyArmed: perfs.reduce((sum2, p) => sum2 + (p.deVerifyArmed ?? 0), 0),
|
|
2063
|
+
verifyChecked: drain?.verifyChecked ?? 0,
|
|
2064
|
+
verifyMinDb: roundDb(drain?.verifyMinDb),
|
|
2065
|
+
verifyInitMs: perfs.reduce((sum2, p) => sum2 + (p.deVerifyInitMs ?? 0), 0),
|
|
2066
|
+
selfVerifyFallback: de.selfVerifyFallback,
|
|
2067
|
+
fallbackReason: de.fallbackReason,
|
|
2068
|
+
fallbackFailedDb: roundDb(de.fallbackFailedDb),
|
|
2069
|
+
fallbackFrameIndex: de.fallbackFrameIndex,
|
|
2070
|
+
fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
|
|
2071
|
+
blankSuspects: drain?.blankSuspects ?? 0,
|
|
2072
|
+
blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
|
|
2073
|
+
blankRecaptures: drain?.blankRecaptures ?? 0,
|
|
2074
|
+
boundaryFrames: perfs.reduce((sum2, p) => sum2 + (p.deBoundaryFrames ?? 0), 0),
|
|
2075
|
+
ncprFallbacks: perfs.reduce((sum2, p) => sum2 + (p.deNcprFallbacks ?? 0), 0),
|
|
2076
|
+
frameTimeouts: perfs.reduce((sum2, p) => sum2 + (p.deFrameTimeouts ?? 0), 0)
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
function maxDefined(values) {
|
|
2080
|
+
const present = values.filter((v) => typeof v === "number");
|
|
2081
|
+
return present.length > 0 ? Math.max(...present) : void 0;
|
|
2082
|
+
}
|
|
2083
|
+
function aggregateChromeMemory(perfs) {
|
|
2084
|
+
const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
|
|
2085
|
+
if (sampled.length === 0) return void 0;
|
|
2086
|
+
return {
|
|
2087
|
+
browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
|
|
2088
|
+
rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
|
|
2089
|
+
// Last samples are per session; summing approximates the whole fleet's
|
|
2090
|
+
// footprint at the end of capture (workers run concurrently).
|
|
2091
|
+
rssLastMb: sampled.reduce((sum2, p) => sum2 + (p.chromeRssLastMb ?? 0), 0),
|
|
2092
|
+
gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
|
|
2093
|
+
samples: sampled.reduce((sum2, p) => sum2 + (p.chromeMemorySamples ?? 0), 0)
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
function aggregateDedup(perfs) {
|
|
2097
|
+
if (perfs.length === 0) return void 0;
|
|
2098
|
+
const armed = perfs.some((p) => p.staticDedupArmed);
|
|
2099
|
+
const skipReasons = armed ? [] : [
|
|
2100
|
+
...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
|
|
2101
|
+
].sort();
|
|
2102
|
+
const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
|
|
2103
|
+
const verificationOutcomes = [
|
|
2104
|
+
...new Set(
|
|
2105
|
+
verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
|
|
2106
|
+
)
|
|
2107
|
+
].sort();
|
|
2108
|
+
return {
|
|
2109
|
+
enabled: perfs.some((p) => p.staticDedupEnabled),
|
|
2110
|
+
armed,
|
|
2111
|
+
predictedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupPredicted ?? 0), 0),
|
|
2112
|
+
reusedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupReused ?? 0), 0),
|
|
2113
|
+
skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
|
|
2114
|
+
...verificationPerfs.length === 0 ? {} : {
|
|
2115
|
+
verifiedFrames: verificationPerfs.reduce(
|
|
2116
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerified ?? 0),
|
|
2117
|
+
0
|
|
2118
|
+
),
|
|
2119
|
+
verificationOutcomes,
|
|
2120
|
+
plannedRuns: verificationPerfs.reduce(
|
|
2121
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationPlannedRuns ?? 0),
|
|
2122
|
+
0
|
|
2123
|
+
),
|
|
2124
|
+
completedRuns: verificationPerfs.reduce(
|
|
2125
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationCompletedRuns ?? 0),
|
|
2126
|
+
0
|
|
2127
|
+
),
|
|
2128
|
+
screenshots: verificationPerfs.reduce(
|
|
2129
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationScreenshots ?? 0),
|
|
2130
|
+
0
|
|
2131
|
+
),
|
|
2132
|
+
seeks: verificationPerfs.reduce(
|
|
2133
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationSeeks ?? 0),
|
|
2134
|
+
0
|
|
2135
|
+
),
|
|
2136
|
+
comparisons: verificationPerfs.reduce(
|
|
2137
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationComparisons ?? 0),
|
|
2138
|
+
0
|
|
2139
|
+
),
|
|
2140
|
+
verificationElapsedMs: verificationPerfs.reduce(
|
|
2141
|
+
(sum2, perf) => sum2 + (perf.staticDedupVerificationElapsedMs ?? 0),
|
|
2142
|
+
0
|
|
2143
|
+
)
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
function aggregateBeginFrameReuse(perfs) {
|
|
2148
|
+
const noDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameNoDamage ?? 0), 0);
|
|
2149
|
+
const hasDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameHasDamage ?? 0), 0);
|
|
2150
|
+
if (noDamageFrames + hasDamageFrames === 0) return void 0;
|
|
2151
|
+
return { noDamageFrames, hasDamageFrames };
|
|
2152
|
+
}
|
|
2153
|
+
function buildRenderPerfSummary(input) {
|
|
2154
|
+
return {
|
|
2155
|
+
renderId: input.job.id,
|
|
2156
|
+
totalElapsedMs: input.totalElapsedMs,
|
|
2157
|
+
// RenderPerfSummary surfaces fps as a decimal because it lands in JSON
|
|
2158
|
+
// payloads (CLI telemetry, regression-harness reports) where a single
|
|
2159
|
+
// number is friendlier than `{num,den}`. Callers needing the rational
|
|
2160
|
+
// back can read `job.config.fps`.
|
|
2161
|
+
fps: fpsToNumber3(input.job.config.fps),
|
|
2162
|
+
quality: input.job.config.quality,
|
|
2163
|
+
workers: input.workerCount,
|
|
2164
|
+
workerSizing: input.workerSizing,
|
|
2165
|
+
chunkedEncode: input.enableChunkedEncode,
|
|
2166
|
+
chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
|
|
2167
|
+
compositionDurationSeconds: input.compositionDurationSeconds,
|
|
2168
|
+
totalFrames: input.totalFrames,
|
|
2169
|
+
resolution: { width: input.outputWidth, height: input.outputHeight },
|
|
2170
|
+
videoCount: input.videoCount,
|
|
2171
|
+
audioCount: input.audioCount,
|
|
2172
|
+
stages: input.perfStages,
|
|
2173
|
+
videoExtractBreakdown: input.videoExtractBreakdown,
|
|
2174
|
+
tmpPeakBytes: input.tmpPeakBytes,
|
|
2175
|
+
captureCalibration: input.captureCalibration ? {
|
|
2176
|
+
sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
|
|
2177
|
+
p95Ms: input.captureCalibration.estimate.p95Ms,
|
|
2178
|
+
multiplier: input.captureCalibration.estimate.multiplier,
|
|
2179
|
+
reasons: input.captureCalibration.estimate.reasons
|
|
2180
|
+
} : void 0,
|
|
2181
|
+
captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
|
|
2182
|
+
hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
|
|
2183
|
+
hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
|
|
2184
|
+
observability: input.observability,
|
|
2185
|
+
captureAvgMs: input.totalFrames > 0 ? Math.round(
|
|
2186
|
+
(input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
|
|
2187
|
+
) : void 0,
|
|
2188
|
+
subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
|
|
2189
|
+
captureP50Ms: (() => {
|
|
2190
|
+
const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
|
|
2191
|
+
if (withSamples.length === 0) return void 0;
|
|
2192
|
+
return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
|
|
2193
|
+
})(),
|
|
2194
|
+
peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
|
|
2195
|
+
peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
|
|
2196
|
+
staticDedup: aggregateDedup(input.dedupPerfs),
|
|
2197
|
+
chromeMemory: aggregateChromeMemory(input.dedupPerfs),
|
|
2198
|
+
beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
|
|
2199
|
+
drawElement: aggregateDrawElement(
|
|
2200
|
+
input.dedupPerfs,
|
|
2201
|
+
input.drawElement ?? { selfVerifyFallback: false }
|
|
2202
|
+
),
|
|
2203
|
+
host: {
|
|
2204
|
+
platform: platform(),
|
|
2205
|
+
arch: arch(),
|
|
2206
|
+
cpuCount: cpus().length,
|
|
2207
|
+
totalMemMb: Math.round(totalmem() / (1024 * 1024)),
|
|
2208
|
+
nodeVersion: process.version,
|
|
2209
|
+
gpuDisabled: input.gpuDisabled
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// src/services/render/captureStageError.ts
|
|
2215
|
+
var CaptureStageError = class extends CaptureFailure {
|
|
2216
|
+
browserConsole;
|
|
2217
|
+
constructor(input) {
|
|
2218
|
+
const classified = classifyCaptureFailure(input.cause);
|
|
2219
|
+
super({
|
|
2220
|
+
kind: classified.kind,
|
|
2221
|
+
message: normalizeErrorMessage(input.cause),
|
|
2222
|
+
cause: input.cause,
|
|
2223
|
+
workerDiagnostics: classified.workerDiagnostics,
|
|
2224
|
+
pageErrorLines: classified.pageErrorLines
|
|
2225
|
+
});
|
|
2226
|
+
this.name = "CaptureStageError";
|
|
2227
|
+
this.browserConsole = withWorkerPageErrors(input.browserConsole, [
|
|
2228
|
+
...classified.pageErrorLines
|
|
2229
|
+
]);
|
|
2230
|
+
if (input.cause instanceof Error && input.cause.stack) {
|
|
2231
|
+
this.stack = input.cause.stack;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
function wrapCaptureStageError(error, browserConsole) {
|
|
2236
|
+
if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
|
|
2237
|
+
return new CaptureStageError({ cause: error, browserConsole });
|
|
2238
|
+
}
|
|
2239
|
+
function getCaptureStageBrowserConsole(error) {
|
|
2240
|
+
if (error instanceof CaptureStageError) return error.browserConsole;
|
|
2241
|
+
if (error instanceof CaptureFailure) return [...error.pageErrorLines];
|
|
2242
|
+
return [];
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
// src/services/render/stages/captureHdrFrameShared.ts
|
|
2246
|
+
import {
|
|
2247
|
+
applyDomLayerMask as applyDomLayerMask2,
|
|
2248
|
+
blitRgba8OverRgb48le as blitRgba8OverRgb48le2,
|
|
2249
|
+
captureAlphaPng as captureAlphaPng2,
|
|
2250
|
+
decodePng as decodePng2,
|
|
2251
|
+
queryElementStacking as queryElementStacking2,
|
|
2252
|
+
removeDomLayerMask as removeDomLayerMask2
|
|
2253
|
+
} from "@hyperframes/engine";
|
|
2254
|
+
|
|
2054
2255
|
// src/services/hdrCompositor.ts
|
|
2256
|
+
import { readSync as readSync2, closeSync as closeSync3 } from "fs";
|
|
2257
|
+
import { join as join6 } from "path";
|
|
2258
|
+
import {
|
|
2259
|
+
captureAlphaPng,
|
|
2260
|
+
applyDomLayerMask,
|
|
2261
|
+
removeDomLayerMask,
|
|
2262
|
+
decodePng,
|
|
2263
|
+
blitRgba8OverRgb48le,
|
|
2264
|
+
blitRgb48leRegion,
|
|
2265
|
+
groupIntoLayers,
|
|
2266
|
+
blitRgb48leAffine,
|
|
2267
|
+
parseTransformMatrix,
|
|
2268
|
+
convertTransfer,
|
|
2269
|
+
isVideoHiddenBeforeStreamStart
|
|
2270
|
+
} from "@hyperframes/engine";
|
|
2055
2271
|
function countNonZeroAlpha(rgba) {
|
|
2056
2272
|
let n = 0;
|
|
2057
2273
|
for (let p = 3; p < rgba.length; p += 4) {
|
|
@@ -2518,7 +2734,7 @@ import {
|
|
|
2518
2734
|
resolveVideoExtractionWindow,
|
|
2519
2735
|
runFfmpeg
|
|
2520
2736
|
} from "@hyperframes/engine";
|
|
2521
|
-
import { fpsToFfmpegArg, fpsToNumber as
|
|
2737
|
+
import { fpsToFfmpegArg, fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
|
|
2522
2738
|
var NO_FOLLOW_FLAG = constants2.O_NOFOLLOW ?? 0;
|
|
2523
2739
|
function tempDirSafePrefix(id) {
|
|
2524
2740
|
const safe = id.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
|
|
@@ -2754,7 +2970,7 @@ async function extractHdrVideoFrames(args) {
|
|
|
2754
2970
|
const { estimatedBytes, budgetBytes } = assertHdrExtractionDiskHeadroom(
|
|
2755
2971
|
framesDir,
|
|
2756
2972
|
plannedVideos,
|
|
2757
|
-
|
|
2973
|
+
fpsToNumber4(job.config.fps),
|
|
2758
2974
|
log
|
|
2759
2975
|
);
|
|
2760
2976
|
const releaseReservation = reserveHdrExtractionBytes(estimatedBytes, budgetBytes);
|
|
@@ -3122,307 +3338,97 @@ function cleanupEndedHdrVideos(args) {
|
|
|
3122
3338
|
if (!stillNeeded) {
|
|
3123
3339
|
const frameSource = hdrVideoFrameSources.get(videoId);
|
|
3124
3340
|
if (frameSource) {
|
|
3125
|
-
cleanupHdrVideoFrameSource(frameSource, log);
|
|
3126
|
-
hdrVideoFrameSources.delete(videoId);
|
|
3127
|
-
}
|
|
3128
|
-
cleanedUpVideos.add(videoId);
|
|
3129
|
-
}
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
// src/services/render/segmentPlan.ts
|
|
3135
|
-
var DEFAULT_SEGMENT_FRAMES = 3e3;
|
|
3136
|
-
var MIN_SEGMENT_FRAMES = 30;
|
|
3137
|
-
function assertPositiveInteger(name, value) {
|
|
3138
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
3139
|
-
throw new Error(`[segmentPlan] ${name} must be a positive integer (got ${String(value)})`);
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
function planSegments(totalFrames, segmentFrames) {
|
|
3143
|
-
assertPositiveInteger("totalFrames", totalFrames);
|
|
3144
|
-
assertPositiveInteger("segmentFrames", segmentFrames);
|
|
3145
|
-
const slices = [];
|
|
3146
|
-
for (let start = 0, index = 0; start < totalFrames; start += segmentFrames, index += 1) {
|
|
3147
|
-
slices.push({
|
|
3148
|
-
index,
|
|
3149
|
-
startFrame: start,
|
|
3150
|
-
endFrame: Math.min(totalFrames, start + segmentFrames)
|
|
3151
|
-
});
|
|
3152
|
-
}
|
|
3153
|
-
return slices;
|
|
3154
|
-
}
|
|
3155
|
-
function resolveSegmentFrames(env) {
|
|
3156
|
-
const raw = env.HF_SEGMENT_FRAMES;
|
|
3157
|
-
if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_FRAMES;
|
|
3158
|
-
const parsed = Number(raw);
|
|
3159
|
-
if (!Number.isInteger(parsed)) return DEFAULT_SEGMENT_FRAMES;
|
|
3160
|
-
return Math.max(MIN_SEGMENT_FRAMES, parsed);
|
|
3161
|
-
}
|
|
3162
|
-
|
|
3163
|
-
// src/services/render/segmentQueue.ts
|
|
3164
|
-
function createSegmentQueue(slices) {
|
|
3165
|
-
let cursor = 0;
|
|
3166
|
-
return {
|
|
3167
|
-
next: () => slices[cursor++],
|
|
3168
|
-
remaining: () => Math.max(0, slices.length - cursor)
|
|
3169
|
-
};
|
|
3170
|
-
}
|
|
3171
|
-
|
|
3172
|
-
// src/services/render/segmentRecycle.ts
|
|
3173
|
-
var DEFAULT_SEGMENT_BROWSER_RECYCLE = 3;
|
|
3174
|
-
function resolveSegmentBrowserRecycle(env) {
|
|
3175
|
-
const raw = env.HF_SEGMENT_BROWSER_RECYCLE;
|
|
3176
|
-
if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_BROWSER_RECYCLE;
|
|
3177
|
-
const parsed = Number(raw);
|
|
3178
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_SEGMENT_BROWSER_RECYCLE;
|
|
3179
|
-
}
|
|
3180
|
-
var TARGET_LOSS_PATTERNS = [/target closed/i, /detached frame/i, /session closed/i];
|
|
3181
|
-
function isTargetLossError(error) {
|
|
3182
|
-
if (!(error instanceof Error)) return false;
|
|
3183
|
-
return TARGET_LOSS_PATTERNS.some((re) => re.test(error.message));
|
|
3184
|
-
}
|
|
3185
|
-
|
|
3186
|
-
// src/services/render/stages/captureStreamingStage.ts
|
|
3187
|
-
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3188
|
-
import { tmpdir } from "node:os";
|
|
3189
|
-
import { join as join8 } from "node:path";
|
|
3190
|
-
import {
|
|
3191
|
-
DrawElementVerificationError,
|
|
3192
|
-
captureFrameToBuffer,
|
|
3193
|
-
captureFrameToBufferPipelined,
|
|
3194
|
-
captureFramesBatchPipelined,
|
|
3195
|
-
closeCaptureSession,
|
|
3196
|
-
createCaptureSession,
|
|
3197
|
-
createFrameReorderBuffer,
|
|
3198
|
-
distributeFrames,
|
|
3199
|
-
distributeFramesInterleaved,
|
|
3200
|
-
executeParallelCapture,
|
|
3201
|
-
getCapturePerfSummary,
|
|
3202
|
-
psnrDb,
|
|
3203
|
-
resolveDeVerifyMinDb,
|
|
3204
|
-
recaptureDrawElementFrameForVerify,
|
|
3205
|
-
completeDeferredDrawElementInit,
|
|
3206
|
-
initializeSession,
|
|
3207
|
-
prepareCaptureSessionForReuse,
|
|
3208
|
-
spawnStreamingEncoder
|
|
3209
|
-
} from "@hyperframes/engine";
|
|
3210
|
-
|
|
3211
|
-
// src/services/render/perfSummary.ts
|
|
3212
|
-
import { arch, cpus, platform, totalmem } from "node:os";
|
|
3213
|
-
import { fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
|
|
3214
|
-
function worstSubTimelineWaitOutcome(perfs) {
|
|
3215
|
-
const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
|
|
3216
|
-
if (outcomes.length === 0) return void 0;
|
|
3217
|
-
if (outcomes.includes("script_failure")) return "script_failure";
|
|
3218
|
-
if (outcomes.includes("timeout")) return "timeout";
|
|
3219
|
-
return "ready";
|
|
3220
|
-
}
|
|
3221
|
-
function pushWorkerDedupPerfs(results, sink) {
|
|
3222
|
-
for (const r of results) {
|
|
3223
|
-
if (r.perf) sink.push(r.perf);
|
|
3224
|
-
}
|
|
3225
|
-
}
|
|
3226
|
-
function roundDb(value) {
|
|
3227
|
-
return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
|
|
3228
|
-
}
|
|
3229
|
-
function aggregateDrawElement(perfs, de) {
|
|
3230
|
-
if (perfs.length === 0) return void 0;
|
|
3231
|
-
const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
|
|
3232
|
-
const gateReasons = [
|
|
3233
|
-
...new Set(
|
|
3234
|
-
[de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
|
|
3235
|
-
)
|
|
3236
|
-
].sort();
|
|
3237
|
-
const gpuRenderers = [
|
|
3238
|
-
...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
|
|
3239
|
-
].sort();
|
|
3240
|
-
const drain = de.drainStats;
|
|
3241
|
-
return {
|
|
3242
|
-
mode: modes.join("|") || "unknown",
|
|
3243
|
-
compileGate: de.compileGate,
|
|
3244
|
-
clampReason: de.clampReason,
|
|
3245
|
-
workerInversion: de.workerInversion ?? "none",
|
|
3246
|
-
preInversionWorkers: de.preInversionWorkers,
|
|
3247
|
-
compositionElementCount: de.compositionElementCount,
|
|
3248
|
-
compositionElementCountSource: de.compositionElementCountSource,
|
|
3249
|
-
compositionElementTags: de.compositionElementTags,
|
|
3250
|
-
arollVideoCount: de.arollVideoCount,
|
|
3251
|
-
heygenVideoCount: de.heygenVideoCount,
|
|
3252
|
-
adaptersUsed: de.adaptersUsed,
|
|
3253
|
-
audioCount: de.audioCount,
|
|
3254
|
-
imageCount: de.imageCount,
|
|
3255
|
-
subCompositionCount: de.subCompositionCount,
|
|
3256
|
-
audioGroupCount: de.audioGroupCount,
|
|
3257
|
-
colorGradingCount: de.colorGradingCount,
|
|
3258
|
-
hasLut: de.hasLut,
|
|
3259
|
-
rootBodyMismatch: de.rootBodyMismatch,
|
|
3260
|
-
rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
|
|
3261
|
-
vfxHostCount: de.vfxHostCount,
|
|
3262
|
-
vfxCapture: de.vfxCapture,
|
|
3263
|
-
vfxTypes: de.vfxTypes,
|
|
3264
|
-
shortBand: de.shortBand,
|
|
3265
|
-
parallelRouter: de.parallelRouter ?? "none",
|
|
3266
|
-
preRouterWorkers: de.preRouterWorkers,
|
|
3267
|
-
gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
|
|
3268
|
-
gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
|
|
3269
|
-
workerEncode: perfs.some((p) => p.deWorkerEncode),
|
|
3270
|
-
verifyArmed: perfs.reduce((sum2, p) => sum2 + (p.deVerifyArmed ?? 0), 0),
|
|
3271
|
-
verifyChecked: drain?.verifyChecked ?? 0,
|
|
3272
|
-
verifyMinDb: roundDb(drain?.verifyMinDb),
|
|
3273
|
-
verifyInitMs: perfs.reduce((sum2, p) => sum2 + (p.deVerifyInitMs ?? 0), 0),
|
|
3274
|
-
selfVerifyFallback: de.selfVerifyFallback,
|
|
3275
|
-
fallbackReason: de.fallbackReason,
|
|
3276
|
-
fallbackFailedDb: roundDb(de.fallbackFailedDb),
|
|
3277
|
-
fallbackFrameIndex: de.fallbackFrameIndex,
|
|
3278
|
-
fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
|
|
3279
|
-
blankSuspects: drain?.blankSuspects ?? 0,
|
|
3280
|
-
blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
|
|
3281
|
-
blankRecaptures: drain?.blankRecaptures ?? 0,
|
|
3282
|
-
boundaryFrames: perfs.reduce((sum2, p) => sum2 + (p.deBoundaryFrames ?? 0), 0),
|
|
3283
|
-
ncprFallbacks: perfs.reduce((sum2, p) => sum2 + (p.deNcprFallbacks ?? 0), 0),
|
|
3284
|
-
frameTimeouts: perfs.reduce((sum2, p) => sum2 + (p.deFrameTimeouts ?? 0), 0)
|
|
3285
|
-
};
|
|
3286
|
-
}
|
|
3287
|
-
function maxDefined(values) {
|
|
3288
|
-
const present = values.filter((v) => typeof v === "number");
|
|
3289
|
-
return present.length > 0 ? Math.max(...present) : void 0;
|
|
3290
|
-
}
|
|
3291
|
-
function aggregateChromeMemory(perfs) {
|
|
3292
|
-
const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
|
|
3293
|
-
if (sampled.length === 0) return void 0;
|
|
3294
|
-
return {
|
|
3295
|
-
browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
|
|
3296
|
-
rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
|
|
3297
|
-
// Last samples are per session; summing approximates the whole fleet's
|
|
3298
|
-
// footprint at the end of capture (workers run concurrently).
|
|
3299
|
-
rssLastMb: sampled.reduce((sum2, p) => sum2 + (p.chromeRssLastMb ?? 0), 0),
|
|
3300
|
-
gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
|
|
3301
|
-
samples: sampled.reduce((sum2, p) => sum2 + (p.chromeMemorySamples ?? 0), 0)
|
|
3302
|
-
};
|
|
3303
|
-
}
|
|
3304
|
-
function aggregateDedup(perfs) {
|
|
3305
|
-
if (perfs.length === 0) return void 0;
|
|
3306
|
-
const armed = perfs.some((p) => p.staticDedupArmed);
|
|
3307
|
-
const skipReasons = armed ? [] : [
|
|
3308
|
-
...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
|
|
3309
|
-
].sort();
|
|
3310
|
-
const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
|
|
3311
|
-
const verificationOutcomes = [
|
|
3312
|
-
...new Set(
|
|
3313
|
-
verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
|
|
3314
|
-
)
|
|
3315
|
-
].sort();
|
|
3316
|
-
return {
|
|
3317
|
-
enabled: perfs.some((p) => p.staticDedupEnabled),
|
|
3318
|
-
armed,
|
|
3319
|
-
predictedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupPredicted ?? 0), 0),
|
|
3320
|
-
reusedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupReused ?? 0), 0),
|
|
3321
|
-
skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
|
|
3322
|
-
...verificationPerfs.length === 0 ? {} : {
|
|
3323
|
-
verifiedFrames: verificationPerfs.reduce(
|
|
3324
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerified ?? 0),
|
|
3325
|
-
0
|
|
3326
|
-
),
|
|
3327
|
-
verificationOutcomes,
|
|
3328
|
-
plannedRuns: verificationPerfs.reduce(
|
|
3329
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationPlannedRuns ?? 0),
|
|
3330
|
-
0
|
|
3331
|
-
),
|
|
3332
|
-
completedRuns: verificationPerfs.reduce(
|
|
3333
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationCompletedRuns ?? 0),
|
|
3334
|
-
0
|
|
3335
|
-
),
|
|
3336
|
-
screenshots: verificationPerfs.reduce(
|
|
3337
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationScreenshots ?? 0),
|
|
3338
|
-
0
|
|
3339
|
-
),
|
|
3340
|
-
seeks: verificationPerfs.reduce(
|
|
3341
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationSeeks ?? 0),
|
|
3342
|
-
0
|
|
3343
|
-
),
|
|
3344
|
-
comparisons: verificationPerfs.reduce(
|
|
3345
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationComparisons ?? 0),
|
|
3346
|
-
0
|
|
3347
|
-
),
|
|
3348
|
-
verificationElapsedMs: verificationPerfs.reduce(
|
|
3349
|
-
(sum2, perf) => sum2 + (perf.staticDedupVerificationElapsedMs ?? 0),
|
|
3350
|
-
0
|
|
3351
|
-
)
|
|
3341
|
+
cleanupHdrVideoFrameSource(frameSource, log);
|
|
3342
|
+
hdrVideoFrameSources.delete(videoId);
|
|
3343
|
+
}
|
|
3344
|
+
cleanedUpVideos.add(videoId);
|
|
3345
|
+
}
|
|
3352
3346
|
}
|
|
3353
|
-
}
|
|
3347
|
+
}
|
|
3354
3348
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3349
|
+
|
|
3350
|
+
// src/services/render/segmentPlan.ts
|
|
3351
|
+
var DEFAULT_SEGMENT_FRAMES = 3e3;
|
|
3352
|
+
var MIN_SEGMENT_FRAMES = 30;
|
|
3353
|
+
function assertPositiveInteger(name, value) {
|
|
3354
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
3355
|
+
throw new Error(`[segmentPlan] ${name} must be a positive integer (got ${String(value)})`);
|
|
3356
|
+
}
|
|
3360
3357
|
}
|
|
3361
|
-
function
|
|
3358
|
+
function planSegments(totalFrames, segmentFrames) {
|
|
3359
|
+
assertPositiveInteger("totalFrames", totalFrames);
|
|
3360
|
+
assertPositiveInteger("segmentFrames", segmentFrames);
|
|
3361
|
+
const slices = [];
|
|
3362
|
+
for (let start = 0, index = 0; start < totalFrames; start += segmentFrames, index += 1) {
|
|
3363
|
+
slices.push({
|
|
3364
|
+
index,
|
|
3365
|
+
startFrame: start,
|
|
3366
|
+
endFrame: Math.min(totalFrames, start + segmentFrames)
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
return slices;
|
|
3370
|
+
}
|
|
3371
|
+
function resolveSegmentFrames(env) {
|
|
3372
|
+
const raw = env.HF_SEGMENT_FRAMES;
|
|
3373
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_FRAMES;
|
|
3374
|
+
const parsed = Number(raw);
|
|
3375
|
+
if (!Number.isInteger(parsed)) return DEFAULT_SEGMENT_FRAMES;
|
|
3376
|
+
return Math.max(MIN_SEGMENT_FRAMES, parsed);
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
// src/services/render/segmentQueue.ts
|
|
3380
|
+
function createSegmentQueue(slices) {
|
|
3381
|
+
let cursor = 0;
|
|
3362
3382
|
return {
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
// RenderPerfSummary surfaces fps as a decimal because it lands in JSON
|
|
3366
|
-
// payloads (CLI telemetry, regression-harness reports) where a single
|
|
3367
|
-
// number is friendlier than `{num,den}`. Callers needing the rational
|
|
3368
|
-
// back can read `job.config.fps`.
|
|
3369
|
-
fps: fpsToNumber4(input.job.config.fps),
|
|
3370
|
-
quality: input.job.config.quality,
|
|
3371
|
-
workers: input.workerCount,
|
|
3372
|
-
workerSizing: input.workerSizing,
|
|
3373
|
-
chunkedEncode: input.enableChunkedEncode,
|
|
3374
|
-
chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
|
|
3375
|
-
compositionDurationSeconds: input.compositionDurationSeconds,
|
|
3376
|
-
totalFrames: input.totalFrames,
|
|
3377
|
-
resolution: { width: input.outputWidth, height: input.outputHeight },
|
|
3378
|
-
videoCount: input.videoCount,
|
|
3379
|
-
audioCount: input.audioCount,
|
|
3380
|
-
stages: input.perfStages,
|
|
3381
|
-
videoExtractBreakdown: input.videoExtractBreakdown,
|
|
3382
|
-
tmpPeakBytes: input.tmpPeakBytes,
|
|
3383
|
-
captureCalibration: input.captureCalibration ? {
|
|
3384
|
-
sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
|
|
3385
|
-
p95Ms: input.captureCalibration.estimate.p95Ms,
|
|
3386
|
-
multiplier: input.captureCalibration.estimate.multiplier,
|
|
3387
|
-
reasons: input.captureCalibration.estimate.reasons
|
|
3388
|
-
} : void 0,
|
|
3389
|
-
captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
|
|
3390
|
-
hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
|
|
3391
|
-
hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
|
|
3392
|
-
observability: input.observability,
|
|
3393
|
-
captureAvgMs: input.totalFrames > 0 ? Math.round(
|
|
3394
|
-
(input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
|
|
3395
|
-
) : void 0,
|
|
3396
|
-
subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
|
|
3397
|
-
captureP50Ms: (() => {
|
|
3398
|
-
const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
|
|
3399
|
-
if (withSamples.length === 0) return void 0;
|
|
3400
|
-
return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
|
|
3401
|
-
})(),
|
|
3402
|
-
peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
|
|
3403
|
-
peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
|
|
3404
|
-
staticDedup: aggregateDedup(input.dedupPerfs),
|
|
3405
|
-
chromeMemory: aggregateChromeMemory(input.dedupPerfs),
|
|
3406
|
-
beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
|
|
3407
|
-
drawElement: aggregateDrawElement(
|
|
3408
|
-
input.dedupPerfs,
|
|
3409
|
-
input.drawElement ?? { selfVerifyFallback: false }
|
|
3410
|
-
),
|
|
3411
|
-
host: {
|
|
3412
|
-
platform: platform(),
|
|
3413
|
-
arch: arch(),
|
|
3414
|
-
cpuCount: cpus().length,
|
|
3415
|
-
totalMemMb: Math.round(totalmem() / (1024 * 1024)),
|
|
3416
|
-
nodeVersion: process.version,
|
|
3417
|
-
gpuDisabled: input.gpuDisabled
|
|
3418
|
-
}
|
|
3383
|
+
next: () => slices[cursor++],
|
|
3384
|
+
remaining: () => Math.max(0, slices.length - cursor)
|
|
3419
3385
|
};
|
|
3420
3386
|
}
|
|
3421
3387
|
|
|
3388
|
+
// src/services/render/segmentRecycle.ts
|
|
3389
|
+
var DEFAULT_SEGMENT_BROWSER_RECYCLE = 3;
|
|
3390
|
+
function resolveSegmentBrowserRecycle(env) {
|
|
3391
|
+
const raw = env.HF_SEGMENT_BROWSER_RECYCLE;
|
|
3392
|
+
if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_BROWSER_RECYCLE;
|
|
3393
|
+
const parsed = Number(raw);
|
|
3394
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_SEGMENT_BROWSER_RECYCLE;
|
|
3395
|
+
}
|
|
3396
|
+
var TARGET_LOSS_PATTERNS = [/target closed/i, /detached frame/i, /session closed/i];
|
|
3397
|
+
function isTargetLossError(error) {
|
|
3398
|
+
if (!(error instanceof Error)) return false;
|
|
3399
|
+
return TARGET_LOSS_PATTERNS.some((re) => re.test(error.message));
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3422
3402
|
// src/services/render/stages/captureStreamingStage.ts
|
|
3403
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3404
|
+
import { tmpdir } from "node:os";
|
|
3405
|
+
import { join as join8 } from "node:path";
|
|
3406
|
+
import {
|
|
3407
|
+
DrawElementVerificationError,
|
|
3408
|
+
captureFrameToBuffer,
|
|
3409
|
+
captureFrameToBufferPipelined,
|
|
3410
|
+
captureFramesBatchPipelined,
|
|
3411
|
+
closeCaptureSession,
|
|
3412
|
+
createCaptureSession,
|
|
3413
|
+
createFrameReorderBuffer,
|
|
3414
|
+
distributeFrames,
|
|
3415
|
+
distributeFramesInterleaved,
|
|
3416
|
+
executeParallelCapture,
|
|
3417
|
+
getCapturePerfSummary,
|
|
3418
|
+
psnrDb,
|
|
3419
|
+
resolveDeVerifyMinDb,
|
|
3420
|
+
recaptureDrawElementFrameForVerify,
|
|
3421
|
+
completeDeferredDrawElementInit,
|
|
3422
|
+
initializeSession,
|
|
3423
|
+
prepareCaptureSessionForReuse,
|
|
3424
|
+
spawnStreamingEncoder
|
|
3425
|
+
} from "@hyperframes/engine";
|
|
3423
3426
|
var DEFAULT_CAPTURE_STALL_MS = 6e4;
|
|
3424
3427
|
var DE_STALL_POLL_MS = 5e3;
|
|
3425
3428
|
var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
|
|
3429
|
+
function poolFailure(error, probeSession) {
|
|
3430
|
+
return wrapCaptureStageError(error, probeSession?.browserConsoleBuffer ?? []);
|
|
3431
|
+
}
|
|
3426
3432
|
var ParallelCaptureStallError = class extends Error {
|
|
3427
3433
|
variant;
|
|
3428
3434
|
constructor(args) {
|
|
@@ -3869,7 +3875,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
3869
3875
|
}
|
|
3870
3876
|
);
|
|
3871
3877
|
} catch (err) {
|
|
3872
|
-
if (parallelDrainError) throw parallelDrainError;
|
|
3878
|
+
if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
|
|
3873
3879
|
if (stalled && abortSignal?.aborted !== true) {
|
|
3874
3880
|
throw new ParallelCaptureStallError({
|
|
3875
3881
|
variant: "pool",
|
|
@@ -3880,13 +3886,15 @@ async function runCaptureStreamingStage(input) {
|
|
|
3880
3886
|
phaseSummary: phaseSummary()
|
|
3881
3887
|
});
|
|
3882
3888
|
}
|
|
3883
|
-
throw err;
|
|
3889
|
+
throw poolFailure(err, probeSession);
|
|
3884
3890
|
} finally {
|
|
3885
3891
|
clearInterval(stallTimer);
|
|
3886
3892
|
abortSignal?.removeEventListener("abort", forwardParentAbort);
|
|
3887
3893
|
}
|
|
3888
|
-
if (parallelDrainError) throw parallelDrainError;
|
|
3894
|
+
if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
|
|
3889
3895
|
pushWorkerDedupPerfs(workerResults, dedupPerfs);
|
|
3896
|
+
const workerPageErrors = [];
|
|
3897
|
+
pushWorkerPageErrors(workerResults, workerPageErrors);
|
|
3890
3898
|
if (parallelGuardRan) {
|
|
3891
3899
|
deDrainStats = parallelStats;
|
|
3892
3900
|
}
|
|
@@ -3896,6 +3904,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
3896
3904
|
await closeCaptureSession(probeSession);
|
|
3897
3905
|
probeSession = null;
|
|
3898
3906
|
}
|
|
3907
|
+
lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
|
|
3899
3908
|
} else {
|
|
3900
3909
|
const videoInjector = createRenderVideoFrameInjector();
|
|
3901
3910
|
const session = probeSession ?? await createCaptureSession(
|
|
@@ -4342,8 +4351,9 @@ async function runCaptureSegmentedStage(input) {
|
|
|
4342
4351
|
|
|
4343
4352
|
// src/services/render/segmentManifest.ts
|
|
4344
4353
|
import { createHash as createHash2 } from "node:crypto";
|
|
4345
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4,
|
|
4354
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4346
4355
|
import { join as join10 } from "node:path";
|
|
4356
|
+
import { renameWithRetry as renameWithRetry2 } from "@hyperframes/engine";
|
|
4347
4357
|
var MANIFEST_FILENAME = "segments.json";
|
|
4348
4358
|
function computeSegmentPlanHash(input) {
|
|
4349
4359
|
const ordered = [
|
|
@@ -4399,7 +4409,7 @@ function writeSegmentManifest(segmentDir, manifest) {
|
|
|
4399
4409
|
mkdirSync4(segmentDir, { recursive: true });
|
|
4400
4410
|
const tmp = join10(segmentDir, `${MANIFEST_FILENAME}.${process.pid}.tmp`);
|
|
4401
4411
|
writeFileSync3(tmp, JSON.stringify(manifest, null, 2), "utf-8");
|
|
4402
|
-
|
|
4412
|
+
renameWithRetry2(tmp, join10(segmentDir, MANIFEST_FILENAME));
|
|
4403
4413
|
}
|
|
4404
4414
|
async function probeSegmentFrameCount(path, probe) {
|
|
4405
4415
|
try {
|
|
@@ -5451,19 +5461,11 @@ import {
|
|
|
5451
5461
|
|
|
5452
5462
|
// src/services/animatedGifPrep.ts
|
|
5453
5463
|
import { createHash as createHash4 } from "node:crypto";
|
|
5454
|
-
import {
|
|
5455
|
-
copyFileSync as copyFileSync2,
|
|
5456
|
-
existsSync as existsSync8,
|
|
5457
|
-
mkdirSync as mkdirSync5,
|
|
5458
|
-
readFileSync as readFileSync5,
|
|
5459
|
-
renameSync as renameSync3,
|
|
5460
|
-
rmSync as rmSync6,
|
|
5461
|
-
statSync as statSync4
|
|
5462
|
-
} from "node:fs";
|
|
5464
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync6, statSync as statSync4 } from "node:fs";
|
|
5463
5465
|
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join12, resolve as resolve5 } from "node:path";
|
|
5464
5466
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
5465
5467
|
import { parseAnimatedGifMetadata } from "@hyperframes/core";
|
|
5466
|
-
import { DEFAULT_VP9_CPU_USED, runFfmpeg as runFfmpeg2 } from "@hyperframes/engine";
|
|
5468
|
+
import { DEFAULT_VP9_CPU_USED, runFfmpeg as runFfmpeg2, renameWithRetry as renameWithRetry3 } from "@hyperframes/engine";
|
|
5467
5469
|
var PREPARED_GIF_SUBDIR = "_animated_gif";
|
|
5468
5470
|
var CACHE_SCHEMA = "hfgif-v1";
|
|
5469
5471
|
function splitUrlSuffix(src) {
|
|
@@ -5642,7 +5644,7 @@ async function ensurePreparedWebm(input) {
|
|
|
5642
5644
|
throw new Error("Animated GIF transcode produced an empty output");
|
|
5643
5645
|
}
|
|
5644
5646
|
if (!isUsableFile(input.cachePath)) {
|
|
5645
|
-
|
|
5647
|
+
renameWithRetry3(tmpPath, input.cachePath);
|
|
5646
5648
|
} else {
|
|
5647
5649
|
rmSync6(tmpPath, { force: true });
|
|
5648
5650
|
}
|
|
@@ -8049,6 +8051,10 @@ async function runProbeStage(input) {
|
|
|
8049
8051
|
if (el.loop && !existing.loop) {
|
|
8050
8052
|
existing.loop = true;
|
|
8051
8053
|
}
|
|
8054
|
+
const audio = composition.audios.find(
|
|
8055
|
+
(audio2) => audio2.type === "video" && audio2.id === `${el.id}-audio`
|
|
8056
|
+
);
|
|
8057
|
+
if (audio) audio.loop = existing.loop;
|
|
8052
8058
|
}
|
|
8053
8059
|
} else if (!compiled.droppedMediaIds.has(el.id)) {
|
|
8054
8060
|
composition.videos.push({
|
|
@@ -8086,6 +8092,7 @@ async function runProbeStage(input) {
|
|
|
8086
8092
|
el,
|
|
8087
8093
|
authoredStarts.get(el.id) ?? existing.mediaStart
|
|
8088
8094
|
);
|
|
8095
|
+
existing.loop = el.loop;
|
|
8089
8096
|
if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) > BROWSER_MEDIA_EPSILON) {
|
|
8090
8097
|
existing.volume = el.volume;
|
|
8091
8098
|
}
|
|
@@ -8097,6 +8104,7 @@ async function runProbeStage(input) {
|
|
|
8097
8104
|
start: el.start,
|
|
8098
8105
|
end: resolveBrowserMediaEnd(el.start, el.end, el.duration),
|
|
8099
8106
|
mediaStart: el.mediaStart,
|
|
8107
|
+
loop: el.loop,
|
|
8100
8108
|
layer: 0,
|
|
8101
8109
|
volume: el.volume,
|
|
8102
8110
|
type: "audio"
|
|
@@ -8351,6 +8359,10 @@ import {
|
|
|
8351
8359
|
extractMediaMetadata as extractMediaMetadata3,
|
|
8352
8360
|
isVideoPastTimelineEnd,
|
|
8353
8361
|
isHdrColorSpace as isHdrColorSpace2,
|
|
8362
|
+
rebaseVideoToWindow,
|
|
8363
|
+
resolveFinalFrameExtractionWindow as resolveFinalFrameExtractionWindow2,
|
|
8364
|
+
resolveVideoExtractionWindow as resolveVideoExtractionWindow2,
|
|
8365
|
+
isHttpUrl as isHttpUrl2,
|
|
8354
8366
|
resolveProjectRelativeSrc as resolveProjectRelativeSrc3,
|
|
8355
8367
|
runVideoExtractionWithRetry,
|
|
8356
8368
|
safeVideoExtractionSourceIdentity
|
|
@@ -8716,6 +8728,8 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
|
|
|
8716
8728
|
start: video.start,
|
|
8717
8729
|
end: video.end,
|
|
8718
8730
|
mediaStart: video.mediaStart,
|
|
8731
|
+
playbackRate: video.playbackRate,
|
|
8732
|
+
loop: video.loop,
|
|
8719
8733
|
layer: 0,
|
|
8720
8734
|
volume: 1,
|
|
8721
8735
|
type: "video"
|
|
@@ -8735,7 +8749,7 @@ import {
|
|
|
8735
8749
|
|
|
8736
8750
|
// src/services/render/audioPadTrim.ts
|
|
8737
8751
|
import { spawn } from "node:child_process";
|
|
8738
|
-
import { mkdtempSync as mkdtempSync3,
|
|
8752
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync7 } from "node:fs";
|
|
8739
8753
|
import { dirname as dirname7, join as join17 } from "node:path";
|
|
8740
8754
|
import {
|
|
8741
8755
|
DEFAULT_CONFIG,
|
|
@@ -8744,6 +8758,7 @@ import {
|
|
|
8744
8758
|
extractAudioMetadata as extractAudioMetadata3,
|
|
8745
8759
|
getFfprobeBinary,
|
|
8746
8760
|
ManagedChildProcess,
|
|
8761
|
+
renameWithRetry as renameWithRetry4,
|
|
8747
8762
|
runFfmpeg as runFfmpeg4,
|
|
8748
8763
|
trackChildProcess
|
|
8749
8764
|
} from "@hyperframes/engine";
|
|
@@ -8999,7 +9014,7 @@ async function enforceAacTruePeak(input) {
|
|
|
8999
9014
|
}
|
|
9000
9015
|
if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
|
|
9001
9016
|
if (measuredPath !== correctedPath) return { success: true };
|
|
9002
|
-
|
|
9017
|
+
renameWithRetry4(correctedPath, input.audioPath);
|
|
9003
9018
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
9004
9019
|
}
|
|
9005
9020
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
@@ -9189,6 +9204,7 @@ async function runAudioStage(input) {
|
|
|
9189
9204
|
const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
|
|
9190
9205
|
let audioLoweredDb;
|
|
9191
9206
|
if (result.hasAudio) {
|
|
9207
|
+
const guardStart = Date.now();
|
|
9192
9208
|
const peak = await constrainAacTruePeak({
|
|
9193
9209
|
audioPath: result.audioOutputPath,
|
|
9194
9210
|
sourcePath: pcmSourcePath,
|
|
@@ -9198,6 +9214,7 @@ async function runAudioStage(input) {
|
|
|
9198
9214
|
});
|
|
9199
9215
|
if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
|
|
9200
9216
|
audioLoweredDb = peak.loweredDb;
|
|
9217
|
+
input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
|
|
9201
9218
|
}
|
|
9202
9219
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
9203
9220
|
} finally {
|
|
@@ -9223,6 +9240,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9223
9240
|
let hasAudio = false;
|
|
9224
9241
|
let audioError;
|
|
9225
9242
|
let audioFailures;
|
|
9243
|
+
let audioPhaseMs;
|
|
9226
9244
|
if (audios.length > 0) {
|
|
9227
9245
|
let audioResult;
|
|
9228
9246
|
try {
|
|
@@ -9272,6 +9290,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9272
9290
|
assertAudioNotInterrupted(audioResult.failures);
|
|
9273
9291
|
hasAudio = audioResult.success;
|
|
9274
9292
|
audioFailures = audioResult.failures;
|
|
9293
|
+
audioPhaseMs = audioResult.phaseMs;
|
|
9275
9294
|
if (!hasAudio) {
|
|
9276
9295
|
audioError = audioResult.error ?? "audio mix failed for an unknown reason";
|
|
9277
9296
|
for (const failure of audioResult.failures ?? []) {
|
|
@@ -9290,7 +9309,8 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9290
9309
|
audioOutputPath,
|
|
9291
9310
|
hasAudio,
|
|
9292
9311
|
audioError,
|
|
9293
|
-
audioFailures
|
|
9312
|
+
audioFailures,
|
|
9313
|
+
audioPhaseMs
|
|
9294
9314
|
};
|
|
9295
9315
|
}
|
|
9296
9316
|
|
|
@@ -9419,6 +9439,7 @@ async function runCaptureStage(input) {
|
|
|
9419
9439
|
let { workerCount } = plan2;
|
|
9420
9440
|
const { forceScreenshot } = plan2;
|
|
9421
9441
|
let lastBrowserConsole = [];
|
|
9442
|
+
const workerPageErrors = [];
|
|
9422
9443
|
let captureBeyondViewport = probeSession?.options.captureBeyondViewport;
|
|
9423
9444
|
const captureCfg = cfg.forceScreenshot === forceScreenshot ? cfg : { ...cfg, forceScreenshot };
|
|
9424
9445
|
if (frameRange !== void 0) {
|
|
@@ -9456,6 +9477,7 @@ async function runCaptureStage(input) {
|
|
|
9456
9477
|
abortSignal,
|
|
9457
9478
|
frameRangeStart: frameRange?.startFrame,
|
|
9458
9479
|
dedupPerfs,
|
|
9480
|
+
pageErrorLines: workerPageErrors,
|
|
9459
9481
|
onProgress: (progress) => {
|
|
9460
9482
|
if (progress.latestWorkerPhase) {
|
|
9461
9483
|
if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
|
|
@@ -9476,6 +9498,11 @@ async function runCaptureStage(input) {
|
|
|
9476
9498
|
},
|
|
9477
9499
|
cfg: captureCfg,
|
|
9478
9500
|
log
|
|
9501
|
+
}).catch((error) => {
|
|
9502
|
+
throw wrapCaptureStageError(
|
|
9503
|
+
error,
|
|
9504
|
+
withWorkerPageErrors(probeSession?.browserConsoleBuffer ?? [], workerPageErrors)
|
|
9505
|
+
);
|
|
9479
9506
|
});
|
|
9480
9507
|
captureAttempts.push(...attempts);
|
|
9481
9508
|
const lastAttempt = attempts[attempts.length - 1];
|
|
@@ -9488,6 +9515,7 @@ async function runCaptureStage(input) {
|
|
|
9488
9515
|
await closeCaptureSession5(probeSession);
|
|
9489
9516
|
probeSession = null;
|
|
9490
9517
|
}
|
|
9518
|
+
lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
|
|
9491
9519
|
} else {
|
|
9492
9520
|
const sequential = await runSequentialDiskCapture(
|
|
9493
9521
|
input,
|
|
@@ -10422,6 +10450,7 @@ async function runCaptureHdrStage(input) {
|
|
|
10422
10450
|
width,
|
|
10423
10451
|
height,
|
|
10424
10452
|
codec: preset.codec,
|
|
10453
|
+
useGpu: job.config.useGpu,
|
|
10425
10454
|
preset: preset.preset,
|
|
10426
10455
|
quality: effectiveQuality,
|
|
10427
10456
|
bitrate: effectiveBitrate,
|
|
@@ -10802,7 +10831,7 @@ function videoEncoderOptions(input, engineCfg) {
|
|
|
10802
10831
|
};
|
|
10803
10832
|
}
|
|
10804
10833
|
function canEncodeWhileCapturing(input, workerCount, cpuCount) {
|
|
10805
|
-
return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg"
|
|
10834
|
+
return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg";
|
|
10806
10835
|
}
|
|
10807
10836
|
async function readLandedFrame(workerPath, mergedPath) {
|
|
10808
10837
|
if (workerPath !== void 0) {
|
|
@@ -11457,6 +11486,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11457
11486
|
options.cfg
|
|
11458
11487
|
);
|
|
11459
11488
|
pushWorkerDedupPerfs(workerResults, options.dedupPerfs);
|
|
11489
|
+
if (options.pageErrorLines) pushWorkerPageErrors(workerResults, options.pageErrorLines);
|
|
11460
11490
|
} finally {
|
|
11461
11491
|
await mergeWorkerFrames(attemptWorkDir, tasks, options.framesDir);
|
|
11462
11492
|
}
|
|
@@ -13855,7 +13885,6 @@ import {
|
|
|
13855
13885
|
existsSync as existsSync21,
|
|
13856
13886
|
linkSync,
|
|
13857
13887
|
mkdirSync as mkdirSync13,
|
|
13858
|
-
renameSync as renameSync6,
|
|
13859
13888
|
rmSync as rmSync12,
|
|
13860
13889
|
writeFileSync as writeFileSync8
|
|
13861
13890
|
} from "node:fs";
|
|
@@ -13867,7 +13896,10 @@ import {
|
|
|
13867
13896
|
import {
|
|
13868
13897
|
getEncoderPreset as getEncoderPreset4,
|
|
13869
13898
|
normalizeVp9CpuUsed,
|
|
13870
|
-
resolveConfig as resolveConfig3
|
|
13899
|
+
resolveConfig as resolveConfig3,
|
|
13900
|
+
declareContentAddressedUrl,
|
|
13901
|
+
renameWithRetry as renameWithRetry6,
|
|
13902
|
+
declareProbeFactsUrl
|
|
13871
13903
|
} from "@hyperframes/engine";
|
|
13872
13904
|
|
|
13873
13905
|
// src/services/distributed/planFrameCost.ts
|
|
@@ -14808,15 +14840,9 @@ import { join as join31 } from "node:path";
|
|
|
14808
14840
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
|
|
14809
14841
|
|
|
14810
14842
|
// src/services/distributed/mp4Stitch.ts
|
|
14811
|
-
import {
|
|
14812
|
-
createReadStream as createReadStream4,
|
|
14813
|
-
createWriteStream,
|
|
14814
|
-
mkdirSync as mkdirSync11,
|
|
14815
|
-
mkdtempSync as mkdtempSync4,
|
|
14816
|
-
renameSync as renameSync5,
|
|
14817
|
-
rmSync as rmSync11
|
|
14818
|
-
} from "node:fs";
|
|
14843
|
+
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync4, rmSync as rmSync11 } from "node:fs";
|
|
14819
14844
|
import { open } from "node:fs/promises";
|
|
14845
|
+
import { renameWithRetry as renameWithRetry5 } from "@hyperframes/engine";
|
|
14820
14846
|
import { dirname as dirname12, join as join30 } from "node:path";
|
|
14821
14847
|
import { finished, pipeline } from "node:stream/promises";
|
|
14822
14848
|
function boxAt(src, off, at, limit) {
|
|
@@ -15189,7 +15215,7 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
15189
15215
|
for (const [i, path] of sourcePaths.entries()) await copyPayload(path, layouts[i], i, out);
|
|
15190
15216
|
out.end();
|
|
15191
15217
|
await finished(out);
|
|
15192
|
-
|
|
15218
|
+
renameWithRetry5(join30(workDir, "out.mp4"), outputPath);
|
|
15193
15219
|
} finally {
|
|
15194
15220
|
out.destroy();
|
|
15195
15221
|
rmSync11(workDir, { recursive: true, force: true });
|
|
@@ -15915,6 +15941,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
15915
15941
|
const needsAlpha = outputNeedsAlpha(config.format);
|
|
15916
15942
|
const remoteFiles = Object.entries(options.remoteFiles ?? {});
|
|
15917
15943
|
const remoteLocalPath = (rel) => join33(projectDir, ...rel.split("/"));
|
|
15944
|
+
for (const [, file] of remoteFiles) {
|
|
15945
|
+
declareContentAddressedUrl(file.url, file.sha256);
|
|
15946
|
+
if (file.probeFactsUrl) declareProbeFactsUrl(file.url, file.probeFactsUrl);
|
|
15947
|
+
}
|
|
15918
15948
|
const urlByAbsentLocalPath = new Map(
|
|
15919
15949
|
remoteFiles.map(([rel, file]) => [remoteLocalPath(rel), file.url])
|
|
15920
15950
|
);
|
|
@@ -16036,7 +16066,8 @@ function mixPlanAudio(c, compiledDir) {
|
|
|
16036
16066
|
audios: c.composition.audios,
|
|
16037
16067
|
masterFxChain: c.composition.masterFxChain,
|
|
16038
16068
|
abortSignal: c.abortSignal,
|
|
16039
|
-
assertNotAborted: c.assertNotAborted
|
|
16069
|
+
assertNotAborted: c.assertNotAborted,
|
|
16070
|
+
log: c.log
|
|
16040
16071
|
});
|
|
16041
16072
|
}
|
|
16042
16073
|
async function extractPlanVideos(c, deferRangeExtraction) {
|
|
@@ -16055,6 +16086,7 @@ async function extractPlanVideos(c, deferRangeExtraction) {
|
|
|
16055
16086
|
if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
|
|
16056
16087
|
if (extractResult.extractionResult) {
|
|
16057
16088
|
assertVideoExtractionSucceeded(extractResult.extractionResult);
|
|
16089
|
+
c.log.info("[plan] extract phases", { ...extractResult.extractionResult.phaseBreakdown });
|
|
16058
16090
|
}
|
|
16059
16091
|
return extractResult;
|
|
16060
16092
|
}
|
|
@@ -16100,12 +16132,12 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
16100
16132
|
const videoSourcesDir = join33(planDir, "video-sources");
|
|
16101
16133
|
rmSync12(videoSourcesDir, { recursive: true, force: true });
|
|
16102
16134
|
if (existsSync21(stagedVideoFrames)) {
|
|
16103
|
-
|
|
16135
|
+
renameWithRetry6(stagedVideoFrames, videoFramesDst);
|
|
16104
16136
|
} else {
|
|
16105
16137
|
mkdirSync13(videoFramesDst, { recursive: true });
|
|
16106
16138
|
}
|
|
16107
16139
|
if (existsSync21(finalCompiledDir)) rmSync12(finalCompiledDir, { recursive: true, force: true });
|
|
16108
|
-
|
|
16140
|
+
renameWithRetry6(compiledDir, finalCompiledDir);
|
|
16109
16141
|
const shipVideoSource = createVideoSourceShipper({
|
|
16110
16142
|
planDir,
|
|
16111
16143
|
projectDir,
|
|
@@ -16208,7 +16240,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
16208
16240
|
}
|
|
16209
16241
|
const planAudioPath = join33(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
16210
16242
|
if (audioResult?.hasAudio && existsSync21(audioResult.audioOutputPath)) {
|
|
16211
|
-
|
|
16243
|
+
renameWithRetry6(audioResult.audioOutputPath, planAudioPath);
|
|
16212
16244
|
}
|
|
16213
16245
|
if (chunksCarryAudio && existsSync21(planAudioPath)) {
|
|
16214
16246
|
if (options.sliceChunkAudio) {
|
|
@@ -17725,7 +17757,6 @@ import {
|
|
|
17725
17757
|
mkdtempSync as mkdtempSync7,
|
|
17726
17758
|
readFileSync as readFileSync13,
|
|
17727
17759
|
readdirSync as readdirSync9,
|
|
17728
|
-
renameSync as renameSync8,
|
|
17729
17760
|
rmSync as rmSync15,
|
|
17730
17761
|
statSync as statSync10,
|
|
17731
17762
|
writeFileSync as writeFileSync11
|
|
@@ -17733,7 +17764,7 @@ import {
|
|
|
17733
17764
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
17734
17765
|
import { dirname as dirname16, isAbsolute as isAbsolute5, join as join39, relative as relative7, resolve as resolve11, sep as sep4 } from "node:path";
|
|
17735
17766
|
import { DISTRIBUTED_FPS as DISTRIBUTED_FPS2, isDistributedFps as isDistributedFps3 } from "@hyperframes/core";
|
|
17736
|
-
import { createFrameLookupTable as createFrameLookupTable4 } from "@hyperframes/engine";
|
|
17767
|
+
import { createFrameLookupTable as createFrameLookupTable4, renameWithRetry as renameWithRetry8 } from "@hyperframes/engine";
|
|
17737
17768
|
|
|
17738
17769
|
// src/services/distributed/planV2Publisher.ts
|
|
17739
17770
|
import {
|
|
@@ -17742,11 +17773,11 @@ import {
|
|
|
17742
17773
|
linkSync as linkSync2,
|
|
17743
17774
|
mkdirSync as mkdirSync15,
|
|
17744
17775
|
mkdtempSync as mkdtempSync6,
|
|
17745
|
-
renameSync as renameSync7,
|
|
17746
17776
|
rmSync as rmSync14,
|
|
17747
17777
|
statSync as statSync9,
|
|
17748
17778
|
writeFileSync as writeFileSync10
|
|
17749
17779
|
} from "node:fs";
|
|
17780
|
+
import { renameWithRetry as renameWithRetry7 } from "@hyperframes/engine";
|
|
17750
17781
|
import { dirname as dirname15, join as join38 } from "node:path";
|
|
17751
17782
|
|
|
17752
17783
|
// src/services/distributed/planV2Errors.ts
|
|
@@ -17835,7 +17866,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
17835
17866
|
if (!canFallbackToCopy(error)) throw error;
|
|
17836
17867
|
copyFileSync6(blob.sourcePath, temporaryPath);
|
|
17837
17868
|
}
|
|
17838
|
-
|
|
17869
|
+
renameWithRetry7(temporaryPath, destinationPath);
|
|
17839
17870
|
} finally {
|
|
17840
17871
|
rmSync14(stagingDir, { recursive: true, force: true });
|
|
17841
17872
|
}
|
|
@@ -17849,7 +17880,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
17849
17880
|
}
|
|
17850
17881
|
}
|
|
17851
17882
|
writeFileSync10(join38(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
|
|
17852
|
-
|
|
17883
|
+
renameWithRetry7(this.temporaryDir, this.destinationDir);
|
|
17853
17884
|
this.#committed = true;
|
|
17854
17885
|
}
|
|
17855
17886
|
async abort() {
|
|
@@ -18116,7 +18147,7 @@ function writeBlob(sourcePath, destinationPath) {
|
|
|
18116
18147
|
const temporaryPath = join39(temporaryDir, "blob");
|
|
18117
18148
|
try {
|
|
18118
18149
|
copyFileSync7(sourcePath, temporaryPath);
|
|
18119
|
-
|
|
18150
|
+
renameWithRetry8(temporaryPath, destinationPath);
|
|
18120
18151
|
} finally {
|
|
18121
18152
|
rmSync15(temporaryDir, { recursive: true, force: true });
|
|
18122
18153
|
}
|
|
@@ -18248,7 +18279,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
18248
18279
|
canonicalJsonStringify(publication.manifest),
|
|
18249
18280
|
"utf-8"
|
|
18250
18281
|
);
|
|
18251
|
-
|
|
18282
|
+
renameWithRetry8(tempDir, planV2Dir);
|
|
18252
18283
|
return resultFromManifest(planV2Dir, publication.manifest);
|
|
18253
18284
|
} catch (error) {
|
|
18254
18285
|
rmSync15(tempDir, { recursive: true, force: true });
|
|
@@ -18533,7 +18564,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
18533
18564
|
}),
|
|
18534
18565
|
"utf-8"
|
|
18535
18566
|
);
|
|
18536
|
-
|
|
18567
|
+
renameWithRetry8(tempDir, destinationDir);
|
|
18537
18568
|
} catch (error) {
|
|
18538
18569
|
rmSync15(tempDir, { recursive: true, force: true });
|
|
18539
18570
|
throw error;
|
|
@@ -18611,7 +18642,6 @@ import {
|
|
|
18611
18642
|
mkdtempSync as mkdtempSync8,
|
|
18612
18643
|
readFileSync as readFileSync14,
|
|
18613
18644
|
readdirSync as readdirSync10,
|
|
18614
|
-
renameSync as renameSync9,
|
|
18615
18645
|
rmSync as rmSync16,
|
|
18616
18646
|
statSync as statSync11,
|
|
18617
18647
|
writeFileSync as writeFileSync12
|
|
@@ -18625,7 +18655,8 @@ import {
|
|
|
18625
18655
|
muxVideoWithAudio as muxVideoWithAudio2,
|
|
18626
18656
|
resolveConfig as resolveConfig6,
|
|
18627
18657
|
runFfmpeg as runFfmpeg7,
|
|
18628
|
-
appendSoftwareEncoderThreadsArg
|
|
18658
|
+
appendSoftwareEncoderThreadsArg,
|
|
18659
|
+
renameWithRetry as renameWithRetry9
|
|
18629
18660
|
} from "@hyperframes/engine";
|
|
18630
18661
|
import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
|
|
18631
18662
|
function cfrRateControlArgs(locked) {
|
|
@@ -18682,7 +18713,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
18682
18713
|
const finalPath = join40(workDir, `output.${format}`);
|
|
18683
18714
|
const audio = audioPath !== null && existsSync27(audioPath) ? audioPath : null;
|
|
18684
18715
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
18685
|
-
|
|
18716
|
+
renameWithRetry9(finalPath, outputPath);
|
|
18686
18717
|
log.info("[assemble] stage timings", {
|
|
18687
18718
|
...stageMs,
|
|
18688
18719
|
chunks: chunkPaths.length,
|
|
@@ -18752,7 +18783,7 @@ function cfrEncodeArgs(inputArgs, encoder, fpsArg) {
|
|
|
18752
18783
|
"-r",
|
|
18753
18784
|
fpsArg
|
|
18754
18785
|
];
|
|
18755
|
-
appendSoftwareEncoderThreadsArg(args);
|
|
18786
|
+
appendSoftwareEncoderThreadsArg(args, "libx264");
|
|
18756
18787
|
return args;
|
|
18757
18788
|
}
|
|
18758
18789
|
async function reencodeCfr(ctx, chunkPaths, encoder) {
|
|
@@ -18934,6 +18965,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
18934
18965
|
// src/services/distributed/renderChunk.ts
|
|
18935
18966
|
import { randomBytes } from "node:crypto";
|
|
18936
18967
|
import { existsSync as existsSync28, mkdirSync as mkdirSync18, readFileSync as readFileSync15, readdirSync as readdirSync11, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
18968
|
+
import { cpus as cpus3 } from "node:os";
|
|
18937
18969
|
import { join as join41 } from "node:path";
|
|
18938
18970
|
import {
|
|
18939
18971
|
assertSwiftShader,
|
|
@@ -19238,6 +19270,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19238
19270
|
let forceScreenshotForChunk = encoder.forceScreenshot;
|
|
19239
19271
|
let shownBoxes;
|
|
19240
19272
|
let extraction;
|
|
19273
|
+
const overlap = { current: null };
|
|
19241
19274
|
try {
|
|
19242
19275
|
if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
|
|
19243
19276
|
const bootStarted = Date.now();
|
|
@@ -19325,6 +19358,59 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19325
19358
|
videoFrameLookup,
|
|
19326
19359
|
frameCacheOptions(cfg, chunkWorkerCount)
|
|
19327
19360
|
);
|
|
19361
|
+
const isPngSequence = plan2.dimensions.format === "png-sequence";
|
|
19362
|
+
outputKind = isPngSequence ? "frame-dir" : "file";
|
|
19363
|
+
const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
|
|
19364
|
+
const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
|
|
19365
|
+
const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
|
|
19366
|
+
const effectiveQuality = encoder.crf ?? preset.quality;
|
|
19367
|
+
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
19368
|
+
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
19369
|
+
if (isPngSequence) {
|
|
19370
|
+
if (!existsSync28(outputChunkPath)) mkdirSync18(outputChunkPath, { recursive: true });
|
|
19371
|
+
} else {
|
|
19372
|
+
const outDir = join41(outputChunkPath, "..");
|
|
19373
|
+
if (!existsSync28(outDir)) mkdirSync18(outDir, { recursive: true });
|
|
19374
|
+
}
|
|
19375
|
+
const encodeInput = {
|
|
19376
|
+
job,
|
|
19377
|
+
log,
|
|
19378
|
+
outputPath: videoOnlyPath,
|
|
19379
|
+
framesDir,
|
|
19380
|
+
videoOnlyPath,
|
|
19381
|
+
width: Math.round(plan2.dimensions.width * encoder.deviceScaleFactor),
|
|
19382
|
+
height: Math.round(plan2.dimensions.height * encoder.deviceScaleFactor),
|
|
19383
|
+
needsAlpha: plan2.dimensions.format !== "mp4",
|
|
19384
|
+
captureImageFormat: captureOptions.format ?? "jpeg",
|
|
19385
|
+
// The encoder writes video only; audio joins at assemble time or in the
|
|
19386
|
+
// chunk that carries audio. Suppressing `hasAudio` skips the png-sequence audio
|
|
19387
|
+
// sidecar AND the mp4 audio mux.
|
|
19388
|
+
hasAudio: false,
|
|
19389
|
+
isPngSequence,
|
|
19390
|
+
// `DistributedFormat` has no "gif" member — distributed chunks are
|
|
19391
|
+
// always video segments (gif renders in-process only).
|
|
19392
|
+
isGif: false,
|
|
19393
|
+
preset,
|
|
19394
|
+
effectiveQuality,
|
|
19395
|
+
effectiveBitrate,
|
|
19396
|
+
engineConfig: {
|
|
19397
|
+
ffmpegEncodeTimeout: cfg.ffmpegEncodeTimeout,
|
|
19398
|
+
vp9CpuUsed: resolveLockedVp9CpuUsed(encoder) ?? cfg.vp9CpuUsed
|
|
19399
|
+
},
|
|
19400
|
+
// Distributed chunks emit a single ffmpeg call per chunk; the
|
|
19401
|
+
// in-process per-chunk-within-chunk path would re-split our
|
|
19402
|
+
// already-chunked work.
|
|
19403
|
+
enableChunkedEncode: false,
|
|
19404
|
+
chunkedEncodeSize: framesInChunk,
|
|
19405
|
+
abortSignal: void 0,
|
|
19406
|
+
assertNotAborted: () => {
|
|
19407
|
+
},
|
|
19408
|
+
// GOP === framesInChunk + force-keyframe at frame 0 → the chunk's
|
|
19409
|
+
// first frame is an IDR keyframe and concat-copy at assemble time
|
|
19410
|
+
// round-trips losslessly.
|
|
19411
|
+
lockGopForChunkConcat: !isPngSequence,
|
|
19412
|
+
gopSize: framesInChunk
|
|
19413
|
+
};
|
|
19328
19414
|
const captureStarted = Date.now();
|
|
19329
19415
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
19330
19416
|
forceScreenshot: forceScreenshotForChunk,
|
|
@@ -19357,6 +19443,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19357
19443
|
}
|
|
19358
19444
|
const captureSession = session;
|
|
19359
19445
|
session = null;
|
|
19446
|
+
overlap.current = canEncodeWhileCapturing(encodeInput, chunkWorkerCount, cpus3().length) ? encodeWhileCapturing(encodeInput, framesInChunk) : null;
|
|
19360
19447
|
await runCaptureStage({
|
|
19361
19448
|
fileServer,
|
|
19362
19449
|
workDir,
|
|
@@ -19376,7 +19463,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19376
19463
|
abortSignal: void 0,
|
|
19377
19464
|
assertNotAborted: () => {
|
|
19378
19465
|
},
|
|
19379
|
-
frameRange: range
|
|
19466
|
+
frameRange: range,
|
|
19467
|
+
onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame)
|
|
19468
|
+
}).catch(async (captureError) => {
|
|
19469
|
+
await overlap.current?.abort();
|
|
19470
|
+
overlap.current = null;
|
|
19471
|
+
throw captureError;
|
|
19380
19472
|
});
|
|
19381
19473
|
return observedChunkCaptureMode(capturePerfs);
|
|
19382
19474
|
},
|
|
@@ -19408,60 +19500,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19408
19500
|
log
|
|
19409
19501
|
);
|
|
19410
19502
|
framesEncoded = framesInChunk;
|
|
19411
|
-
const isPngSequence = plan2.dimensions.format === "png-sequence";
|
|
19412
|
-
outputKind = isPngSequence ? "frame-dir" : "file";
|
|
19413
|
-
const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
|
|
19414
|
-
const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
|
|
19415
|
-
const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
|
|
19416
|
-
const effectiveQuality = encoder.crf ?? preset.quality;
|
|
19417
|
-
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
19418
|
-
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
19419
|
-
if (isPngSequence) {
|
|
19420
|
-
if (!existsSync28(outputChunkPath)) mkdirSync18(outputChunkPath, { recursive: true });
|
|
19421
|
-
} else {
|
|
19422
|
-
const outDir = join41(outputChunkPath, "..");
|
|
19423
|
-
if (!existsSync28(outDir)) mkdirSync18(outDir, { recursive: true });
|
|
19424
|
-
}
|
|
19425
19503
|
const encodeStarted = Date.now();
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
outputPath: videoOnlyPath,
|
|
19430
|
-
framesDir,
|
|
19431
|
-
videoOnlyPath,
|
|
19432
|
-
width: Math.round(plan2.dimensions.width * encoder.deviceScaleFactor),
|
|
19433
|
-
height: Math.round(plan2.dimensions.height * encoder.deviceScaleFactor),
|
|
19434
|
-
needsAlpha: plan2.dimensions.format !== "mp4",
|
|
19435
|
-
captureImageFormat: captureOptions.format ?? "jpeg",
|
|
19436
|
-
// The encoder writes video only; audio joins at assemble time or in the
|
|
19437
|
-
// chunk that carries audio. Suppressing `hasAudio` skips the png-sequence audio
|
|
19438
|
-
// sidecar AND the mp4 audio mux.
|
|
19439
|
-
hasAudio: false,
|
|
19440
|
-
isPngSequence,
|
|
19441
|
-
// `DistributedFormat` has no "gif" member — distributed chunks are
|
|
19442
|
-
// always video segments (gif renders in-process only).
|
|
19443
|
-
isGif: false,
|
|
19444
|
-
preset,
|
|
19445
|
-
effectiveQuality,
|
|
19446
|
-
effectiveBitrate,
|
|
19447
|
-
engineConfig: {
|
|
19448
|
-
ffmpegEncodeTimeout: cfg.ffmpegEncodeTimeout,
|
|
19449
|
-
vp9CpuUsed: resolveLockedVp9CpuUsed(encoder) ?? cfg.vp9CpuUsed
|
|
19450
|
-
},
|
|
19451
|
-
// Distributed chunks emit a single ffmpeg call per chunk; the
|
|
19452
|
-
// in-process per-chunk-within-chunk path would re-split our
|
|
19453
|
-
// already-chunked work.
|
|
19454
|
-
enableChunkedEncode: false,
|
|
19455
|
-
chunkedEncodeSize: framesInChunk,
|
|
19456
|
-
abortSignal: void 0,
|
|
19457
|
-
assertNotAborted: () => {
|
|
19458
|
-
},
|
|
19459
|
-
// GOP === framesInChunk + force-keyframe at frame 0 → the chunk's
|
|
19460
|
-
// first frame is an IDR keyframe and concat-copy at assemble time
|
|
19461
|
-
// round-trips losslessly.
|
|
19462
|
-
lockGopForChunkConcat: !isPngSequence,
|
|
19463
|
-
gopSize: framesInChunk
|
|
19464
|
-
});
|
|
19504
|
+
const overlapped = overlap.current;
|
|
19505
|
+
overlap.current = null;
|
|
19506
|
+
await (overlapped ? overlapped.finish() : runEncodeStage(encodeInput));
|
|
19465
19507
|
if (encoder.chunksCarryAudio) {
|
|
19466
19508
|
const audioScratchPath = `${outputChunkPath}.audio.m4a`;
|
|
19467
19509
|
const audioChunks = options?.frameRange ? chunksWithSubRange(chunks, chunkIndex, range) : { chunks, index: chunkIndex };
|
|
@@ -19479,6 +19521,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19479
19521
|
}
|
|
19480
19522
|
encodeStageMs = Date.now() - encodeStarted;
|
|
19481
19523
|
} finally {
|
|
19524
|
+
await overlap.current?.abort().catch(() => {
|
|
19525
|
+
});
|
|
19482
19526
|
await extraction?.catch(() => {
|
|
19483
19527
|
});
|
|
19484
19528
|
if (session) {
|
|
@@ -19587,6 +19631,9 @@ var READ_CHUNK_BYTES = 256 * 1024;
|
|
|
19587
19631
|
// src/services/distributed/audioSidecar.ts
|
|
19588
19632
|
import { extractAudioMetadata as extractAudioMetadata4, runFfmpeg as runFfmpeg8 } from "@hyperframes/engine";
|
|
19589
19633
|
var AUDIO_SIDECAR_MIN_BYTES = 32 * 1024 * 1024;
|
|
19634
|
+
|
|
19635
|
+
// src/services/distributed/probeFacts.ts
|
|
19636
|
+
import { buildProbeFactsText } from "@hyperframes/engine";
|
|
19590
19637
|
export {
|
|
19591
19638
|
CURRENT_PLAN_PROTOCOL,
|
|
19592
19639
|
DEFAULT_CONFIG2 as DEFAULT_CONFIG,
|