hyperframes 0.7.89 → 0.7.90
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/cli.js +747 -334
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/studio/assets/{hyperframes-player-DVTnBvSO.js → hyperframes-player-RS9ROX_S.js} +1 -1
- package/dist/studio/assets/{index-Qvx7boWH.js → index-Bi-CwQDX.js} +1 -1
- package/dist/studio/assets/{index-CddKwfQ1.js → index-Cql15Yur.js} +3 -3
- package/dist/studio/assets/{index-ulQEdWKo.js → index-CquRJgm-.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var VERSION;
|
|
|
50
50
|
var init_version = __esm({
|
|
51
51
|
"src/version.ts"() {
|
|
52
52
|
"use strict";
|
|
53
|
-
VERSION = true ? "0.7.
|
|
53
|
+
VERSION = true ? "0.7.90" : "0.0.0-dev";
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -62735,7 +62735,7 @@ function parsePositiveByteLimitMb(content, noLimitCutoffBytes) {
|
|
|
62735
62735
|
}
|
|
62736
62736
|
return Number(bytes / BYTES_PER_MIB_BIGINT);
|
|
62737
62737
|
}
|
|
62738
|
-
function
|
|
62738
|
+
function getCgroupMemoryLimitMb() {
|
|
62739
62739
|
if (_cachedCgroupLimitMb !== void 0) return _cachedCgroupLimitMb;
|
|
62740
62740
|
if (process.platform !== "linux") {
|
|
62741
62741
|
_cachedCgroupLimitMb = null;
|
|
@@ -62782,7 +62782,7 @@ function warnCgroupReadFailure(path2, error) {
|
|
|
62782
62782
|
}
|
|
62783
62783
|
function getSystemTotalMb() {
|
|
62784
62784
|
const hostTotalMb = Math.floor(totalmem() / BYTES_PER_MIB);
|
|
62785
|
-
const cgroupLimitMb =
|
|
62785
|
+
const cgroupLimitMb = getCgroupMemoryLimitMb();
|
|
62786
62786
|
return cgroupLimitMb === null ? hostTotalMb : Math.min(hostTotalMb, cgroupLimitMb);
|
|
62787
62787
|
}
|
|
62788
62788
|
function isLowMemorySystem(totalMb = getSystemTotalMb()) {
|
|
@@ -68812,7 +68812,7 @@ function sanitizeFfprobeDiagnostic(stderr, filePath) {
|
|
|
68812
68812
|
if (redacted.length <= FFPROBE_ERROR_MAX_CHARS) return redacted;
|
|
68813
68813
|
return `\u2026${redacted.slice(-(FFPROBE_ERROR_MAX_CHARS - 1))}`;
|
|
68814
68814
|
}
|
|
68815
|
-
async function runFfprobe(filePath, argsWithoutInput, signal) {
|
|
68815
|
+
async function runFfprobe(filePath, argsWithoutInput, signal, stdoutOptions) {
|
|
68816
68816
|
if (filePath === "-") {
|
|
68817
68817
|
throw new Error('[FFmpeg] Refusing to probe "-": stdin is not a supported input path.');
|
|
68818
68818
|
}
|
|
@@ -68826,12 +68826,17 @@ async function runFfprobe(filePath, argsWithoutInput, signal) {
|
|
|
68826
68826
|
const decoder = new StringDecoder("utf8");
|
|
68827
68827
|
let stdout2 = "";
|
|
68828
68828
|
let stdoutTruncated = false;
|
|
68829
|
+
const stdoutMaxChars = stdoutOptions?.maxChars ?? FFPROBE_STDOUT_MAX_CHARS;
|
|
68829
68830
|
proc.stdout.on("data", (data2) => {
|
|
68830
68831
|
if (stdoutTruncated) return;
|
|
68831
68832
|
stdout2 += decoder.write(data2);
|
|
68832
|
-
if (stdout2.length >
|
|
68833
|
-
|
|
68834
|
-
|
|
68833
|
+
if (stdout2.length > stdoutMaxChars) {
|
|
68834
|
+
if (stdoutOptions?.retainTail) {
|
|
68835
|
+
stdout2 = stdout2.slice(-stdoutMaxChars);
|
|
68836
|
+
} else {
|
|
68837
|
+
stdoutTruncated = true;
|
|
68838
|
+
stdout2 = "";
|
|
68839
|
+
}
|
|
68835
68840
|
}
|
|
68836
68841
|
});
|
|
68837
68842
|
const managed = new ManagedChildProcess(proc, {
|
|
@@ -68843,7 +68848,7 @@ async function runFfprobe(filePath, argsWithoutInput, signal) {
|
|
|
68843
68848
|
stdout2 += decoder.end();
|
|
68844
68849
|
if (stdoutTruncated) {
|
|
68845
68850
|
throw new Error(
|
|
68846
|
-
`[FFmpeg] ffprobe output exceeded ${
|
|
68851
|
+
`[FFmpeg] ffprobe output exceeded ${stdoutMaxChars} characters; refusing to parse a truncated result.`
|
|
68847
68852
|
);
|
|
68848
68853
|
}
|
|
68849
68854
|
if (outcome.reason === "spawn_error") {
|
|
@@ -68990,6 +68995,7 @@ async function extractMediaMetadata(filePath) {
|
|
|
68990
68995
|
return {
|
|
68991
68996
|
durationSeconds: 0,
|
|
68992
68997
|
videoStreamDurationSeconds: 0,
|
|
68998
|
+
videoStreamStartSeconds: 0,
|
|
68993
68999
|
width: stillImageMeta.width,
|
|
68994
69000
|
height: stillImageMeta.height,
|
|
68995
69001
|
fps: 0,
|
|
@@ -69021,9 +69027,12 @@ async function extractMediaMetadata(filePath) {
|
|
|
69021
69027
|
const hasAlpha = pixelFormatHasAlpha(pixelFormat) || alphaMode === "1";
|
|
69022
69028
|
const containerDuration = output?.format.duration ? parseFloat(output.format.duration) : 0;
|
|
69023
69029
|
const streamDuration = videoStream.duration ? parseFloat(videoStream.duration) : 0;
|
|
69030
|
+
const parsedStreamStart = videoStream.start_time ? parseFloat(videoStream.start_time) : 0;
|
|
69031
|
+
const streamStart = Number.isFinite(parsedStreamStart) ? parsedStreamStart : 0;
|
|
69024
69032
|
return {
|
|
69025
69033
|
durationSeconds: containerDuration,
|
|
69026
69034
|
videoStreamDurationSeconds: streamDuration > 0 ? streamDuration : containerDuration,
|
|
69035
|
+
videoStreamStartSeconds: streamStart,
|
|
69027
69036
|
width: videoStream.width || stillImage()?.width || 0,
|
|
69028
69037
|
height: videoStream.height || stillImage()?.height || 0,
|
|
69029
69038
|
fps,
|
|
@@ -69042,6 +69051,59 @@ async function extractMediaMetadata(filePath) {
|
|
|
69042
69051
|
});
|
|
69043
69052
|
return probePromise;
|
|
69044
69053
|
}
|
|
69054
|
+
async function extractFinalVideoFrameTimestamp(filePath, metadata, signal) {
|
|
69055
|
+
const videoDurationSeconds = metadata.videoStreamDurationSeconds;
|
|
69056
|
+
const candidateStreamStart = metadata.videoStreamStartSeconds ?? 0;
|
|
69057
|
+
const videoStreamStartSeconds = Number.isFinite(candidateStreamStart) ? candidateStreamStart : 0;
|
|
69058
|
+
const cacheKey = `${filePath}\0${String(videoStreamStartSeconds)}\0${String(videoDurationSeconds)}`;
|
|
69059
|
+
let probeCache = finalVideoFrameTimestampCache;
|
|
69060
|
+
if (signal) {
|
|
69061
|
+
probeCache = finalVideoFrameTimestampSignalCaches.get(signal) ?? /* @__PURE__ */ new Map();
|
|
69062
|
+
finalVideoFrameTimestampSignalCaches.set(signal, probeCache);
|
|
69063
|
+
}
|
|
69064
|
+
const cached2 = probeCache.get(cacheKey);
|
|
69065
|
+
if (cached2) return cached2;
|
|
69066
|
+
const probePromise = (async () => {
|
|
69067
|
+
if (!(videoDurationSeconds > 0) || !Number.isFinite(videoDurationSeconds)) {
|
|
69068
|
+
throw new Error(
|
|
69069
|
+
`[FFmpeg] Cannot locate final video frame for invalid duration ${String(videoDurationSeconds)}`
|
|
69070
|
+
);
|
|
69071
|
+
}
|
|
69072
|
+
const streamEnd = videoStreamStartSeconds + videoDurationSeconds;
|
|
69073
|
+
const intervalStart = Math.max(videoStreamStartSeconds, streamEnd - 1);
|
|
69074
|
+
const parseFinalTimestamp = (stdout2) => stdout2.split("\n").map((line2) => line2.trim().split(",")[0]?.trim() ?? "").filter((value) => value.length > 0).map((value) => Number(value)).filter((timestamp2) => Number.isFinite(timestamp2)).at(-1);
|
|
69075
|
+
const probe = async (readInterval) => {
|
|
69076
|
+
const args = [
|
|
69077
|
+
"-select_streams",
|
|
69078
|
+
"v:0",
|
|
69079
|
+
"-show_entries",
|
|
69080
|
+
"frame=best_effort_timestamp_time",
|
|
69081
|
+
"-of",
|
|
69082
|
+
"csv=p=0"
|
|
69083
|
+
];
|
|
69084
|
+
if (readInterval) args.splice(2, 0, "-read_intervals", readInterval);
|
|
69085
|
+
const stdout2 = await runFfprobe(filePath, args, signal, {
|
|
69086
|
+
retainTail: true,
|
|
69087
|
+
maxChars: 64 * 1024
|
|
69088
|
+
});
|
|
69089
|
+
return parseFinalTimestamp(stdout2);
|
|
69090
|
+
};
|
|
69091
|
+
const timestamp = await probe(`${intervalStart}%${streamEnd}`) ?? await probe(
|
|
69092
|
+
/* full scan */
|
|
69093
|
+
);
|
|
69094
|
+
if (timestamp === void 0) {
|
|
69095
|
+
throw new Error("[FFmpeg] ffprobe found no decodable final video frame");
|
|
69096
|
+
}
|
|
69097
|
+
return Math.min(Math.max(timestamp - videoStreamStartSeconds, 0), videoDurationSeconds);
|
|
69098
|
+
})();
|
|
69099
|
+
probeCache.set(cacheKey, probePromise);
|
|
69100
|
+
probePromise.catch(() => {
|
|
69101
|
+
if (probeCache.get(cacheKey) === probePromise) {
|
|
69102
|
+
probeCache.delete(cacheKey);
|
|
69103
|
+
}
|
|
69104
|
+
});
|
|
69105
|
+
return probePromise;
|
|
69106
|
+
}
|
|
69045
69107
|
async function extractAudioMetadata(filePath, options) {
|
|
69046
69108
|
const cached2 = options?.signal ? void 0 : audioMetadataCache.get(filePath);
|
|
69047
69109
|
if (cached2) return cached2;
|
|
@@ -69148,7 +69210,7 @@ async function analyzeKeyframeIntervalsUncached(filePath) {
|
|
|
69148
69210
|
isProblematic: maxInterval > 2
|
|
69149
69211
|
};
|
|
69150
69212
|
}
|
|
69151
|
-
var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, audioMetadataCache, AAC_LC_SAMPLES_PER_PACKET, nativeCrc32, extractVideoMetadata, keyframeCache;
|
|
69213
|
+
var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, finalVideoFrameTimestampCache, finalVideoFrameTimestampSignalCaches, audioMetadataCache, AAC_LC_SAMPLES_PER_PACKET, nativeCrc32, extractVideoMetadata, keyframeCache;
|
|
69152
69214
|
var init_ffprobe = __esm({
|
|
69153
69215
|
"../engine/src/utils/ffprobe.ts"() {
|
|
69154
69216
|
"use strict";
|
|
@@ -69160,6 +69222,8 @@ var init_ffprobe = __esm({
|
|
|
69160
69222
|
FFPROBE_STDOUT_MAX_CHARS = 8e6;
|
|
69161
69223
|
FFPROBE_ERROR_MAX_CHARS = 4 * 1024;
|
|
69162
69224
|
videoMetadataCache = /* @__PURE__ */ new Map();
|
|
69225
|
+
finalVideoFrameTimestampCache = /* @__PURE__ */ new Map();
|
|
69226
|
+
finalVideoFrameTimestampSignalCaches = /* @__PURE__ */ new WeakMap();
|
|
69163
69227
|
audioMetadataCache = /* @__PURE__ */ new Map();
|
|
69164
69228
|
AAC_LC_SAMPLES_PER_PACKET = 1024;
|
|
69165
69229
|
nativeCrc32 = typeof zlib.crc32 === "function" ? zlib.crc32 : void 0;
|
|
@@ -83919,20 +83983,21 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
83919
83983
|
} catch (error) {
|
|
83920
83984
|
throw classifyVideoExtractionError(error);
|
|
83921
83985
|
}
|
|
83922
|
-
|
|
83986
|
+
const playableDuration = resolvePlayableVideoDuration(metadata);
|
|
83987
|
+
if (!(playableDuration > 0)) {
|
|
83923
83988
|
throw new VideoSourceExtractionError(
|
|
83924
83989
|
"invalid_media",
|
|
83925
83990
|
false,
|
|
83926
83991
|
"Video source has no positive duration",
|
|
83927
|
-
`
|
|
83992
|
+
`Playable video stream duration is ${playableDuration}s`
|
|
83928
83993
|
);
|
|
83929
83994
|
}
|
|
83930
|
-
if (startTime >=
|
|
83995
|
+
if (startTime >= playableDuration) {
|
|
83931
83996
|
throw new VideoSourceExtractionError(
|
|
83932
83997
|
"media_start_out_of_range",
|
|
83933
83998
|
false,
|
|
83934
83999
|
"Video media start is outside the source duration",
|
|
83935
|
-
`Video media start ${startTime}s is outside
|
|
84000
|
+
`Video media start ${startTime}s is outside playable video duration ${playableDuration}s`
|
|
83936
84001
|
);
|
|
83937
84002
|
}
|
|
83938
84003
|
const format = resolveFrameFormat(metadata, options.format);
|
|
@@ -83947,19 +84012,25 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
83947
84012
|
if (codecMayHaveAlpha(metadata.videoCodec)) {
|
|
83948
84013
|
args.push("-c:v", decoderForCodec(metadata.videoCodec));
|
|
83949
84014
|
}
|
|
83950
|
-
|
|
84015
|
+
if (options.finalFrameOnly) {
|
|
84016
|
+
args.push("-i", videoPath, "-ss", String(startTime), "-frames:v", "1");
|
|
84017
|
+
} else {
|
|
84018
|
+
args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
|
|
84019
|
+
}
|
|
83951
84020
|
const vfFilters = [];
|
|
83952
84021
|
if (isHdr && isMacOS) {
|
|
83953
84022
|
vfFilters.push("format=nv12");
|
|
83954
84023
|
}
|
|
83955
|
-
if (!metadata.isVFR) {
|
|
84024
|
+
if (!options.finalFrameOnly && !metadata.isVFR) {
|
|
83956
84025
|
vfFilters.push(`fps=${fps}`);
|
|
83957
84026
|
}
|
|
83958
84027
|
if (options.sdrToHdrTransfer) {
|
|
83959
84028
|
vfFilters.push(SDR_TO_HDR_COLORSPACE_FILTER);
|
|
83960
84029
|
}
|
|
83961
84030
|
if (vfFilters.length > 0) args.push("-vf", vfFilters.join(","));
|
|
83962
|
-
if (metadata.isVFR)
|
|
84031
|
+
if (!options.finalFrameOnly && metadata.isVFR) {
|
|
84032
|
+
args.push("-fps_mode", "cfr", "-r", String(fps));
|
|
84033
|
+
}
|
|
83963
84034
|
args.push("-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
|
|
83964
84035
|
if (format === "png") args.push("-compression_level", "1");
|
|
83965
84036
|
args.push("-y", outputPattern);
|
|
@@ -84036,10 +84107,124 @@ function classifyFfmpegSpawnError(error, stderr = "") {
|
|
|
84036
84107
|
diagnostic
|
|
84037
84108
|
);
|
|
84038
84109
|
}
|
|
84039
|
-
function resolveSegmentDuration(requested, mediaStart,
|
|
84110
|
+
function resolveSegmentDuration(requested, mediaStart, sourceDuration) {
|
|
84040
84111
|
if (Number.isFinite(requested) && requested > 0) return requested;
|
|
84041
|
-
const sourceRemaining =
|
|
84042
|
-
return sourceRemaining > 0 ? sourceRemaining :
|
|
84112
|
+
const sourceRemaining = sourceDuration - mediaStart;
|
|
84113
|
+
return sourceRemaining > 0 ? sourceRemaining : sourceDuration;
|
|
84114
|
+
}
|
|
84115
|
+
function resolvePlayableVideoDuration(metadata) {
|
|
84116
|
+
return Number.isFinite(metadata.videoStreamDurationSeconds) && metadata.videoStreamDurationSeconds > 0 ? metadata.videoStreamDurationSeconds : metadata.durationSeconds;
|
|
84117
|
+
}
|
|
84118
|
+
function resolveTimelineExtractionWindow(video, resolvedDuration, timelineEnd, sourceDuration) {
|
|
84119
|
+
if (timelineEnd === void 0) {
|
|
84120
|
+
return {
|
|
84121
|
+
compositionStart: video.start,
|
|
84122
|
+
mediaStart: video.mediaStart,
|
|
84123
|
+
durationSeconds: resolvedDuration
|
|
84124
|
+
};
|
|
84125
|
+
}
|
|
84126
|
+
if (!Number.isFinite(timelineEnd)) {
|
|
84127
|
+
throw new Error(`Video extraction timelineEnd must be finite; got ${String(timelineEnd)}`);
|
|
84128
|
+
}
|
|
84129
|
+
const compositionStart = Math.max(0, video.start);
|
|
84130
|
+
const trimmedPreroll = compositionStart - video.start;
|
|
84131
|
+
const timelineDuration2 = Math.max(0, timelineEnd - compositionStart);
|
|
84132
|
+
const resolvedVisibleDuration = resolvedDuration - trimmedPreroll;
|
|
84133
|
+
const visibleDuration = Math.max(0, Math.min(resolvedVisibleDuration, timelineDuration2));
|
|
84134
|
+
let mediaStart = video.mediaStart + trimmedPreroll;
|
|
84135
|
+
if (visibleDuration > 0 && sourceDuration !== void 0) {
|
|
84136
|
+
const sourceRemaining = Math.max(0, sourceDuration - video.mediaStart);
|
|
84137
|
+
if (sourceRemaining > 0 && video.loop && Number.isFinite(video.end)) {
|
|
84138
|
+
const phaseOffset = trimmedPreroll % sourceRemaining;
|
|
84139
|
+
const phaseRemaining = sourceRemaining - phaseOffset;
|
|
84140
|
+
if (visibleDuration >= phaseRemaining) {
|
|
84141
|
+
return {
|
|
84142
|
+
compositionStart: video.start,
|
|
84143
|
+
mediaStart: video.mediaStart,
|
|
84144
|
+
durationSeconds: sourceRemaining,
|
|
84145
|
+
preserveTimelinePhase: true
|
|
84146
|
+
};
|
|
84147
|
+
}
|
|
84148
|
+
mediaStart = video.mediaStart + phaseOffset;
|
|
84149
|
+
} else if (sourceRemaining > 0) {
|
|
84150
|
+
const sourceVisibleAfterPreroll = Math.max(0, sourceRemaining - trimmedPreroll);
|
|
84151
|
+
if (visibleDuration <= sourceVisibleAfterPreroll) {
|
|
84152
|
+
return {
|
|
84153
|
+
compositionStart,
|
|
84154
|
+
mediaStart,
|
|
84155
|
+
durationSeconds: visibleDuration
|
|
84156
|
+
};
|
|
84157
|
+
}
|
|
84158
|
+
const extractionDuration = Math.min(
|
|
84159
|
+
sourceRemaining,
|
|
84160
|
+
Math.max(sourceVisibleAfterPreroll, FINAL_FRAME_LOGICAL_DURATION_SECONDS)
|
|
84161
|
+
);
|
|
84162
|
+
const extractionOffset = sourceRemaining - extractionDuration;
|
|
84163
|
+
return {
|
|
84164
|
+
compositionStart: video.start + extractionOffset,
|
|
84165
|
+
mediaStart: video.mediaStart + extractionOffset,
|
|
84166
|
+
durationSeconds: extractionDuration,
|
|
84167
|
+
preserveTimelineEnd: true,
|
|
84168
|
+
ensureFinalFrame: true
|
|
84169
|
+
};
|
|
84170
|
+
}
|
|
84171
|
+
}
|
|
84172
|
+
return {
|
|
84173
|
+
compositionStart,
|
|
84174
|
+
mediaStart,
|
|
84175
|
+
durationSeconds: visibleDuration
|
|
84176
|
+
};
|
|
84177
|
+
}
|
|
84178
|
+
async function resolveFinalFrameExtractionWindow(videoPath, video, metadata, window3, signal) {
|
|
84179
|
+
if (!window3.ensureFinalFrame) return window3;
|
|
84180
|
+
const playableDuration = resolvePlayableVideoDuration(metadata);
|
|
84181
|
+
const finalFrameTimestamp = await extractFinalVideoFrameTimestamp(
|
|
84182
|
+
videoPath,
|
|
84183
|
+
{
|
|
84184
|
+
videoStreamDurationSeconds: playableDuration,
|
|
84185
|
+
videoStreamStartSeconds: metadata.videoStreamStartSeconds
|
|
84186
|
+
},
|
|
84187
|
+
signal
|
|
84188
|
+
);
|
|
84189
|
+
if (window3.mediaStart < finalFrameTimestamp - 1e-9) return window3;
|
|
84190
|
+
const sourceRemaining = playableDuration - video.mediaStart;
|
|
84191
|
+
const logicalDuration = Math.min(sourceRemaining, FINAL_FRAME_LOGICAL_DURATION_SECONDS);
|
|
84192
|
+
return {
|
|
84193
|
+
compositionStart: Math.max(0, video.start),
|
|
84194
|
+
mediaStart: playableDuration - logicalDuration,
|
|
84195
|
+
extractionMediaStart: finalFrameTimestamp,
|
|
84196
|
+
durationSeconds: logicalDuration,
|
|
84197
|
+
preserveTimelineEnd: true,
|
|
84198
|
+
finalFrameOnly: true
|
|
84199
|
+
};
|
|
84200
|
+
}
|
|
84201
|
+
function resolveVideoExtractionWindow(video, metadata, timelineEnd) {
|
|
84202
|
+
const playableDuration = resolvePlayableVideoDuration(metadata);
|
|
84203
|
+
if (!(playableDuration > 0)) {
|
|
84204
|
+
throw new VideoSourceExtractionError(
|
|
84205
|
+
"invalid_media",
|
|
84206
|
+
false,
|
|
84207
|
+
"Video source has no positive duration",
|
|
84208
|
+
`Playable video stream duration is ${playableDuration}s`
|
|
84209
|
+
);
|
|
84210
|
+
}
|
|
84211
|
+
if (video.mediaStart >= playableDuration) {
|
|
84212
|
+
throw new VideoSourceExtractionError(
|
|
84213
|
+
"media_start_out_of_range",
|
|
84214
|
+
false,
|
|
84215
|
+
"Video media start is outside the source duration",
|
|
84216
|
+
`Video media start ${video.mediaStart}s is outside playable video duration ${playableDuration}s`
|
|
84217
|
+
);
|
|
84218
|
+
}
|
|
84219
|
+
const resolvedDuration = resolveSegmentDuration(
|
|
84220
|
+
video.end - video.start,
|
|
84221
|
+
video.mediaStart,
|
|
84222
|
+
playableDuration
|
|
84223
|
+
);
|
|
84224
|
+
return resolveTimelineExtractionWindow(video, resolvedDuration, timelineEnd, playableDuration);
|
|
84225
|
+
}
|
|
84226
|
+
function resolveVideoExtractionDuration(video, metadata, timelineEnd) {
|
|
84227
|
+
return resolveVideoExtractionWindow(video, metadata, timelineEnd).durationSeconds;
|
|
84043
84228
|
}
|
|
84044
84229
|
function codecMayHaveAlpha(codec) {
|
|
84045
84230
|
return ALPHA_CAPABLE_CODECS.has((codec ?? "").toLowerCase());
|
|
@@ -84087,7 +84272,13 @@ function linkOrCopyFrame(src, dest) {
|
|
|
84087
84272
|
}
|
|
84088
84273
|
}
|
|
84089
84274
|
function supersetGroupingKey(work, fps) {
|
|
84090
|
-
return [
|
|
84275
|
+
return [
|
|
84276
|
+
work.videoPath,
|
|
84277
|
+
String(fps),
|
|
84278
|
+
work.format,
|
|
84279
|
+
work.sdrToHdrTransfer ?? "",
|
|
84280
|
+
work.finalFrameOnly ? "final" : "range"
|
|
84281
|
+
].join("\0");
|
|
84091
84282
|
}
|
|
84092
84283
|
function isIntegralFrameOffset(offsetSeconds, fps) {
|
|
84093
84284
|
const frames = offsetSeconds * fps;
|
|
@@ -84103,6 +84294,7 @@ function windowsOverlapOrTouch(misses, baseStart) {
|
|
|
84103
84294
|
}
|
|
84104
84295
|
function buildSupersetGroup(groupId, misses, fps) {
|
|
84105
84296
|
if (misses.length < 2) return null;
|
|
84297
|
+
if (misses.some(({ work }) => work.finalFrameOnly)) return null;
|
|
84106
84298
|
const baseStart = Math.min(...misses.map(({ work }) => work.video.mediaStart));
|
|
84107
84299
|
if (!misses.every(({ work }) => isIntegralFrameOffset(work.video.mediaStart - baseStart, fps))) {
|
|
84108
84300
|
return null;
|
|
@@ -84203,6 +84395,11 @@ function resolveProjectRelativeSrc(src, baseDir, compiledDir) {
|
|
|
84203
84395
|
return candidates.find(existsSync14) ?? join12(baseDir, cleanSrc);
|
|
84204
84396
|
}
|
|
84205
84397
|
async function extractAllVideoFrames(videos, baseDir, options, signal, config, compiledDir) {
|
|
84398
|
+
if (options.timelineEnd !== void 0 && !Number.isFinite(options.timelineEnd)) {
|
|
84399
|
+
throw new Error(
|
|
84400
|
+
`Video extraction timelineEnd must be finite; got ${String(options.timelineEnd)}`
|
|
84401
|
+
);
|
|
84402
|
+
}
|
|
84206
84403
|
const startTime = Date.now();
|
|
84207
84404
|
const extracted = [];
|
|
84208
84405
|
const errors = [];
|
|
@@ -84232,6 +84429,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84232
84429
|
const warnedSrcs = /* @__PURE__ */ new Set();
|
|
84233
84430
|
for (const video of videos) {
|
|
84234
84431
|
if (signal?.aborted) break;
|
|
84432
|
+
if (options.timelineEnd !== void 0 && video.start >= options.timelineEnd) continue;
|
|
84235
84433
|
try {
|
|
84236
84434
|
let videoPath = video.src;
|
|
84237
84435
|
if (!isHttpUrl(videoPath)) {
|
|
@@ -84283,9 +84481,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84283
84481
|
videoPath,
|
|
84284
84482
|
mtimeMs: stat3.mtimeMs,
|
|
84285
84483
|
size: stat3.size,
|
|
84286
|
-
mediaStart: video.mediaStart
|
|
84287
|
-
start: video.start,
|
|
84288
|
-
end: video.end
|
|
84484
|
+
mediaStart: video.mediaStart
|
|
84289
84485
|
};
|
|
84290
84486
|
});
|
|
84291
84487
|
const phase2ProbeStart = Date.now();
|
|
@@ -84337,12 +84533,13 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84337
84533
|
const entry = resolvedVideos[i2];
|
|
84338
84534
|
const metadata = videoMetadata[i2];
|
|
84339
84535
|
if (!entry || !metadata) continue;
|
|
84340
|
-
|
|
84536
|
+
const playableDuration = resolvePlayableVideoDuration(metadata);
|
|
84537
|
+
if (entry.video.mediaStart >= playableDuration) {
|
|
84341
84538
|
errors.push({
|
|
84342
84539
|
videoId: entry.video.id,
|
|
84343
84540
|
kind: "media_start_out_of_range",
|
|
84344
84541
|
retryable: false,
|
|
84345
|
-
error: `SDR\u2192HDR conversion skipped: mediaStart (${entry.video.mediaStart}s) \u2265
|
|
84542
|
+
error: `SDR\u2192HDR conversion skipped: mediaStart (${entry.video.mediaStart}s) \u2265 playable video duration (${playableDuration}s)`
|
|
84346
84543
|
});
|
|
84347
84544
|
hdrSkippedIndices.add(i2);
|
|
84348
84545
|
continue;
|
|
@@ -84397,7 +84594,12 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84397
84594
|
};
|
|
84398
84595
|
}
|
|
84399
84596
|
function scopedExtractionOptions(work) {
|
|
84400
|
-
return {
|
|
84597
|
+
return {
|
|
84598
|
+
...options,
|
|
84599
|
+
format: work.format,
|
|
84600
|
+
sdrToHdrTransfer: work.sdrToHdrTransfer,
|
|
84601
|
+
finalFrameOnly: work.finalFrameOnly
|
|
84602
|
+
};
|
|
84401
84603
|
}
|
|
84402
84604
|
function rehydratePublishedCache(work, target) {
|
|
84403
84605
|
const rehydrated = rehydrateCacheEntry(target.entry, {
|
|
@@ -84413,18 +84615,17 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84413
84615
|
if (!cacheRootDir) return { work };
|
|
84414
84616
|
const keyInput = cacheKeyInputs[work.index];
|
|
84415
84617
|
if (!keyInput) return { work };
|
|
84416
|
-
const
|
|
84417
|
-
|
|
84418
|
-
|
|
84419
|
-
|
|
84420
|
-
|
|
84421
|
-
);
|
|
84618
|
+
const transformParts = [
|
|
84619
|
+
work.sdrToHdrTransfer ? sdrToHdrTransformKey(work.sdrToHdrTransfer) : void 0,
|
|
84620
|
+
work.finalFrameOnly ? "final-frame" : void 0
|
|
84621
|
+
].filter((part) => part !== void 0);
|
|
84622
|
+
const transform = transformParts.length > 0 ? transformParts.join("+") : void 0;
|
|
84422
84623
|
const lookup = lookupCacheEntry(cacheRootDir, {
|
|
84423
84624
|
videoPath: keyInput.videoPath,
|
|
84424
84625
|
mtimeMs: keyInput.mtimeMs,
|
|
84425
84626
|
size: keyInput.size,
|
|
84426
84627
|
mediaStart: keyInput.mediaStart,
|
|
84427
|
-
duration:
|
|
84628
|
+
duration: work.videoDuration,
|
|
84428
84629
|
fps: options.fps,
|
|
84429
84630
|
format: work.format,
|
|
84430
84631
|
transform
|
|
@@ -84447,7 +84648,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84447
84648
|
() => extractVideoFramesRange(
|
|
84448
84649
|
work.videoPath,
|
|
84449
84650
|
work.video.id,
|
|
84450
|
-
work.
|
|
84651
|
+
work.extractionMediaStart,
|
|
84451
84652
|
work.videoDuration,
|
|
84452
84653
|
scopedExtractionOptions(work),
|
|
84453
84654
|
signal,
|
|
@@ -84471,7 +84672,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84471
84672
|
() => extractVideoFramesRange(
|
|
84472
84673
|
work.videoPath,
|
|
84473
84674
|
work.video.id,
|
|
84474
|
-
work.
|
|
84675
|
+
work.extractionMediaStart,
|
|
84475
84676
|
work.videoDuration,
|
|
84476
84677
|
scopedExtractionOptions(work),
|
|
84477
84678
|
signal,
|
|
@@ -84571,17 +84772,32 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84571
84772
|
}
|
|
84572
84773
|
try {
|
|
84573
84774
|
const metadata = videoMetadata[index] ?? await extractMediaMetadata(videoPath);
|
|
84574
|
-
const
|
|
84575
|
-
|
|
84576
|
-
|
|
84577
|
-
|
|
84775
|
+
const initialWindow = resolveVideoExtractionWindow(video, metadata, options.timelineEnd);
|
|
84776
|
+
const window3 = await resolveFinalFrameExtractionWindow(
|
|
84777
|
+
videoPath,
|
|
84778
|
+
video,
|
|
84779
|
+
metadata,
|
|
84780
|
+
initialWindow,
|
|
84781
|
+
signal
|
|
84578
84782
|
);
|
|
84579
|
-
|
|
84580
|
-
|
|
84783
|
+
const videoDuration = window3.durationSeconds;
|
|
84784
|
+
if (videoDuration <= 0) {
|
|
84785
|
+
return { skipped: true };
|
|
84786
|
+
}
|
|
84787
|
+
if (!window3.preserveTimelinePhase) {
|
|
84788
|
+
video.start = window3.compositionStart;
|
|
84789
|
+
if (!window3.preserveTimelineEnd) {
|
|
84790
|
+
video.end = window3.compositionStart + videoDuration;
|
|
84791
|
+
}
|
|
84792
|
+
video.mediaStart = window3.mediaStart;
|
|
84581
84793
|
}
|
|
84794
|
+
const keyInput = cacheKeyInputs[index];
|
|
84795
|
+
const extractionMediaStart = window3.extractionMediaStart ?? window3.mediaStart;
|
|
84796
|
+
if (keyInput) keyInput.mediaStart = extractionMediaStart;
|
|
84582
84797
|
const format = resolveFrameFormat(metadata, options.format);
|
|
84583
84798
|
const sdrToHdrTransfer = sdrToHdrTransfers[index];
|
|
84584
|
-
const
|
|
84799
|
+
const finalFrameOnly = window3.finalFrameOnly === true;
|
|
84800
|
+
const dedupeKey = `${videoPath}\0${extractionMediaStart}\0${videoDuration}\0${options.fps}\0${format}\0${sdrToHdrTransfer ?? ""}\0${finalFrameOnly ? "final" : "range"}`;
|
|
84585
84801
|
return {
|
|
84586
84802
|
work: {
|
|
84587
84803
|
video,
|
|
@@ -84589,6 +84805,8 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84589
84805
|
index,
|
|
84590
84806
|
metadata,
|
|
84591
84807
|
videoDuration,
|
|
84808
|
+
extractionMediaStart,
|
|
84809
|
+
finalFrameOnly,
|
|
84592
84810
|
format,
|
|
84593
84811
|
sdrToHdrTransfer,
|
|
84594
84812
|
dedupeKey
|
|
@@ -84628,25 +84846,35 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84628
84846
|
for (const groupOutcomes of supersetOutcomes) {
|
|
84629
84847
|
for (const [key2, outcome] of groupOutcomes) uniqueOutcomes.set(key2, outcome);
|
|
84630
84848
|
}
|
|
84631
|
-
const results =
|
|
84632
|
-
|
|
84849
|
+
const results = [];
|
|
84850
|
+
for (const prepared of preparedExtractions) {
|
|
84851
|
+
if ("skipped" in prepared) continue;
|
|
84852
|
+
if ("error" in prepared) {
|
|
84853
|
+
results.push(prepared);
|
|
84854
|
+
continue;
|
|
84855
|
+
}
|
|
84633
84856
|
const outcome = uniqueOutcomes.get(prepared.work.dedupeKey);
|
|
84634
|
-
if (!outcome)
|
|
84635
|
-
|
|
84857
|
+
if (!outcome) {
|
|
84858
|
+
results.push({
|
|
84859
|
+
error: extractionError(prepared.work.video.id, "missing extraction result")
|
|
84860
|
+
});
|
|
84861
|
+
continue;
|
|
84862
|
+
}
|
|
84636
84863
|
if ("error" in outcome) {
|
|
84637
84864
|
const isFollower = outcome.error.videoId !== prepared.work.video.id;
|
|
84638
84865
|
const message = isFollower ? `[shared extraction, leader ${outcome.error.videoId}] ${outcome.error.error}` : outcome.error.error;
|
|
84639
|
-
|
|
84866
|
+
results.push({
|
|
84640
84867
|
error: {
|
|
84641
84868
|
videoId: prepared.work.video.id,
|
|
84642
84869
|
kind: outcome.error.kind,
|
|
84643
84870
|
retryable: outcome.error.retryable,
|
|
84644
84871
|
error: message
|
|
84645
84872
|
}
|
|
84646
|
-
};
|
|
84873
|
+
});
|
|
84874
|
+
continue;
|
|
84647
84875
|
}
|
|
84648
|
-
|
|
84649
|
-
}
|
|
84876
|
+
results.push({ result: { ...outcome.result, videoId: prepared.work.video.id } });
|
|
84877
|
+
}
|
|
84650
84878
|
breakdown.extractMs = Date.now() - phase3Start;
|
|
84651
84879
|
for (const item of results) {
|
|
84652
84880
|
if ("error" in item && item.error) {
|
|
@@ -84678,7 +84906,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
84678
84906
|
function getFrameIndexAtTime(extracted, globalTime, videoStart, loop = false, mediaStart = 0, holdLastFrame = false) {
|
|
84679
84907
|
let localTime = globalTime - videoStart;
|
|
84680
84908
|
if (localTime < 0) return null;
|
|
84681
|
-
const loopDuration = Math.max(0, extracted.metadata
|
|
84909
|
+
const loopDuration = Math.max(0, resolvePlayableVideoDuration(extracted.metadata) - mediaStart);
|
|
84682
84910
|
if (loop && loopDuration > 0 && localTime >= loopDuration) {
|
|
84683
84911
|
localTime %= loopDuration;
|
|
84684
84912
|
}
|
|
@@ -84712,7 +84940,7 @@ function createFrameLookupTable(videos, extracted) {
|
|
|
84712
84940
|
}
|
|
84713
84941
|
return table;
|
|
84714
84942
|
}
|
|
84715
|
-
var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, VideoSourceExtractionError, TRANSIENT_FFMPEG_SPAWN_CODES, ALPHA_CAPABLE_CODECS, FrameLookupTable;
|
|
84943
|
+
var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, VideoSourceExtractionError, TRANSIENT_FFMPEG_SPAWN_CODES, FINAL_FRAME_LOGICAL_DURATION_SECONDS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
|
|
84716
84944
|
var init_videoFrameExtractor = __esm({
|
|
84717
84945
|
"../engine/src/services/videoFrameExtractor.ts"() {
|
|
84718
84946
|
"use strict";
|
|
@@ -84744,6 +84972,7 @@ var init_videoFrameExtractor = __esm({
|
|
|
84744
84972
|
hyperframesVideoSourceExtractionError = true;
|
|
84745
84973
|
};
|
|
84746
84974
|
TRANSIENT_FFMPEG_SPAWN_CODES = /* @__PURE__ */ new Set(["EAGAIN", "EMFILE", "ENFILE"]);
|
|
84975
|
+
FINAL_FRAME_LOGICAL_DURATION_SECONDS = 1e-6;
|
|
84747
84976
|
ALPHA_CAPABLE_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8", "prores"]);
|
|
84748
84977
|
FrameLookupTable = class {
|
|
84749
84978
|
videos = /* @__PURE__ */ new Map();
|
|
@@ -88349,6 +88578,7 @@ __export(src_exports, {
|
|
|
88349
88578
|
executeParallelCapture: () => executeParallelCapture,
|
|
88350
88579
|
extractAllVideoFrames: () => extractAllVideoFrames,
|
|
88351
88580
|
extractAudioMetadata: () => extractAudioMetadata,
|
|
88581
|
+
extractFinalVideoFrameTimestamp: () => extractFinalVideoFrameTimestamp,
|
|
88352
88582
|
extractMediaMetadata: () => extractMediaMetadata,
|
|
88353
88583
|
extractVideoFramesRange: () => extractVideoFramesRange,
|
|
88354
88584
|
extractVideoMetadata: () => extractVideoMetadata,
|
|
@@ -88356,6 +88586,7 @@ __export(src_exports, {
|
|
|
88356
88586
|
formatFfmpegError: () => formatFfmpegError,
|
|
88357
88587
|
getCapturePerfSummary: () => getCapturePerfSummary,
|
|
88358
88588
|
getCdpSession: () => getCdpSession,
|
|
88589
|
+
getCgroupMemoryLimitMb: () => getCgroupMemoryLimitMb,
|
|
88359
88590
|
getCompositionDuration: () => getCompositionDuration,
|
|
88360
88591
|
getDrawElementVerificationDetails: () => getDrawElementVerificationDetails,
|
|
88361
88592
|
getEncoderPreset: () => getEncoderPreset,
|
|
@@ -88413,8 +88644,13 @@ __export(src_exports, {
|
|
|
88413
88644
|
resolveConfig: () => resolveConfig,
|
|
88414
88645
|
resolveDeVerifyMinDb: () => resolveDeVerifyMinDb,
|
|
88415
88646
|
resolveExtractCacheDir: () => resolveExtractCacheDir,
|
|
88647
|
+
resolveFinalFrameExtractionWindow: () => resolveFinalFrameExtractionWindow,
|
|
88416
88648
|
resolveHeadlessShellPath: () => resolveHeadlessShellPath,
|
|
88649
|
+
resolvePlayableVideoDuration: () => resolvePlayableVideoDuration,
|
|
88417
88650
|
resolveProjectRelativeSrc: () => resolveProjectRelativeSrc,
|
|
88651
|
+
resolveTimelineExtractionWindow: () => resolveTimelineExtractionWindow,
|
|
88652
|
+
resolveVideoExtractionDuration: () => resolveVideoExtractionDuration,
|
|
88653
|
+
resolveVideoExtractionWindow: () => resolveVideoExtractionWindow,
|
|
88418
88654
|
roundedRectAlpha: () => roundedRectAlpha,
|
|
88419
88655
|
runFfmpeg: () => runFfmpeg,
|
|
88420
88656
|
runVideoExtractionWithRetry: () => runVideoExtractionWithRetry,
|
|
@@ -112460,7 +112696,7 @@ function expectedFramesForVideo(video, entry, fps) {
|
|
|
112460
112696
|
const rounding = entry && !entry.metadata.isVFR ? "nearest" : "ceil";
|
|
112461
112697
|
const slotFrames = expectedFramesForClip(video.start, video.end, fps, rounding);
|
|
112462
112698
|
if (!entry) return slotFrames;
|
|
112463
|
-
const sourceDuration = entry.metadata
|
|
112699
|
+
const sourceDuration = resolvePlayableVideoDuration(entry.metadata) - video.mediaStart;
|
|
112464
112700
|
if (!Number.isFinite(sourceDuration) || sourceDuration <= 0) return slotFrames;
|
|
112465
112701
|
const sourceFrames = expectedFramesForClip(0, sourceDuration, fps, rounding);
|
|
112466
112702
|
return Math.min(slotFrames, sourceFrames);
|
|
@@ -112508,6 +112744,7 @@ var init_videoFrameCoverage = __esm({
|
|
|
112508
112744
|
"../producer/src/services/render/videoFrameCoverage.ts"() {
|
|
112509
112745
|
"use strict";
|
|
112510
112746
|
init_esm10();
|
|
112747
|
+
init_src();
|
|
112511
112748
|
VideoFrameCoverageError = class extends Error {
|
|
112512
112749
|
// Read structurally by isVideoFrameCoverageError and cross-module callers.
|
|
112513
112750
|
// fallow-ignore-next-line unused-class-member
|
|
@@ -119211,6 +119448,7 @@ async function runExtractVideosStage(input2) {
|
|
|
119211
119448
|
fps: fpsToNumber(job.config.fps),
|
|
119212
119449
|
outputDir: join47(compiledDir, "__hyperframes_video_frames"),
|
|
119213
119450
|
format: job.config.videoFrameFormat ?? "auto",
|
|
119451
|
+
timelineEnd: composition.duration,
|
|
119214
119452
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
119215
119453
|
collectProbeFailures: extractionPolicy.failureMode === "enforce"
|
|
119216
119454
|
},
|
|
@@ -119559,17 +119797,26 @@ function closeHdrVideoFrameSource(source, log2) {
|
|
|
119559
119797
|
});
|
|
119560
119798
|
}
|
|
119561
119799
|
}
|
|
119800
|
+
function resolveHdrVideoFrameIndex(time, startTime, fps, frameCount, loop = false) {
|
|
119801
|
+
const frameIndex = Math.round((time - startTime) * fps);
|
|
119802
|
+
if (frameIndex < 0 || frameCount < 1) return null;
|
|
119803
|
+
return loop ? frameIndex % frameCount : Math.min(frameIndex, frameCount - 1);
|
|
119804
|
+
}
|
|
119562
119805
|
function blitHdrVideoLayer(canvas, el, time, fps, hdrVideoFrameSources, hdrStartTimes, width, height, log2, sourceTransfer, targetTransfer, hdrPerf) {
|
|
119563
119806
|
const frameSource = hdrVideoFrameSources.get(el.id);
|
|
119564
119807
|
const startTime = hdrStartTimes.get(el.id);
|
|
119565
119808
|
if (!frameSource || startTime === void 0 || el.opacity <= 0) {
|
|
119566
119809
|
return;
|
|
119567
119810
|
}
|
|
119568
|
-
const
|
|
119569
|
-
|
|
119570
|
-
|
|
119571
|
-
|
|
119572
|
-
|
|
119811
|
+
const effectiveIndex = resolveHdrVideoFrameIndex(
|
|
119812
|
+
time,
|
|
119813
|
+
startTime,
|
|
119814
|
+
fps,
|
|
119815
|
+
frameSource.frameCount,
|
|
119816
|
+
frameSource.loop
|
|
119817
|
+
);
|
|
119818
|
+
if (effectiveIndex === null) return;
|
|
119819
|
+
const frameOffset = effectiveIndex * frameSource.frameSize;
|
|
119573
119820
|
try {
|
|
119574
119821
|
if (hdrPerf) hdrPerf.hdrVideoLayerBlits += 1;
|
|
119575
119822
|
const bytesRead = timeHdrPhase(
|
|
@@ -119955,8 +120202,402 @@ var init_hdrCompositor = __esm({
|
|
|
119955
120202
|
}
|
|
119956
120203
|
});
|
|
119957
120204
|
|
|
120205
|
+
// ../producer/src/services/render/stages/captureHdrResources.ts
|
|
120206
|
+
import {
|
|
120207
|
+
closeSync as closeSync7,
|
|
120208
|
+
constants as constants3,
|
|
120209
|
+
fstatSync as fstatSync4,
|
|
120210
|
+
mkdirSync as mkdirSync24,
|
|
120211
|
+
mkdtempSync as mkdtempSync6,
|
|
120212
|
+
openSync as openSync6,
|
|
120213
|
+
readFileSync as readFileSync30,
|
|
120214
|
+
rmSync as rmSync15,
|
|
120215
|
+
statfsSync as statfsSync2
|
|
120216
|
+
} from "fs";
|
|
120217
|
+
import { join as join50 } from "path";
|
|
120218
|
+
function tempDirSafePrefix(id) {
|
|
120219
|
+
const safe = id.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
|
|
120220
|
+
return safe || "video";
|
|
120221
|
+
}
|
|
120222
|
+
function planHdrResources(args) {
|
|
120223
|
+
const { composition, nativeHdrVideoIds, nativeHdrImageIds, projectDir, compiledDir } = args;
|
|
120224
|
+
const hdrVideoIds = composition.videos.filter((v2) => nativeHdrVideoIds.has(v2.id)).map((v2) => v2.id);
|
|
120225
|
+
const hdrVideoSrcPaths = /* @__PURE__ */ new Map();
|
|
120226
|
+
for (const v2 of composition.videos) {
|
|
120227
|
+
if (!hdrVideoIds.includes(v2.id)) continue;
|
|
120228
|
+
let srcPath = v2.src;
|
|
120229
|
+
if (!srcPath.startsWith("/")) {
|
|
120230
|
+
const fromCompiled = join50(compiledDir, srcPath);
|
|
120231
|
+
srcPath = args.existsSync(fromCompiled) ? fromCompiled : join50(projectDir, srcPath);
|
|
120232
|
+
}
|
|
120233
|
+
hdrVideoSrcPaths.set(v2.id, srcPath);
|
|
120234
|
+
}
|
|
120235
|
+
const hdrVideoStartTimes = /* @__PURE__ */ new Map();
|
|
120236
|
+
for (const v2 of composition.videos) {
|
|
120237
|
+
if (hdrVideoIds.includes(v2.id)) hdrVideoStartTimes.set(v2.id, v2.start);
|
|
120238
|
+
}
|
|
120239
|
+
const hdrImageStartTimes = /* @__PURE__ */ new Map();
|
|
120240
|
+
for (const img of composition.images) {
|
|
120241
|
+
if (nativeHdrImageIds.has(img.id)) hdrImageStartTimes.set(img.id, img.start);
|
|
120242
|
+
}
|
|
120243
|
+
return {
|
|
120244
|
+
hdrVideoIds,
|
|
120245
|
+
hdrVideoSrcPaths,
|
|
120246
|
+
hdrVideoStartTimes,
|
|
120247
|
+
hdrImageStartTimes,
|
|
120248
|
+
hdrExtractionDims: /* @__PURE__ */ new Map(),
|
|
120249
|
+
hdrImageFitInfo: /* @__PURE__ */ new Map()
|
|
120250
|
+
};
|
|
120251
|
+
}
|
|
120252
|
+
async function probeHdrExtractionDims(args) {
|
|
120253
|
+
const { domSession, nativeHdrIds, nativeHdrImageIds, composition, prep } = args;
|
|
120254
|
+
const uniqueStartTimes = [
|
|
120255
|
+
.../* @__PURE__ */ new Set([...prep.hdrVideoStartTimes.values(), ...prep.hdrImageStartTimes.values()])
|
|
120256
|
+
].sort((a, b2) => a - b2);
|
|
120257
|
+
for (const seekTime of uniqueStartTimes) {
|
|
120258
|
+
await domSession.page.evaluate((t2) => {
|
|
120259
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t2);
|
|
120260
|
+
}, seekTime);
|
|
120261
|
+
if (domSession.onBeforeCapture) {
|
|
120262
|
+
await domSession.onBeforeCapture(domSession.page, seekTime);
|
|
120263
|
+
}
|
|
120264
|
+
const stacking = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
120265
|
+
for (const el of stacking) {
|
|
120266
|
+
if (el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0 && !prep.hdrExtractionDims.has(el.id)) {
|
|
120267
|
+
prep.hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
120268
|
+
}
|
|
120269
|
+
if (el.isHdr && nativeHdrImageIds.has(el.id) && !prep.hdrImageFitInfo.has(el.id)) {
|
|
120270
|
+
prep.hdrImageFitInfo.set(el.id, { fit: el.objectFit, position: el.objectPosition });
|
|
120271
|
+
}
|
|
120272
|
+
}
|
|
120273
|
+
}
|
|
120274
|
+
for (const [imageId, startTime] of prep.hdrImageStartTimes) {
|
|
120275
|
+
if (prep.hdrExtractionDims.has(imageId)) continue;
|
|
120276
|
+
const img = composition.images.find((i2) => i2.id === imageId);
|
|
120277
|
+
if (!img) continue;
|
|
120278
|
+
const duration = img.end - img.start;
|
|
120279
|
+
const retryTime = startTime + Math.min(0.5, duration * 0.1);
|
|
120280
|
+
await domSession.page.evaluate((t2) => {
|
|
120281
|
+
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t2);
|
|
120282
|
+
}, retryTime);
|
|
120283
|
+
if (domSession.onBeforeCapture) {
|
|
120284
|
+
await domSession.onBeforeCapture(domSession.page, retryTime);
|
|
120285
|
+
}
|
|
120286
|
+
const retryStacking = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
120287
|
+
for (const el of retryStacking) {
|
|
120288
|
+
if (el.id === imageId && el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0) {
|
|
120289
|
+
prep.hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
120290
|
+
if (!prep.hdrImageFitInfo.has(el.id)) {
|
|
120291
|
+
prep.hdrImageFitInfo.set(el.id, { fit: el.objectFit, position: el.objectPosition });
|
|
120292
|
+
}
|
|
120293
|
+
break;
|
|
120294
|
+
}
|
|
120295
|
+
}
|
|
120296
|
+
}
|
|
120297
|
+
}
|
|
120298
|
+
function estimateHdrExtractionBytes(videos, fps) {
|
|
120299
|
+
let total = 0;
|
|
120300
|
+
for (const v2 of videos) {
|
|
120301
|
+
const frames = Math.ceil(Math.max(0, v2.durationSeconds) * fps);
|
|
120302
|
+
total += frames * v2.width * v2.height * 6;
|
|
120303
|
+
}
|
|
120304
|
+
return total;
|
|
120305
|
+
}
|
|
120306
|
+
function resolveHdrExtractionBudgetBytes(raw, cgroupLimitMb = getCgroupMemoryLimitMb()) {
|
|
120307
|
+
let configuredBudget;
|
|
120308
|
+
if (raw !== void 0 && raw.trim() !== "") {
|
|
120309
|
+
const value = Number(raw);
|
|
120310
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
120311
|
+
throw new Error(`${HDR_EXTRACTION_MAX_BYTES_ENV} must be a positive finite byte count`);
|
|
120312
|
+
}
|
|
120313
|
+
configuredBudget = Math.floor(value);
|
|
120314
|
+
}
|
|
120315
|
+
const cgroupBudget = cgroupLimitMb !== null && Number.isFinite(cgroupLimitMb) && cgroupLimitMb > 0 ? Math.floor(cgroupLimitMb * BYTES_PER_MIB2 * HDR_EXTRACTION_CGROUP_BUDGET_FRACTION) : void 0;
|
|
120316
|
+
if (configuredBudget === void 0) return cgroupBudget;
|
|
120317
|
+
if (cgroupBudget === void 0) return configuredBudget;
|
|
120318
|
+
return Math.min(configuredBudget, cgroupBudget);
|
|
120319
|
+
}
|
|
120320
|
+
function resolveHdrExtractionActiveBudgetBytes(configuredBudgetBytes, freeBytes) {
|
|
120321
|
+
const diskBudgetBytes = Math.floor(freeBytes * HDR_EXTRACTION_HEADROOM_FRACTION);
|
|
120322
|
+
return configuredBudgetBytes === void 0 ? diskBudgetBytes : Math.min(configuredBudgetBytes, diskBudgetBytes);
|
|
120323
|
+
}
|
|
120324
|
+
function reserveHdrExtractionBytes(estimatedBytes, budgetBytes) {
|
|
120325
|
+
if (!Number.isFinite(estimatedBytes) || estimatedBytes < 0) {
|
|
120326
|
+
throw new Error(`HDR extraction reservation must be a finite non-negative byte count`);
|
|
120327
|
+
}
|
|
120328
|
+
const aggregateBytes = aggregateHdrExtractionReservedBytes + estimatedBytes;
|
|
120329
|
+
if (budgetBytes !== void 0 && aggregateBytes > budgetBytes) {
|
|
120330
|
+
throw new Error(
|
|
120331
|
+
`Concurrent HDR pre-extractions need ~${(aggregateBytes / 1e9).toFixed(1)} GB of raw 16-bit frame scratch, exceeding the active ${(budgetBytes / 1e9).toFixed(1)} GB budget.`
|
|
120332
|
+
);
|
|
120333
|
+
}
|
|
120334
|
+
aggregateHdrExtractionReservedBytes = aggregateBytes;
|
|
120335
|
+
let released = false;
|
|
120336
|
+
return () => {
|
|
120337
|
+
if (released) return;
|
|
120338
|
+
released = true;
|
|
120339
|
+
aggregateHdrExtractionReservedBytes = Math.max(
|
|
120340
|
+
0,
|
|
120341
|
+
aggregateHdrExtractionReservedBytes - estimatedBytes
|
|
120342
|
+
);
|
|
120343
|
+
};
|
|
120344
|
+
}
|
|
120345
|
+
function resolveHdrExtractionWindow(video, compositionDuration, metadata) {
|
|
120346
|
+
if (!Number.isFinite(compositionDuration) || compositionDuration <= 0) {
|
|
120347
|
+
throw new Error(
|
|
120348
|
+
`Cannot extract HDR video "${video.id}" with invalid composition duration ${String(compositionDuration)}`
|
|
120349
|
+
);
|
|
120350
|
+
}
|
|
120351
|
+
const window3 = resolveVideoExtractionWindow(video, metadata, compositionDuration);
|
|
120352
|
+
if (!Number.isFinite(window3.durationSeconds)) {
|
|
120353
|
+
throw new Error(
|
|
120354
|
+
`HDR video "${video.id}" has no finite interval inside the ${compositionDuration}s composition`
|
|
120355
|
+
);
|
|
120356
|
+
}
|
|
120357
|
+
if (window3.durationSeconds <= 0) return null;
|
|
120358
|
+
if (!Number.isFinite(window3.mediaStart) || window3.mediaStart < 0) {
|
|
120359
|
+
throw new Error(`HDR video "${video.id}" has invalid mediaStart ${String(window3.mediaStart)}`);
|
|
120360
|
+
}
|
|
120361
|
+
return window3;
|
|
120362
|
+
}
|
|
120363
|
+
function cleanupHdrFrameDirectory(frameDir, rawPath, log2) {
|
|
120364
|
+
if (process.env.KEEP_TEMP === "1") return;
|
|
120365
|
+
try {
|
|
120366
|
+
rmSync15(frameDir, { recursive: true, force: true });
|
|
120367
|
+
} catch (err) {
|
|
120368
|
+
log2?.warn("Failed to clean up HDR raw frame directory", {
|
|
120369
|
+
frameDir,
|
|
120370
|
+
rawPath,
|
|
120371
|
+
error: err instanceof Error ? err.message : String(err)
|
|
120372
|
+
});
|
|
120373
|
+
}
|
|
120374
|
+
}
|
|
120375
|
+
function cleanupHdrVideoFrameSource(source, log2) {
|
|
120376
|
+
closeHdrVideoFrameSource(source, log2);
|
|
120377
|
+
cleanupHdrFrameDirectory(source.dir, source.rawPath, log2);
|
|
120378
|
+
}
|
|
120379
|
+
function assertHdrExtractionDiskHeadroom(framesDir, plannedVideos, fps, log2) {
|
|
120380
|
+
const estimatedBytes = estimateHdrExtractionBytes(plannedVideos, fps);
|
|
120381
|
+
const configuredBudget = resolveHdrExtractionBudgetBytes(
|
|
120382
|
+
process.env[HDR_EXTRACTION_MAX_BYTES_ENV]
|
|
120383
|
+
);
|
|
120384
|
+
const estimatedGb = (estimatedBytes / 1e9).toFixed(1);
|
|
120385
|
+
if (configuredBudget !== void 0 && estimatedBytes > configuredBudget) {
|
|
120386
|
+
throw new Error(
|
|
120387
|
+
`HDR pre-extraction needs ~${estimatedGb} GB of raw 16-bit frames, exceeding the active scratch budget of ${(configuredBudget / 1e9).toFixed(1)} GB. If the composition doesn't need HDR output, re-run with --sdr; otherwise reduce its HDR duration/resolution or use a render container with a larger memory limit.`
|
|
120388
|
+
);
|
|
120389
|
+
}
|
|
120390
|
+
let freeBytes;
|
|
120391
|
+
try {
|
|
120392
|
+
const stat3 = statfsSync2(framesDir);
|
|
120393
|
+
freeBytes = stat3.bavail * stat3.bsize;
|
|
120394
|
+
} catch {
|
|
120395
|
+
return { estimatedBytes, budgetBytes: configuredBudget };
|
|
120396
|
+
}
|
|
120397
|
+
const diskBudgetBytes = Math.floor(freeBytes * HDR_EXTRACTION_HEADROOM_FRACTION);
|
|
120398
|
+
if (estimatedBytes > diskBudgetBytes) {
|
|
120399
|
+
throw new Error(
|
|
120400
|
+
`HDR pre-extraction needs ~${estimatedGb} GB of raw 16-bit frames but only ${(freeBytes / 1e9).toFixed(1)} GB is free at ${framesDir}. If the composition doesn't need HDR output, re-run with --sdr; otherwise free up disk space and retry.`
|
|
120401
|
+
);
|
|
120402
|
+
}
|
|
120403
|
+
if (estimatedBytes > HDR_EXTRACTION_WARN_BYTES) {
|
|
120404
|
+
log2.warn(
|
|
120405
|
+
`HDR pre-extraction will write ~${estimatedGb} GB of raw 16-bit frames (pass --sdr to skip if HDR output isn't needed)`,
|
|
120406
|
+
{ estimatedBytes, freeBytes }
|
|
120407
|
+
);
|
|
120408
|
+
}
|
|
120409
|
+
return {
|
|
120410
|
+
estimatedBytes,
|
|
120411
|
+
budgetBytes: resolveHdrExtractionActiveBudgetBytes(configuredBudget, freeBytes)
|
|
120412
|
+
};
|
|
120413
|
+
}
|
|
120414
|
+
async function extractHdrVideoFrames(args) {
|
|
120415
|
+
const { job, log: log2, framesDir, composition, prep, width, height, abortSignal, hdrDiagnostics } = args;
|
|
120416
|
+
const runFfmpegImpl = args.runFfmpegImpl ?? runFfmpeg;
|
|
120417
|
+
const extractMediaMetadataImpl = args.extractMediaMetadataImpl ?? extractMediaMetadata;
|
|
120418
|
+
const resolveFinalFrameExtractionWindowImpl = args.resolveFinalFrameExtractionWindowImpl ?? resolveFinalFrameExtractionWindow;
|
|
120419
|
+
const out = /* @__PURE__ */ new Map();
|
|
120420
|
+
mkdirSync24(framesDir, { recursive: true });
|
|
120421
|
+
const plannedVideos = [];
|
|
120422
|
+
const extractionWindows = /* @__PURE__ */ new Map();
|
|
120423
|
+
for (const [videoId, srcPath] of prep.hdrVideoSrcPaths) {
|
|
120424
|
+
const video = composition.videos.find((v2) => v2.id === videoId);
|
|
120425
|
+
if (!video) continue;
|
|
120426
|
+
const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
|
|
120427
|
+
const metadata = await extractMediaMetadataImpl(srcPath);
|
|
120428
|
+
const initialWindow = resolveHdrExtractionWindow(video, composition.duration, metadata);
|
|
120429
|
+
if (!initialWindow) continue;
|
|
120430
|
+
const window3 = await resolveFinalFrameExtractionWindowImpl(
|
|
120431
|
+
srcPath,
|
|
120432
|
+
video,
|
|
120433
|
+
metadata,
|
|
120434
|
+
initialWindow,
|
|
120435
|
+
abortSignal
|
|
120436
|
+
);
|
|
120437
|
+
extractionWindows.set(videoId, window3);
|
|
120438
|
+
prep.hdrVideoStartTimes.set(videoId, window3.compositionStart);
|
|
120439
|
+
plannedVideos.push({
|
|
120440
|
+
durationSeconds: window3.durationSeconds,
|
|
120441
|
+
width: dims.width,
|
|
120442
|
+
height: dims.height
|
|
120443
|
+
});
|
|
120444
|
+
}
|
|
120445
|
+
const { estimatedBytes, budgetBytes } = assertHdrExtractionDiskHeadroom(
|
|
120446
|
+
framesDir,
|
|
120447
|
+
plannedVideos,
|
|
120448
|
+
fpsToNumber(job.config.fps),
|
|
120449
|
+
log2
|
|
120450
|
+
);
|
|
120451
|
+
const releaseReservation = reserveHdrExtractionBytes(estimatedBytes, budgetBytes);
|
|
120452
|
+
const createdFrameDirs = /* @__PURE__ */ new Set();
|
|
120453
|
+
try {
|
|
120454
|
+
for (const [videoId, srcPath] of prep.hdrVideoSrcPaths) {
|
|
120455
|
+
const video = composition.videos.find((v2) => v2.id === videoId);
|
|
120456
|
+
const window3 = extractionWindows.get(videoId);
|
|
120457
|
+
if (!video || !window3) continue;
|
|
120458
|
+
mkdirSync24(framesDir, { recursive: true });
|
|
120459
|
+
const frameDir = mkdtempSync6(join50(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
|
|
120460
|
+
createdFrameDirs.add(frameDir);
|
|
120461
|
+
const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
|
|
120462
|
+
const rawPath = join50(frameDir, "frames.rgb48le");
|
|
120463
|
+
const extractionStart = String(window3.extractionMediaStart ?? window3.mediaStart);
|
|
120464
|
+
const ffmpegArgs = [];
|
|
120465
|
+
if (window3.finalFrameOnly) {
|
|
120466
|
+
ffmpegArgs.push("-i", srcPath, "-ss", extractionStart, "-frames:v", "1");
|
|
120467
|
+
} else {
|
|
120468
|
+
ffmpegArgs.push(
|
|
120469
|
+
"-ss",
|
|
120470
|
+
extractionStart,
|
|
120471
|
+
"-i",
|
|
120472
|
+
srcPath,
|
|
120473
|
+
"-t",
|
|
120474
|
+
String(window3.durationSeconds)
|
|
120475
|
+
);
|
|
120476
|
+
}
|
|
120477
|
+
if (!window3.finalFrameOnly) {
|
|
120478
|
+
ffmpegArgs.push("-r", fpsToFfmpegArg(job.config.fps));
|
|
120479
|
+
}
|
|
120480
|
+
ffmpegArgs.push(
|
|
120481
|
+
"-vf",
|
|
120482
|
+
`scale=${dims.width}:${dims.height}:force_original_aspect_ratio=increase,crop=${dims.width}:${dims.height}`,
|
|
120483
|
+
"-pix_fmt",
|
|
120484
|
+
"rgb48le",
|
|
120485
|
+
"-f",
|
|
120486
|
+
"rawvideo",
|
|
120487
|
+
"-y",
|
|
120488
|
+
rawPath
|
|
120489
|
+
);
|
|
120490
|
+
const result = await runFfmpegImpl(ffmpegArgs, { signal: abortSignal });
|
|
120491
|
+
if (!result.success) {
|
|
120492
|
+
hdrDiagnostics.videoExtractionFailures += 1;
|
|
120493
|
+
log2.error("HDR frame pre-extraction failed; aborting render", {
|
|
120494
|
+
videoId,
|
|
120495
|
+
srcPath,
|
|
120496
|
+
stderr: result.stderr.slice(-400)
|
|
120497
|
+
});
|
|
120498
|
+
throw new Error(
|
|
120499
|
+
`HDR frame extraction failed for video "${videoId}". Aborting render to avoid shipping black HDR layers.`
|
|
120500
|
+
);
|
|
120501
|
+
}
|
|
120502
|
+
const frameSize = dims.width * dims.height * 6;
|
|
120503
|
+
const fd = openSync6(rawPath, constants3.O_RDONLY | NO_FOLLOW_FLAG);
|
|
120504
|
+
let handedOff = false;
|
|
120505
|
+
try {
|
|
120506
|
+
const frameCount = Math.floor(fstatSync4(fd).size / frameSize);
|
|
120507
|
+
if (frameCount < 1) {
|
|
120508
|
+
hdrDiagnostics.videoExtractionFailures += 1;
|
|
120509
|
+
throw new Error(
|
|
120510
|
+
`HDR frame extraction produced no frames for video "${videoId}". Aborting render to avoid shipping black HDR layers.`
|
|
120511
|
+
);
|
|
120512
|
+
}
|
|
120513
|
+
out.set(videoId, {
|
|
120514
|
+
dir: frameDir,
|
|
120515
|
+
rawPath,
|
|
120516
|
+
fd,
|
|
120517
|
+
width: dims.width,
|
|
120518
|
+
height: dims.height,
|
|
120519
|
+
frameSize,
|
|
120520
|
+
frameCount,
|
|
120521
|
+
scratch: Buffer.allocUnsafe(frameSize),
|
|
120522
|
+
loop: video.loop
|
|
120523
|
+
});
|
|
120524
|
+
handedOff = true;
|
|
120525
|
+
} finally {
|
|
120526
|
+
if (!handedOff) closeSync7(fd);
|
|
120527
|
+
}
|
|
120528
|
+
}
|
|
120529
|
+
return { sources: out, estimatedBytes, releaseReservation };
|
|
120530
|
+
} catch (error) {
|
|
120531
|
+
for (const source of out.values()) {
|
|
120532
|
+
cleanupHdrVideoFrameSource(source, log2);
|
|
120533
|
+
createdFrameDirs.delete(source.dir);
|
|
120534
|
+
}
|
|
120535
|
+
for (const frameDir of createdFrameDirs) {
|
|
120536
|
+
cleanupHdrFrameDirectory(frameDir, void 0, log2);
|
|
120537
|
+
}
|
|
120538
|
+
releaseReservation();
|
|
120539
|
+
throw error;
|
|
120540
|
+
}
|
|
120541
|
+
}
|
|
120542
|
+
function decodeHdrImageBuffers(args) {
|
|
120543
|
+
const { log: log2, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
|
|
120544
|
+
const out = /* @__PURE__ */ new Map();
|
|
120545
|
+
for (const [imageId, srcPath] of hdrImageSrcPaths) {
|
|
120546
|
+
try {
|
|
120547
|
+
const decoded = decodePngToRgb48le(readFileSync30(srcPath));
|
|
120548
|
+
const layout2 = prep.hdrExtractionDims.get(imageId);
|
|
120549
|
+
const fitInfo = prep.hdrImageFitInfo.get(imageId);
|
|
120550
|
+
if (layout2 && (layout2.width !== decoded.width || layout2.height !== decoded.height)) {
|
|
120551
|
+
const fit = normalizeObjectFit(fitInfo?.fit);
|
|
120552
|
+
const resampled = resampleRgb48leObjectFit(
|
|
120553
|
+
decoded.data,
|
|
120554
|
+
decoded.width,
|
|
120555
|
+
decoded.height,
|
|
120556
|
+
layout2.width,
|
|
120557
|
+
layout2.height,
|
|
120558
|
+
fit,
|
|
120559
|
+
fitInfo?.position
|
|
120560
|
+
);
|
|
120561
|
+
out.set(imageId, { data: resampled, width: layout2.width, height: layout2.height });
|
|
120562
|
+
} else {
|
|
120563
|
+
out.set(imageId, {
|
|
120564
|
+
data: Buffer.from(decoded.data),
|
|
120565
|
+
width: decoded.width,
|
|
120566
|
+
height: decoded.height
|
|
120567
|
+
});
|
|
120568
|
+
}
|
|
120569
|
+
} catch (err) {
|
|
120570
|
+
hdrDiagnostics.imageDecodeFailures += 1;
|
|
120571
|
+
log2.error("HDR image decode failed; aborting render", {
|
|
120572
|
+
imageId,
|
|
120573
|
+
srcPath,
|
|
120574
|
+
error: err instanceof Error ? err.message : String(err)
|
|
120575
|
+
});
|
|
120576
|
+
throw new Error(
|
|
120577
|
+
`HDR image decode failed for image "${imageId}". Aborting render to avoid shipping missing HDR image layers.`
|
|
120578
|
+
);
|
|
120579
|
+
}
|
|
120580
|
+
}
|
|
120581
|
+
return out;
|
|
120582
|
+
}
|
|
120583
|
+
var NO_FOLLOW_FLAG, HDR_EXTRACTION_HEADROOM_FRACTION, HDR_EXTRACTION_CGROUP_BUDGET_FRACTION, HDR_EXTRACTION_WARN_BYTES, HDR_EXTRACTION_MAX_BYTES_ENV, BYTES_PER_MIB2, aggregateHdrExtractionReservedBytes;
|
|
120584
|
+
var init_captureHdrResources = __esm({
|
|
120585
|
+
"../producer/src/services/render/stages/captureHdrResources.ts"() {
|
|
120586
|
+
"use strict";
|
|
120587
|
+
init_src();
|
|
120588
|
+
init_dist3();
|
|
120589
|
+
init_hdrCompositor();
|
|
120590
|
+
NO_FOLLOW_FLAG = constants3.O_NOFOLLOW ?? 0;
|
|
120591
|
+
HDR_EXTRACTION_HEADROOM_FRACTION = 0.9;
|
|
120592
|
+
HDR_EXTRACTION_CGROUP_BUDGET_FRACTION = 0.5;
|
|
120593
|
+
HDR_EXTRACTION_WARN_BYTES = 1e10;
|
|
120594
|
+
HDR_EXTRACTION_MAX_BYTES_ENV = "HDR_EXTRACTION_MAX_BYTES";
|
|
120595
|
+
BYTES_PER_MIB2 = 1024 * 1024;
|
|
120596
|
+
aggregateHdrExtractionReservedBytes = 0;
|
|
120597
|
+
}
|
|
120598
|
+
});
|
|
120599
|
+
|
|
119958
120600
|
// ../producer/src/services/render/stages/captureHdrFrameShared.ts
|
|
119959
|
-
import { rmSync as rmSync15 } from "fs";
|
|
119960
120601
|
function shouldUseHybridLayeredPath(args) {
|
|
119961
120602
|
if (args.hasHdrContent) return false;
|
|
119962
120603
|
if (args.workerCount <= 1) return false;
|
|
@@ -120180,17 +120821,7 @@ function cleanupEndedHdrVideos(args) {
|
|
|
120180
120821
|
if (!stillNeeded) {
|
|
120181
120822
|
const frameSource = hdrVideoFrameSources.get(videoId);
|
|
120182
120823
|
if (frameSource) {
|
|
120183
|
-
|
|
120184
|
-
try {
|
|
120185
|
-
rmSync15(frameSource.dir, { recursive: true, force: true });
|
|
120186
|
-
} catch (err) {
|
|
120187
|
-
log2.warn("Failed to clean up HDR raw frame directory", {
|
|
120188
|
-
videoId,
|
|
120189
|
-
frameDir: frameSource.dir,
|
|
120190
|
-
rawPath: frameSource.rawPath,
|
|
120191
|
-
error: err instanceof Error ? err.message : String(err)
|
|
120192
|
-
});
|
|
120193
|
-
}
|
|
120824
|
+
cleanupHdrVideoFrameSource(frameSource, log2);
|
|
120194
120825
|
hdrVideoFrameSources.delete(videoId);
|
|
120195
120826
|
}
|
|
120196
120827
|
cleanedUpVideos.add(videoId);
|
|
@@ -120203,6 +120834,7 @@ var init_captureHdrFrameShared = __esm({
|
|
|
120203
120834
|
"use strict";
|
|
120204
120835
|
init_src();
|
|
120205
120836
|
init_hdrCompositor();
|
|
120837
|
+
init_captureHdrResources();
|
|
120206
120838
|
init_hdrPerf();
|
|
120207
120839
|
}
|
|
120208
120840
|
});
|
|
@@ -120210,7 +120842,7 @@ var init_captureHdrFrameShared = __esm({
|
|
|
120210
120842
|
// ../producer/src/services/render/stages/captureStreamingStage.ts
|
|
120211
120843
|
import { mkdtemp as mkdtemp2, writeFile as writeFile2 } from "fs/promises";
|
|
120212
120844
|
import { tmpdir as tmpdir7 } from "os";
|
|
120213
|
-
import { join as
|
|
120845
|
+
import { join as join51 } from "path";
|
|
120214
120846
|
function resolveDeStallTimeoutMs() {
|
|
120215
120847
|
const raw = process.env.HF_DE_STALL_MS ?? process.env.HF_DE_PARALLEL_STALL_MS;
|
|
120216
120848
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
@@ -120314,11 +120946,11 @@ function createDrainFrameGuard(args) {
|
|
|
120314
120946
|
return buf;
|
|
120315
120947
|
}
|
|
120316
120948
|
if (db < verifyMinDb) {
|
|
120317
|
-
const dumpDir = await mkdtemp2(
|
|
120949
|
+
const dumpDir = await mkdtemp2(join51(tmpdir7(), "hf-de-verify-fail-")).catch(() => null);
|
|
120318
120950
|
if (dumpDir) {
|
|
120319
120951
|
await Promise.all([
|
|
120320
|
-
writeFile2(
|
|
120321
|
-
writeFile2(
|
|
120952
|
+
writeFile2(join51(dumpDir, `frame-${idx}-de.jpg`), buf),
|
|
120953
|
+
writeFile2(join51(dumpDir, `frame-${idx}-truth.jpg`), truth)
|
|
120322
120954
|
]).catch(() => {
|
|
120323
120955
|
});
|
|
120324
120956
|
}
|
|
@@ -120788,263 +121420,6 @@ var init_hdrImageTransferCache = __esm({
|
|
|
120788
121420
|
}
|
|
120789
121421
|
});
|
|
120790
121422
|
|
|
120791
|
-
// ../producer/src/services/render/stages/captureHdrResources.ts
|
|
120792
|
-
import {
|
|
120793
|
-
closeSync as closeSync7,
|
|
120794
|
-
constants as constants3,
|
|
120795
|
-
fstatSync as fstatSync4,
|
|
120796
|
-
mkdirSync as mkdirSync24,
|
|
120797
|
-
mkdtempSync as mkdtempSync6,
|
|
120798
|
-
openSync as openSync6,
|
|
120799
|
-
readFileSync as readFileSync30,
|
|
120800
|
-
statfsSync as statfsSync2
|
|
120801
|
-
} from "fs";
|
|
120802
|
-
import { join as join51 } from "path";
|
|
120803
|
-
function tempDirSafePrefix(id) {
|
|
120804
|
-
const safe = id.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
|
|
120805
|
-
return safe || "video";
|
|
120806
|
-
}
|
|
120807
|
-
function planHdrResources(args) {
|
|
120808
|
-
const { composition, nativeHdrVideoIds, nativeHdrImageIds, projectDir, compiledDir } = args;
|
|
120809
|
-
const hdrVideoIds = composition.videos.filter((v2) => nativeHdrVideoIds.has(v2.id)).map((v2) => v2.id);
|
|
120810
|
-
const hdrVideoSrcPaths = /* @__PURE__ */ new Map();
|
|
120811
|
-
for (const v2 of composition.videos) {
|
|
120812
|
-
if (!hdrVideoIds.includes(v2.id)) continue;
|
|
120813
|
-
let srcPath = v2.src;
|
|
120814
|
-
if (!srcPath.startsWith("/")) {
|
|
120815
|
-
const fromCompiled = join51(compiledDir, srcPath);
|
|
120816
|
-
srcPath = args.existsSync(fromCompiled) ? fromCompiled : join51(projectDir, srcPath);
|
|
120817
|
-
}
|
|
120818
|
-
hdrVideoSrcPaths.set(v2.id, srcPath);
|
|
120819
|
-
}
|
|
120820
|
-
const hdrVideoStartTimes = /* @__PURE__ */ new Map();
|
|
120821
|
-
for (const v2 of composition.videos) {
|
|
120822
|
-
if (hdrVideoIds.includes(v2.id)) hdrVideoStartTimes.set(v2.id, v2.start);
|
|
120823
|
-
}
|
|
120824
|
-
const hdrImageStartTimes = /* @__PURE__ */ new Map();
|
|
120825
|
-
for (const img of composition.images) {
|
|
120826
|
-
if (nativeHdrImageIds.has(img.id)) hdrImageStartTimes.set(img.id, img.start);
|
|
120827
|
-
}
|
|
120828
|
-
return {
|
|
120829
|
-
hdrVideoIds,
|
|
120830
|
-
hdrVideoSrcPaths,
|
|
120831
|
-
hdrVideoStartTimes,
|
|
120832
|
-
hdrImageStartTimes,
|
|
120833
|
-
hdrExtractionDims: /* @__PURE__ */ new Map(),
|
|
120834
|
-
hdrImageFitInfo: /* @__PURE__ */ new Map()
|
|
120835
|
-
};
|
|
120836
|
-
}
|
|
120837
|
-
async function probeHdrExtractionDims(args) {
|
|
120838
|
-
const { domSession, nativeHdrIds, nativeHdrImageIds, composition, prep } = args;
|
|
120839
|
-
const uniqueStartTimes = [
|
|
120840
|
-
.../* @__PURE__ */ new Set([...prep.hdrVideoStartTimes.values(), ...prep.hdrImageStartTimes.values()])
|
|
120841
|
-
].sort((a, b2) => a - b2);
|
|
120842
|
-
for (const seekTime of uniqueStartTimes) {
|
|
120843
|
-
await domSession.page.evaluate((t2) => {
|
|
120844
|
-
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t2);
|
|
120845
|
-
}, seekTime);
|
|
120846
|
-
if (domSession.onBeforeCapture) {
|
|
120847
|
-
await domSession.onBeforeCapture(domSession.page, seekTime);
|
|
120848
|
-
}
|
|
120849
|
-
const stacking = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
120850
|
-
for (const el of stacking) {
|
|
120851
|
-
if (el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0 && !prep.hdrExtractionDims.has(el.id)) {
|
|
120852
|
-
prep.hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
120853
|
-
}
|
|
120854
|
-
if (el.isHdr && nativeHdrImageIds.has(el.id) && !prep.hdrImageFitInfo.has(el.id)) {
|
|
120855
|
-
prep.hdrImageFitInfo.set(el.id, { fit: el.objectFit, position: el.objectPosition });
|
|
120856
|
-
}
|
|
120857
|
-
}
|
|
120858
|
-
}
|
|
120859
|
-
for (const [imageId, startTime] of prep.hdrImageStartTimes) {
|
|
120860
|
-
if (prep.hdrExtractionDims.has(imageId)) continue;
|
|
120861
|
-
const img = composition.images.find((i2) => i2.id === imageId);
|
|
120862
|
-
if (!img) continue;
|
|
120863
|
-
const duration = img.end - img.start;
|
|
120864
|
-
const retryTime = startTime + Math.min(0.5, duration * 0.1);
|
|
120865
|
-
await domSession.page.evaluate((t2) => {
|
|
120866
|
-
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t2);
|
|
120867
|
-
}, retryTime);
|
|
120868
|
-
if (domSession.onBeforeCapture) {
|
|
120869
|
-
await domSession.onBeforeCapture(domSession.page, retryTime);
|
|
120870
|
-
}
|
|
120871
|
-
const retryStacking = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
120872
|
-
for (const el of retryStacking) {
|
|
120873
|
-
if (el.id === imageId && el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0) {
|
|
120874
|
-
prep.hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
120875
|
-
if (!prep.hdrImageFitInfo.has(el.id)) {
|
|
120876
|
-
prep.hdrImageFitInfo.set(el.id, { fit: el.objectFit, position: el.objectPosition });
|
|
120877
|
-
}
|
|
120878
|
-
break;
|
|
120879
|
-
}
|
|
120880
|
-
}
|
|
120881
|
-
}
|
|
120882
|
-
}
|
|
120883
|
-
function estimateHdrExtractionBytes(videos, fps) {
|
|
120884
|
-
let total = 0;
|
|
120885
|
-
for (const v2 of videos) {
|
|
120886
|
-
const frames = Math.ceil(Math.max(0, v2.durationSeconds) * fps);
|
|
120887
|
-
total += frames * v2.width * v2.height * 6;
|
|
120888
|
-
}
|
|
120889
|
-
return total;
|
|
120890
|
-
}
|
|
120891
|
-
function assertHdrExtractionDiskHeadroom(framesDir, plannedVideos, fps, log2) {
|
|
120892
|
-
const estimatedBytes = estimateHdrExtractionBytes(plannedVideos, fps);
|
|
120893
|
-
let freeBytes;
|
|
120894
|
-
try {
|
|
120895
|
-
const stat3 = statfsSync2(framesDir);
|
|
120896
|
-
freeBytes = stat3.bavail * stat3.bsize;
|
|
120897
|
-
} catch {
|
|
120898
|
-
return;
|
|
120899
|
-
}
|
|
120900
|
-
const estimatedGb = (estimatedBytes / 1e9).toFixed(1);
|
|
120901
|
-
if (estimatedBytes > freeBytes * HDR_EXTRACTION_HEADROOM_FRACTION) {
|
|
120902
|
-
throw new Error(
|
|
120903
|
-
`HDR pre-extraction needs ~${estimatedGb} GB of raw 16-bit frames but only ${(freeBytes / 1e9).toFixed(1)} GB is free at ${framesDir}. If the composition doesn't need HDR output, re-run with --sdr; otherwise free up disk space and retry.`
|
|
120904
|
-
);
|
|
120905
|
-
}
|
|
120906
|
-
if (estimatedBytes > HDR_EXTRACTION_WARN_BYTES) {
|
|
120907
|
-
log2.warn(
|
|
120908
|
-
`HDR pre-extraction will write ~${estimatedGb} GB of raw 16-bit frames (pass --sdr to skip if HDR output isn't needed)`,
|
|
120909
|
-
{ estimatedBytes, freeBytes }
|
|
120910
|
-
);
|
|
120911
|
-
}
|
|
120912
|
-
}
|
|
120913
|
-
async function extractHdrVideoFrames(args) {
|
|
120914
|
-
const { job, log: log2, framesDir, composition, prep, width, height, abortSignal, hdrDiagnostics } = args;
|
|
120915
|
-
const out = /* @__PURE__ */ new Map();
|
|
120916
|
-
mkdirSync24(framesDir, { recursive: true });
|
|
120917
|
-
const plannedVideos = [];
|
|
120918
|
-
for (const [videoId] of prep.hdrVideoSrcPaths) {
|
|
120919
|
-
const video = composition.videos.find((v2) => v2.id === videoId);
|
|
120920
|
-
if (!video) continue;
|
|
120921
|
-
const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
|
|
120922
|
-
plannedVideos.push({
|
|
120923
|
-
durationSeconds: video.end - video.start,
|
|
120924
|
-
width: dims.width,
|
|
120925
|
-
height: dims.height
|
|
120926
|
-
});
|
|
120927
|
-
}
|
|
120928
|
-
assertHdrExtractionDiskHeadroom(framesDir, plannedVideos, fpsToNumber(job.config.fps), log2);
|
|
120929
|
-
for (const [videoId, srcPath] of prep.hdrVideoSrcPaths) {
|
|
120930
|
-
const video = composition.videos.find((v2) => v2.id === videoId);
|
|
120931
|
-
if (!video) continue;
|
|
120932
|
-
mkdirSync24(framesDir, { recursive: true });
|
|
120933
|
-
const frameDir = mkdtempSync6(join51(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
|
|
120934
|
-
const duration = video.end - video.start;
|
|
120935
|
-
const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
|
|
120936
|
-
const rawPath = join51(frameDir, "frames.rgb48le");
|
|
120937
|
-
const ffmpegArgs = [
|
|
120938
|
-
"-ss",
|
|
120939
|
-
String(video.mediaStart),
|
|
120940
|
-
"-i",
|
|
120941
|
-
srcPath,
|
|
120942
|
-
"-t",
|
|
120943
|
-
String(duration),
|
|
120944
|
-
"-r",
|
|
120945
|
-
fpsToFfmpegArg(job.config.fps),
|
|
120946
|
-
"-vf",
|
|
120947
|
-
`scale=${dims.width}:${dims.height}:force_original_aspect_ratio=increase,crop=${dims.width}:${dims.height}`,
|
|
120948
|
-
"-pix_fmt",
|
|
120949
|
-
"rgb48le",
|
|
120950
|
-
"-f",
|
|
120951
|
-
"rawvideo",
|
|
120952
|
-
"-y",
|
|
120953
|
-
rawPath
|
|
120954
|
-
];
|
|
120955
|
-
const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
|
|
120956
|
-
if (!result.success) {
|
|
120957
|
-
hdrDiagnostics.videoExtractionFailures += 1;
|
|
120958
|
-
log2.error("HDR frame pre-extraction failed; aborting render", {
|
|
120959
|
-
videoId,
|
|
120960
|
-
srcPath,
|
|
120961
|
-
stderr: result.stderr.slice(-400)
|
|
120962
|
-
});
|
|
120963
|
-
throw new Error(
|
|
120964
|
-
`HDR frame extraction failed for video "${videoId}". Aborting render to avoid shipping black HDR layers.`
|
|
120965
|
-
);
|
|
120966
|
-
}
|
|
120967
|
-
const frameSize = dims.width * dims.height * 6;
|
|
120968
|
-
const fd = openSync6(rawPath, constants3.O_RDONLY | NO_FOLLOW_FLAG);
|
|
120969
|
-
let handedOff = false;
|
|
120970
|
-
try {
|
|
120971
|
-
const frameCount = Math.floor(fstatSync4(fd).size / frameSize);
|
|
120972
|
-
if (frameCount < 1) {
|
|
120973
|
-
hdrDiagnostics.videoExtractionFailures += 1;
|
|
120974
|
-
throw new Error(
|
|
120975
|
-
`HDR frame extraction produced no frames for video "${videoId}". Aborting render to avoid shipping black HDR layers.`
|
|
120976
|
-
);
|
|
120977
|
-
}
|
|
120978
|
-
out.set(videoId, {
|
|
120979
|
-
dir: frameDir,
|
|
120980
|
-
rawPath,
|
|
120981
|
-
fd,
|
|
120982
|
-
width: dims.width,
|
|
120983
|
-
height: dims.height,
|
|
120984
|
-
frameSize,
|
|
120985
|
-
frameCount,
|
|
120986
|
-
scratch: Buffer.allocUnsafe(frameSize)
|
|
120987
|
-
});
|
|
120988
|
-
handedOff = true;
|
|
120989
|
-
} finally {
|
|
120990
|
-
if (!handedOff) closeSync7(fd);
|
|
120991
|
-
}
|
|
120992
|
-
}
|
|
120993
|
-
return out;
|
|
120994
|
-
}
|
|
120995
|
-
function decodeHdrImageBuffers(args) {
|
|
120996
|
-
const { log: log2, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
|
|
120997
|
-
const out = /* @__PURE__ */ new Map();
|
|
120998
|
-
for (const [imageId, srcPath] of hdrImageSrcPaths) {
|
|
120999
|
-
try {
|
|
121000
|
-
const decoded = decodePngToRgb48le(readFileSync30(srcPath));
|
|
121001
|
-
const layout2 = prep.hdrExtractionDims.get(imageId);
|
|
121002
|
-
const fitInfo = prep.hdrImageFitInfo.get(imageId);
|
|
121003
|
-
if (layout2 && (layout2.width !== decoded.width || layout2.height !== decoded.height)) {
|
|
121004
|
-
const fit = normalizeObjectFit(fitInfo?.fit);
|
|
121005
|
-
const resampled = resampleRgb48leObjectFit(
|
|
121006
|
-
decoded.data,
|
|
121007
|
-
decoded.width,
|
|
121008
|
-
decoded.height,
|
|
121009
|
-
layout2.width,
|
|
121010
|
-
layout2.height,
|
|
121011
|
-
fit,
|
|
121012
|
-
fitInfo?.position
|
|
121013
|
-
);
|
|
121014
|
-
out.set(imageId, { data: resampled, width: layout2.width, height: layout2.height });
|
|
121015
|
-
} else {
|
|
121016
|
-
out.set(imageId, {
|
|
121017
|
-
data: Buffer.from(decoded.data),
|
|
121018
|
-
width: decoded.width,
|
|
121019
|
-
height: decoded.height
|
|
121020
|
-
});
|
|
121021
|
-
}
|
|
121022
|
-
} catch (err) {
|
|
121023
|
-
hdrDiagnostics.imageDecodeFailures += 1;
|
|
121024
|
-
log2.error("HDR image decode failed; aborting render", {
|
|
121025
|
-
imageId,
|
|
121026
|
-
srcPath,
|
|
121027
|
-
error: err instanceof Error ? err.message : String(err)
|
|
121028
|
-
});
|
|
121029
|
-
throw new Error(
|
|
121030
|
-
`HDR image decode failed for image "${imageId}". Aborting render to avoid shipping missing HDR image layers.`
|
|
121031
|
-
);
|
|
121032
|
-
}
|
|
121033
|
-
}
|
|
121034
|
-
return out;
|
|
121035
|
-
}
|
|
121036
|
-
var NO_FOLLOW_FLAG, HDR_EXTRACTION_HEADROOM_FRACTION, HDR_EXTRACTION_WARN_BYTES;
|
|
121037
|
-
var init_captureHdrResources = __esm({
|
|
121038
|
-
"../producer/src/services/render/stages/captureHdrResources.ts"() {
|
|
121039
|
-
"use strict";
|
|
121040
|
-
init_src();
|
|
121041
|
-
init_dist3();
|
|
121042
|
-
NO_FOLLOW_FLAG = constants3.O_NOFOLLOW ?? 0;
|
|
121043
|
-
HDR_EXTRACTION_HEADROOM_FRACTION = 0.9;
|
|
121044
|
-
HDR_EXTRACTION_WARN_BYTES = 1e10;
|
|
121045
|
-
}
|
|
121046
|
-
});
|
|
121047
|
-
|
|
121048
121423
|
// ../producer/src/services/render/stages/captureHdrSequentialLoop.ts
|
|
121049
121424
|
import { join as join53 } from "path";
|
|
121050
121425
|
async function runSequentialLayeredFrameLoop(input2) {
|
|
@@ -121714,6 +122089,7 @@ async function runCaptureHdrStage(input2) {
|
|
|
121714
122089
|
let hdrEncoder = null;
|
|
121715
122090
|
let hdrEncoderClosed = false;
|
|
121716
122091
|
let domSessionClosed = false;
|
|
122092
|
+
let releaseHdrExtractionReservation = null;
|
|
121717
122093
|
const hdrVideoFrameSources = /* @__PURE__ */ new Map();
|
|
121718
122094
|
try {
|
|
121719
122095
|
await initializeSession(domSession);
|
|
@@ -121790,7 +122166,8 @@ async function runCaptureHdrStage(input2) {
|
|
|
121790
122166
|
abortSignal,
|
|
121791
122167
|
hdrDiagnostics
|
|
121792
122168
|
});
|
|
121793
|
-
|
|
122169
|
+
releaseHdrExtractionReservation = extracted.releaseReservation;
|
|
122170
|
+
for (const [id, source] of extracted.sources) hdrVideoFrameSources.set(id, source);
|
|
121794
122171
|
const hdrImageBuffers = decodeHdrImageBuffers({
|
|
121795
122172
|
log: log2,
|
|
121796
122173
|
hdrImageSrcPaths,
|
|
@@ -121939,9 +122316,11 @@ async function runCaptureHdrStage(input2) {
|
|
|
121939
122316
|
});
|
|
121940
122317
|
}
|
|
121941
122318
|
for (const frameSource of hdrVideoFrameSources.values()) {
|
|
121942
|
-
|
|
122319
|
+
cleanupHdrVideoFrameSource(frameSource, log2);
|
|
121943
122320
|
}
|
|
121944
122321
|
hdrVideoFrameSources.clear();
|
|
122322
|
+
releaseHdrExtractionReservation?.();
|
|
122323
|
+
releaseHdrExtractionReservation = null;
|
|
121945
122324
|
}
|
|
121946
122325
|
return {
|
|
121947
122326
|
lastBrowserConsole,
|
|
@@ -125376,6 +125755,22 @@ function parseServerQuality(value) {
|
|
|
125376
125755
|
function parseServerFormat(value) {
|
|
125377
125756
|
return value === "mp4" || value === "webm" || value === "mov" ? value : void 0;
|
|
125378
125757
|
}
|
|
125758
|
+
function parseServerOutputDynamicRange(value) {
|
|
125759
|
+
return value === "auto" || value === "hdr" || value === "sdr" ? value : void 0;
|
|
125760
|
+
}
|
|
125761
|
+
function parseLegacyServerHdrMode(value) {
|
|
125762
|
+
return value === "auto" || value === "force-hdr" || value === "force-sdr" ? value : void 0;
|
|
125763
|
+
}
|
|
125764
|
+
function fromRenderHdrMode(hdrMode) {
|
|
125765
|
+
if (hdrMode === "force-hdr") return "hdr";
|
|
125766
|
+
if (hdrMode === "force-sdr") return "sdr";
|
|
125767
|
+
return hdrMode;
|
|
125768
|
+
}
|
|
125769
|
+
function toRenderHdrMode(outputDynamicRange) {
|
|
125770
|
+
if (outputDynamicRange === "hdr") return "force-hdr";
|
|
125771
|
+
if (outputDynamicRange === "sdr") return "force-sdr";
|
|
125772
|
+
return outputDynamicRange;
|
|
125773
|
+
}
|
|
125379
125774
|
function parseRenderOptions(body) {
|
|
125380
125775
|
const fps = parseServerFps(body.fps);
|
|
125381
125776
|
const quality = parseServerQuality(body.quality);
|
|
@@ -125386,6 +125781,7 @@ function parseRenderOptions(body) {
|
|
|
125386
125781
|
const outputPath = parseOutputCandidate(body);
|
|
125387
125782
|
const entryFile = nonEmptyString(body.entryFile);
|
|
125388
125783
|
const format = parseServerFormat(body.format);
|
|
125784
|
+
const outputDynamicRange = parseServerOutputDynamicRange(body.outputDynamicRange) ?? fromRenderHdrMode(parseLegacyServerHdrMode(body.hdrMode));
|
|
125389
125785
|
const videoFrameFormat = isVideoFrameFormat(body.videoFrameFormat) ? body.videoFrameFormat : void 0;
|
|
125390
125786
|
const { variables, outputResolution, outputResolutionAspectAgnostic } = parseRenderOverrides(body);
|
|
125391
125787
|
return {
|
|
@@ -125398,6 +125794,7 @@ function parseRenderOptions(body) {
|
|
|
125398
125794
|
strictness,
|
|
125399
125795
|
entryFile,
|
|
125400
125796
|
format,
|
|
125797
|
+
outputDynamicRange,
|
|
125401
125798
|
variables,
|
|
125402
125799
|
outputResolution,
|
|
125403
125800
|
outputResolutionAspectAgnostic,
|
|
@@ -125430,7 +125827,8 @@ function buildRenderJobConfig(input2, outputPath, log2) {
|
|
|
125430
125827
|
variables: input2.variables,
|
|
125431
125828
|
outputResolution: input2.outputResolution,
|
|
125432
125829
|
outputResolutionAspectAgnostic: input2.outputResolutionAspectAgnostic,
|
|
125433
|
-
videoFrameFormat: input2.videoFrameFormat
|
|
125830
|
+
videoFrameFormat: input2.videoFrameFormat,
|
|
125831
|
+
hdrMode: toRenderHdrMode(input2.outputDynamicRange)
|
|
125434
125832
|
}
|
|
125435
125833
|
});
|
|
125436
125834
|
return renderConfigFromRequest(request, { logger: log2 });
|
|
@@ -125446,6 +125844,17 @@ function validateRenderOverrides(body) {
|
|
|
125446
125844
|
if (body.variables !== void 0 && !isPlainObject4(body.variables)) {
|
|
125447
125845
|
return 'variables must be a JSON object keyed by variable id (e.g. {"title":"Hello"})';
|
|
125448
125846
|
}
|
|
125847
|
+
if (body.outputDynamicRange !== void 0 && parseServerOutputDynamicRange(body.outputDynamicRange) === void 0) {
|
|
125848
|
+
return 'outputDynamicRange must be one of: "auto", "hdr", "sdr"';
|
|
125849
|
+
}
|
|
125850
|
+
const legacyHdrMode = parseLegacyServerHdrMode(body.hdrMode);
|
|
125851
|
+
if (body.hdrMode !== void 0 && legacyHdrMode === void 0) {
|
|
125852
|
+
return 'legacy hdrMode must be one of: "auto", "force-hdr", "force-sdr"';
|
|
125853
|
+
}
|
|
125854
|
+
const outputDynamicRange = parseServerOutputDynamicRange(body.outputDynamicRange);
|
|
125855
|
+
if (outputDynamicRange !== void 0 && legacyHdrMode !== void 0 && outputDynamicRange !== fromRenderHdrMode(legacyHdrMode)) {
|
|
125856
|
+
return "outputDynamicRange and legacy hdrMode must describe the same output policy";
|
|
125857
|
+
}
|
|
125449
125858
|
return validateOutputResolutionOverride(body);
|
|
125450
125859
|
}
|
|
125451
125860
|
function validateOutputResolutionOverride(body) {
|
|
@@ -126386,6 +126795,10 @@ function readVideoMetadata(value, field) {
|
|
|
126386
126795
|
record.videoStreamDurationSeconds,
|
|
126387
126796
|
`${field}.videoStreamDurationSeconds`
|
|
126388
126797
|
),
|
|
126798
|
+
// Plans written before stream-start metadata existed implicitly used the
|
|
126799
|
+
// overwhelmingly common start-at-zero domain. Preserve that compatibility
|
|
126800
|
+
// while carrying non-zero edit-list/transport timestamps in new plans.
|
|
126801
|
+
videoStreamStartSeconds: record.videoStreamStartSeconds === void 0 ? 0 : readFiniteNumber(record.videoStreamStartSeconds, `${field}.videoStreamStartSeconds`),
|
|
126389
126802
|
width: readPositiveInteger(record.width, `${field}.width`),
|
|
126390
126803
|
height: readPositiveInteger(record.height, `${field}.height`),
|
|
126391
126804
|
fps: readFiniteNumber(record.fps, `${field}.fps`),
|