@hyperframes/producer 0.8.179 → 0.8.181
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 +2 -2
- package/dist/distributed.js +139 -48
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +54 -54
- package/dist/index.js +138 -48
- package/dist/public-server.js +78 -23
- package/dist/services/distributed/assemble.d.ts +4 -2
- package/dist/services/distributed/chunkAudio.d.ts +8 -0
- package/dist/services/distributed/chunkFrames.d.ts +2 -1
- package/dist/services/distributed/planProtocol.d.ts +1 -0
- package/dist/services/distributed/planV2Execution.d.ts +4 -4
- package/dist/services/distributed/renderChunk.d.ts +23 -3
- package/dist/services/render/audioTruePeak.d.ts +12 -0
- package/package.json +7 -7
package/dist/distributed.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ export { buildChunkSlices, measurePlanDirBytes, plan, rejectUnsupportedDistribut
|
|
|
33
33
|
export { createPlanV2FromExecutionPlan, createPlanV2FromV1, getPlanV2ExecutionPlanHash, isPlanV2DeferredSourceArtifact, listPlanV2ArtifactsForTarget, materializePlanV2Target, planV2, planV2WithPublisher, publishPlanV2FromExecutionPlan, publishPlanV2FromV1, readPlanV2Manifest, validatePlanV2MaterializedTarget, PLAN_V2_INTEGRITY_UNRECOVERABLE, PLAN_V2_MATERIALIZATION_MARKER, PlanV2IntegrityError, type PlanV2Artifact, type PlanV2Limitations, type PlanV2Manifest, type PlanV2MaterializeOptions, type PlanV2MaterializationResult, type PlanV2MaterializationTarget, type PlanV2RemoteSources, type PlanV2Result, type PlanV2WithPublisherOptions, } from "./services/distributed/planV2.js";
|
|
34
34
|
export { LocalPlanV2ArtifactPublisher, type LocalPlanV2ArtifactPublisherOptions, type PlanV2ArtifactPublisher, type PlanV2PublishBlob, } from "./services/distributed/planV2Publisher.js";
|
|
35
35
|
export { assembleV2, renderChunkV2 } from "./services/distributed/planV2Execution.js";
|
|
36
|
-
export { applyRuntimeEnvSnapshot, readWebGlVendorInfoFromCanvas, renderChunk, type ChunkRenderer, type ChunkResult, type EffectiveChunkResult, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, PLAN_HASH_MISMATCH, RenderChunkValidationError, } from "./services/distributed/renderChunk.js";
|
|
37
|
-
export { assemble, chunksAreStitchedMp4, type AssembleResult, } from "./services/distributed/assemble.js";
|
|
36
|
+
export { applyRuntimeEnvSnapshot, readWebGlVendorInfoFromCanvas, renderChunk, type ChunkFrameRange, type ChunkRenderer, type ChunkResult, type EffectiveChunkResult, type RenderChunkOptions, CHUNK_FRAME_RANGE_INVALID, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, PLAN_HASH_MISMATCH, RenderChunkValidationError, } from "./services/distributed/renderChunk.js";
|
|
37
|
+
export { assemble, chunksAreStitchedMp4, type AssembleChunkPaths, type AssembleResult, } from "./services/distributed/assemble.js";
|
|
38
38
|
export { muxChunkWithAudio } from "./services/distributed/chunkAudio.js";
|
|
39
39
|
export { buildStitchedHeader, readMp4Layout, type Mp4Layout, type ReadRange, } from "./services/distributed/mp4Stitch.js";
|
|
40
40
|
export { InvalidConfigError, type SerializableDistributedRenderConfig, validateDistributedRenderConfig, validateVariablesPayload, } from "./services/distributed/renderConfigValidation.js";
|
package/dist/distributed.js
CHANGED
|
@@ -5709,14 +5709,84 @@ import { mkdtempSync as mkdtempSync2, renameSync as renameSync2, rmSync as rmSyn
|
|
|
5709
5709
|
import { dirname as dirname6, join as join13 } from "node:path";
|
|
5710
5710
|
import {
|
|
5711
5711
|
DEFAULT_CONFIG,
|
|
5712
|
-
describeFfmpegFailure,
|
|
5712
|
+
describeFfmpegFailure as describeFfmpegFailure2,
|
|
5713
5713
|
extractAudioMetadata as extractAudioMetadata2,
|
|
5714
5714
|
getFfprobeBinary,
|
|
5715
5715
|
ManagedChildProcess,
|
|
5716
|
-
runFfmpeg as
|
|
5716
|
+
runFfmpeg as runFfmpeg5,
|
|
5717
5717
|
trackChildProcess
|
|
5718
5718
|
} from "@hyperframes/engine";
|
|
5719
5719
|
import { redactKnownPaths, redactTelemetryString as redactTelemetryString3 } from "@hyperframes/core";
|
|
5720
|
+
|
|
5721
|
+
// src/services/render/audioTruePeak.ts
|
|
5722
|
+
import { aacSegmentCount, describeFfmpegFailure, runFfmpeg as runFfmpeg4 } from "@hyperframes/engine";
|
|
5723
|
+
var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
5724
|
+
function checkedDbfs(value) {
|
|
5725
|
+
if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
|
|
5726
|
+
throw new Error("[audioTruePeak] FFmpeg did not report AAC true peak");
|
|
5727
|
+
}
|
|
5728
|
+
return value;
|
|
5729
|
+
}
|
|
5730
|
+
async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
|
|
5731
|
+
let firstBlock;
|
|
5732
|
+
let laterBlocks = Number.NEGATIVE_INFINITY;
|
|
5733
|
+
let partialLine = "";
|
|
5734
|
+
const readLines = (chunk) => {
|
|
5735
|
+
const lines = (partialLine + chunk).split(/\r|\n/);
|
|
5736
|
+
partialLine = lines.pop() ?? "";
|
|
5737
|
+
for (const line of lines) {
|
|
5738
|
+
const ftpk = /FTPK:([^d]*)dBFS/.exec(line)?.[1];
|
|
5739
|
+
if (!ftpk) continue;
|
|
5740
|
+
const block = Math.max(...ftpk.trim().split(/\s+/).map(toDbfs));
|
|
5741
|
+
if (firstBlock === void 0) firstBlock = block;
|
|
5742
|
+
else laterBlocks = Math.max(laterBlocks, block);
|
|
5743
|
+
}
|
|
5744
|
+
};
|
|
5745
|
+
const result = await runFfmpeg4(
|
|
5746
|
+
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
|
|
5747
|
+
{ signal, timeout: timeoutMs, onStderr: readLines }
|
|
5748
|
+
);
|
|
5749
|
+
if (!result.success) {
|
|
5750
|
+
throw new Error(`[audioTruePeak] ${describeFfmpegFailure(result, timeoutMs)}`);
|
|
5751
|
+
}
|
|
5752
|
+
const summary = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
|
|
5753
|
+
return {
|
|
5754
|
+
peak: checkedDbfs(toDbfs(summary.at(-1)?.[1] ?? "nan")),
|
|
5755
|
+
firstBlock: checkedDbfs(firstBlock ?? Number.NEGATIVE_INFINITY),
|
|
5756
|
+
laterBlocks: checkedDbfs(laterBlocks)
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
async function measureTruePeakDbfs(audioPath, options) {
|
|
5760
|
+
const { timeoutMs, signal, segments } = options;
|
|
5761
|
+
const wholeFile = async () => (await scanTruePeak(audioPath, "", timeoutMs, signal)).peak;
|
|
5762
|
+
const block = 4800;
|
|
5763
|
+
const count = segments?.sampleRate === 48e3 ? aacSegmentCount(segments.durationSeconds) : 1;
|
|
5764
|
+
if (!segments || count === 1) return wholeFile();
|
|
5765
|
+
const cut = (k) => k * Math.floor(segments.durationSeconds * 10 / count) * block;
|
|
5766
|
+
const peers = new AbortController();
|
|
5767
|
+
const segmentSignal = signal ? AbortSignal.any([signal, peers.signal]) : peers.signal;
|
|
5768
|
+
const scans = await Promise.all(
|
|
5769
|
+
Array.from({ length: count }, (_, k) => {
|
|
5770
|
+
const start = k === 0 ? 0 : cut(k) - block;
|
|
5771
|
+
const end = k === count - 1 ? "" : `:end_sample=${cut(k + 1)}`;
|
|
5772
|
+
return scanTruePeak(
|
|
5773
|
+
audioPath,
|
|
5774
|
+
`atrim=start_sample=${start}${end},`,
|
|
5775
|
+
timeoutMs,
|
|
5776
|
+
segmentSignal
|
|
5777
|
+
);
|
|
5778
|
+
})
|
|
5779
|
+
).catch((error) => {
|
|
5780
|
+
peers.abort();
|
|
5781
|
+
throw error;
|
|
5782
|
+
});
|
|
5783
|
+
const last = scans[count - 1];
|
|
5784
|
+
const counted = Math.max(scans[0].peak, ...scans.slice(1).map((scan) => scan.laterBlocks));
|
|
5785
|
+
if (last.peak > last.firstBlock || last.peak <= counted) return Math.max(counted, last.peak);
|
|
5786
|
+
return wholeFile();
|
|
5787
|
+
}
|
|
5788
|
+
|
|
5789
|
+
// src/services/render/audioPadTrim.ts
|
|
5720
5790
|
var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
|
|
5721
5791
|
var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
|
|
5722
5792
|
var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
|
|
@@ -5816,7 +5886,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
5816
5886
|
const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
|
|
5817
5887
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
5818
5888
|
const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
|
|
5819
|
-
const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg ? void 0 : (audioPath, signal) =>
|
|
5889
|
+
const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg ? void 0 : (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }));
|
|
5820
5890
|
const [videoResult, audioResult] = await Promise.allSettled([
|
|
5821
5891
|
probeVideo(input.videoPath),
|
|
5822
5892
|
probeAudio(input.audioPath, input.signal)
|
|
@@ -5925,7 +5995,11 @@ async function constrainAacTruePeak(input) {
|
|
|
5925
5995
|
audioPath: input.audioPath,
|
|
5926
5996
|
targetDurationSeconds: input.targetDurationSeconds,
|
|
5927
5997
|
signal: input.signal,
|
|
5928
|
-
probeTruePeak: (audioPath, signal) =>
|
|
5998
|
+
probeTruePeak: async (audioPath, signal) => {
|
|
5999
|
+
const { sampleRate } = await extractAudioMetadata2(audioPath, { signal });
|
|
6000
|
+
const segments = { durationSeconds: input.targetDurationSeconds, sampleRate };
|
|
6001
|
+
return measureTruePeakDbfs(audioPath, { timeoutMs, signal, segments });
|
|
6002
|
+
},
|
|
5929
6003
|
runner: (args) => defaultRunFfmpeg(args, timeoutMs, input.signal)
|
|
5930
6004
|
});
|
|
5931
6005
|
}
|
|
@@ -6062,28 +6136,12 @@ async function defaultProbeAudioInfo(audioPath, signal) {
|
|
|
6062
6136
|
audioCodec: metadata.audioCodec
|
|
6063
6137
|
};
|
|
6064
6138
|
}
|
|
6065
|
-
async function defaultProbeAudioTruePeakDbfs(audioPath, timeoutMs, signal) {
|
|
6066
|
-
const result = await runFfmpeg4(
|
|
6067
|
-
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", "ebur128=peak=true", "-f", "null", "-"],
|
|
6068
|
-
{ signal, timeout: timeoutMs }
|
|
6069
|
-
);
|
|
6070
|
-
if (!result.success) {
|
|
6071
|
-
throw new Error(`[audioPadTrim] ${describeFfmpegFailure(result, timeoutMs)}`);
|
|
6072
|
-
}
|
|
6073
|
-
const matches = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
|
|
6074
|
-
const token = matches.at(-1)?.[1]?.toLowerCase();
|
|
6075
|
-
const value = token === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
6076
|
-
if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
|
|
6077
|
-
throw new Error("[audioPadTrim] FFmpeg did not report AAC true peak");
|
|
6078
|
-
}
|
|
6079
|
-
return value;
|
|
6080
|
-
}
|
|
6081
6139
|
async function defaultRunFfmpeg(args, timeoutMs, signal) {
|
|
6082
|
-
const result = await
|
|
6140
|
+
const result = await runFfmpeg5(args, { signal, timeout: timeoutMs });
|
|
6083
6141
|
if (result.success) return { success: true };
|
|
6084
6142
|
return {
|
|
6085
6143
|
success: false,
|
|
6086
|
-
error: `[audioPadTrim] ${
|
|
6144
|
+
error: `[audioPadTrim] ${describeFfmpegFailure2(result, timeoutMs)}`,
|
|
6087
6145
|
failureReason: result.failureReason
|
|
6088
6146
|
};
|
|
6089
6147
|
}
|
|
@@ -6773,6 +6831,7 @@ var FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
|
6773
6831
|
var PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
6774
6832
|
var MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
6775
6833
|
var CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
6834
|
+
var CHUNK_FRAME_RANGE_INVALID = "CHUNK_FRAME_RANGE_INVALID";
|
|
6776
6835
|
var MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
6777
6836
|
var RenderChunkValidationError = class extends Error {
|
|
6778
6837
|
code;
|
|
@@ -7024,7 +7083,8 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
7024
7083
|
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
7025
7084
|
acceptsLegacyV1WithoutDescriptor: true
|
|
7026
7085
|
})
|
|
7027
|
-
})
|
|
7086
|
+
}),
|
|
7087
|
+
chunkFrameRange: true
|
|
7028
7088
|
});
|
|
7029
7089
|
function getDistributedRenderCapabilities() {
|
|
7030
7090
|
return DISTRIBUTED_RENDER_CAPABILITIES;
|
|
@@ -7373,7 +7433,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
7373
7433
|
import { closeSync as closeSync4, existsSync as existsSync14, ftruncateSync, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
7374
7434
|
import { writeFileSync as writeFileSync5, writeSync } from "node:fs";
|
|
7375
7435
|
import { join as join20 } from "node:path";
|
|
7376
|
-
import { appendRenderProvenanceArgs, describeFfmpegFailure as
|
|
7436
|
+
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
|
|
7377
7437
|
|
|
7378
7438
|
// src/services/distributed/mp4Stitch.ts
|
|
7379
7439
|
import {
|
|
@@ -7739,6 +7799,18 @@ function audioCutForChunk(grid, chunks, index, fps) {
|
|
|
7739
7799
|
const durationSeconds = end === grid.packetCount ? null : cutSeconds(end) - (seekSeconds ?? 0);
|
|
7740
7800
|
return { seekSeconds, durationSeconds, firstPacket: first, packetCount: end - first };
|
|
7741
7801
|
}
|
|
7802
|
+
function chunksWithSubRange(chunks, index, range) {
|
|
7803
|
+
const slice = chunks[index];
|
|
7804
|
+
const parts = [
|
|
7805
|
+
{ startFrame: slice.startFrame, endFrame: range.startFrame },
|
|
7806
|
+
range,
|
|
7807
|
+
{ startFrame: range.endFrame, endFrame: slice.endFrame }
|
|
7808
|
+
].filter((part) => part.endFrame > part.startFrame);
|
|
7809
|
+
const split = [...chunks.slice(0, index), ...parts, ...chunks.slice(index + 1)].map(
|
|
7810
|
+
(chunk, i) => ({ index: i, startFrame: chunk.startFrame, endFrame: chunk.endFrame })
|
|
7811
|
+
);
|
|
7812
|
+
return { chunks: split, index: range.startFrame > slice.startFrame ? index + 1 : index };
|
|
7813
|
+
}
|
|
7742
7814
|
async function probeAacPacketGrid(audioPath) {
|
|
7743
7815
|
const probe = await runFfprobeJson([
|
|
7744
7816
|
"-v",
|
|
@@ -7851,10 +7923,10 @@ async function muxChunkWithAudio(input) {
|
|
|
7851
7923
|
const args = [...chunkMuxArgs(input, cut), "-c", "copy"];
|
|
7852
7924
|
appendRenderProvenanceArgs(args, input.outputPath);
|
|
7853
7925
|
args.push("-y", input.outputPath);
|
|
7854
|
-
const result = await
|
|
7926
|
+
const result = await runFfmpeg6(args, { signal: input.signal, timeout: input.timeoutMs });
|
|
7855
7927
|
if (!result.success) {
|
|
7856
7928
|
throw new Error(
|
|
7857
|
-
`[chunkAudio] chunk mux failed: ${
|
|
7929
|
+
`[chunkAudio] chunk mux failed: ${describeFfmpegFailure3(result, input.timeoutMs)}`
|
|
7858
7930
|
);
|
|
7859
7931
|
}
|
|
7860
7932
|
const slice = input.chunks[input.chunkIndex];
|
|
@@ -9684,12 +9756,12 @@ import {
|
|
|
9684
9756
|
import { dirname as dirname12, join as join26 } from "node:path";
|
|
9685
9757
|
import {
|
|
9686
9758
|
appendRenderProvenanceArgs as appendRenderProvenanceArgs2,
|
|
9687
|
-
describeFfmpegFailure as
|
|
9759
|
+
describeFfmpegFailure as describeFfmpegFailure4,
|
|
9688
9760
|
getEncoderPreset as getEncoderPreset5,
|
|
9689
9761
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME4,
|
|
9690
9762
|
muxVideoWithAudio as muxVideoWithAudio2,
|
|
9691
9763
|
resolveConfig as resolveConfig4,
|
|
9692
|
-
runFfmpeg as
|
|
9764
|
+
runFfmpeg as runFfmpeg7
|
|
9693
9765
|
} from "@hyperframes/engine";
|
|
9694
9766
|
import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
|
|
9695
9767
|
function cfrRateControlArgs(locked) {
|
|
@@ -9699,11 +9771,12 @@ function cfrRateControlArgs(locked) {
|
|
|
9699
9771
|
}
|
|
9700
9772
|
return ["-preset", preset.preset, "-crf", String(locked.crf ?? preset.quality)];
|
|
9701
9773
|
}
|
|
9702
|
-
async function assemble(planDir,
|
|
9774
|
+
async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
9703
9775
|
const start = Date.now();
|
|
9704
9776
|
const log = options?.logger ?? defaultLogger;
|
|
9705
9777
|
const cfr = options?.cfr === true;
|
|
9706
|
-
const plan2 = readAssemblePlan(planDir,
|
|
9778
|
+
const plan2 = readAssemblePlan(planDir, chunkOutputs);
|
|
9779
|
+
const chunkPaths = chunkOutputs.flat();
|
|
9707
9780
|
if (plan2.dimensions.format === "png-sequence") {
|
|
9708
9781
|
return mergePngFrameDirs(chunkPaths, outputPath, plan2.totalFrames, audioPath, start);
|
|
9709
9782
|
}
|
|
@@ -9786,7 +9859,12 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
9786
9859
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
9787
9860
|
);
|
|
9788
9861
|
}
|
|
9789
|
-
for (const
|
|
9862
|
+
for (const [index, entry] of chunkPaths.entries()) {
|
|
9863
|
+
if (typeof entry !== "string" && entry.length === 0) {
|
|
9864
|
+
throw new Error(`[assemble] chunk ${index} has an empty list of parts`);
|
|
9865
|
+
}
|
|
9866
|
+
}
|
|
9867
|
+
for (const path of chunkPaths.flat()) {
|
|
9790
9868
|
if (!existsSync18(path)) {
|
|
9791
9869
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
9792
9870
|
}
|
|
@@ -9892,10 +9970,10 @@ async function probeJoinedFrameInfo(videoPaths, fps, signal) {
|
|
|
9892
9970
|
};
|
|
9893
9971
|
}
|
|
9894
9972
|
async function runAssembleFfmpeg(args, step, signal, timeout) {
|
|
9895
|
-
const result = await
|
|
9973
|
+
const result = await runFfmpeg7(args, { signal, timeout });
|
|
9896
9974
|
if (!result.success) {
|
|
9897
9975
|
throw encoderFailureError(`[assemble] ffmpeg ${step} failed`, {
|
|
9898
|
-
error:
|
|
9976
|
+
error: describeFfmpegFailure4(result, timeout),
|
|
9899
9977
|
failureReason: result.failureReason
|
|
9900
9978
|
});
|
|
9901
9979
|
}
|
|
@@ -10005,6 +10083,17 @@ function validatePlanVideosForChunk(value) {
|
|
|
10005
10083
|
);
|
|
10006
10084
|
}
|
|
10007
10085
|
}
|
|
10086
|
+
function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
|
|
10087
|
+
if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
|
|
10088
|
+
const { startFrame, endFrame } = frameRange;
|
|
10089
|
+
if (!Number.isSafeInteger(startFrame) || !Number.isSafeInteger(endFrame) || startFrame < slice.startFrame || endFrame > slice.endFrame || endFrame <= startFrame) {
|
|
10090
|
+
throw new RenderChunkValidationError(
|
|
10091
|
+
CHUNK_FRAME_RANGE_INVALID,
|
|
10092
|
+
`[renderChunk] frameRange ${JSON.stringify(frameRange)} must be a non-empty integer range inside chunk ${chunkIndex}'s slice [${slice.startFrame}, ${slice.endFrame})`
|
|
10093
|
+
);
|
|
10094
|
+
}
|
|
10095
|
+
return { startFrame, endFrame };
|
|
10096
|
+
}
|
|
10008
10097
|
var distributedCaptureSessionDependencies = {
|
|
10009
10098
|
createCaptureSession: createCaptureSession9,
|
|
10010
10099
|
assertSwiftShader,
|
|
@@ -10075,7 +10164,7 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
|
|
|
10075
10164
|
if (lockedEncoder.encoder !== "libvpx-vp9-software") return void 0;
|
|
10076
10165
|
return lockedEncoder.vp9CpuUsed ?? LEGACY_DISTRIBUTED_VP9_CPU_USED;
|
|
10077
10166
|
}
|
|
10078
|
-
async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
10167
|
+
async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
10079
10168
|
const start = Date.now();
|
|
10080
10169
|
const log = defaultLogger;
|
|
10081
10170
|
const planJsonPath = join27(planDir, "plan.json");
|
|
@@ -10129,13 +10218,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10129
10218
|
if (slice === void 0) {
|
|
10130
10219
|
throw new RenderChunkValidationError(CHUNK_INDEX_OUT_OF_RANGE, "[renderChunk] missing slice");
|
|
10131
10220
|
}
|
|
10132
|
-
const
|
|
10133
|
-
if (
|
|
10221
|
+
const sliceFrames = slice.endFrame - slice.startFrame;
|
|
10222
|
+
if (sliceFrames <= 0) {
|
|
10134
10223
|
throw new RenderChunkValidationError(
|
|
10135
10224
|
CHUNK_INDEX_OUT_OF_RANGE,
|
|
10136
|
-
`[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${
|
|
10225
|
+
`[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${sliceFrames}`
|
|
10137
10226
|
);
|
|
10138
10227
|
}
|
|
10228
|
+
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
10229
|
+
const framesInChunk = range.endFrame - range.startFrame;
|
|
10139
10230
|
const compiledDir = join27(planDir, "compiled");
|
|
10140
10231
|
if (!existsSync19(compiledDir)) {
|
|
10141
10232
|
throw new RenderChunkValidationError(
|
|
@@ -10200,10 +10291,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10200
10291
|
const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
|
|
10201
10292
|
planDir,
|
|
10202
10293
|
planVideos,
|
|
10203
|
-
frames: Array.from(
|
|
10204
|
-
{ length: slice.endFrame - slice.startFrame },
|
|
10205
|
-
(_, i) => slice.startFrame + i
|
|
10206
|
-
),
|
|
10294
|
+
frames: Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i),
|
|
10207
10295
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
10208
10296
|
outputRoot: videoFramesDir,
|
|
10209
10297
|
cfg,
|
|
@@ -10359,7 +10447,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10359
10447
|
abortSignal: void 0,
|
|
10360
10448
|
assertNotAborted: () => {
|
|
10361
10449
|
},
|
|
10362
|
-
frameRange:
|
|
10450
|
+
frameRange: range
|
|
10363
10451
|
});
|
|
10364
10452
|
const observedModes = new Set(capturePerfs.map((perf) => perf.captureMode));
|
|
10365
10453
|
if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
|
|
@@ -10453,12 +10541,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10453
10541
|
});
|
|
10454
10542
|
if (encoder.chunksCarryAudio) {
|
|
10455
10543
|
const audioScratchPath = `${outputChunkPath}.audio.m4a`;
|
|
10544
|
+
const audioChunks = options?.frameRange ? chunksWithSubRange(chunks, chunkIndex, range) : { chunks, index: chunkIndex };
|
|
10456
10545
|
await muxChunkWithAudio({
|
|
10457
10546
|
videoPath: videoOnlyPath,
|
|
10458
10547
|
audioPath: chunkAudioSource(planDir, chunkIndex, audioScratchPath),
|
|
10459
10548
|
outputPath: outputChunkPath,
|
|
10460
|
-
chunks,
|
|
10461
|
-
chunkIndex,
|
|
10549
|
+
chunks: audioChunks.chunks,
|
|
10550
|
+
chunkIndex: audioChunks.index,
|
|
10462
10551
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
10463
10552
|
timeoutMs: cfg.ffmpegProcessTimeout
|
|
10464
10553
|
});
|
|
@@ -10487,8 +10576,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10487
10576
|
const perfPayload = {
|
|
10488
10577
|
planHash: plan2.planHash,
|
|
10489
10578
|
chunkIndex,
|
|
10490
|
-
startFrame:
|
|
10491
|
-
endFrame:
|
|
10579
|
+
startFrame: range.startFrame,
|
|
10580
|
+
endFrame: range.endFrame,
|
|
10492
10581
|
framesEncoded,
|
|
10493
10582
|
durationMs,
|
|
10494
10583
|
planHashMs,
|
|
@@ -10517,6 +10606,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10517
10606
|
outputPath: outputChunkPath,
|
|
10518
10607
|
outputKind,
|
|
10519
10608
|
framesEncoded,
|
|
10609
|
+
frameRange: range,
|
|
10520
10610
|
sha256,
|
|
10521
10611
|
durationMs,
|
|
10522
10612
|
planHashMs,
|
|
@@ -10533,12 +10623,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10533
10623
|
}
|
|
10534
10624
|
|
|
10535
10625
|
// src/services/distributed/planV2Execution.ts
|
|
10536
|
-
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
|
|
10626
|
+
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
10537
10627
|
const workRoot = mkdtempSync7(join28(tmpdir2(), "hf-plan-v2-chunk-"));
|
|
10538
10628
|
const materializedPlanDir = join28(workRoot, "plan");
|
|
10539
10629
|
try {
|
|
10540
10630
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
10541
|
-
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath);
|
|
10631
|
+
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
10542
10632
|
} finally {
|
|
10543
10633
|
rmSync13(workRoot, { recursive: true, force: true });
|
|
10544
10634
|
}
|
|
@@ -10852,6 +10942,7 @@ function hashProjectDir(projectDir, files = listSiteFiles(projectDir)) {
|
|
|
10852
10942
|
}
|
|
10853
10943
|
export {
|
|
10854
10944
|
CHUNK_EXCEEDS_WORKER_TIMEOUT,
|
|
10945
|
+
CHUNK_FRAME_RANGE_INVALID,
|
|
10855
10946
|
CURRENT_PLAN_PROTOCOL,
|
|
10856
10947
|
ChunkExceedsWorkerTimeoutError,
|
|
10857
10948
|
DEFAULT_CHUNK_SIZE,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "2ae4b2eb24c5dea85f83bd9af6c14abc153ad59fc3f338d1ca9142d9df0c662e",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|