hyperframes 0.8.79 → 0.8.81

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.
@@ -54099,7 +54099,20 @@ function quantizeSeekTime(timeSeconds, fps, subFrameDivisions) {
54099
54099
  const grid = safeFps(fps) * divisions;
54100
54100
  return Math.round(safeTime(timeSeconds) * grid) / grid;
54101
54101
  }
54102
- var MEDIA_VISUAL_STYLE_PROPERTIES;
54102
+ function snapTimeToFrameBoundary(timeSeconds, fps) {
54103
+ const grid = safeFps(fps);
54104
+ const time = safeTime(timeSeconds);
54105
+ const framePosition = time * grid;
54106
+ const nearestFrame = Math.round(framePosition);
54107
+ return Math.abs(framePosition - nearestFrame) <= FRAME_BOUNDARY_EPSILON ? nearestFrame / grid : time;
54108
+ }
54109
+ function exportClipWindow(start, end, fps) {
54110
+ return {
54111
+ start: snapTimeToFrameBoundary(start, fps),
54112
+ end: Number.isFinite(end) ? snapTimeToFrameBoundary(end, fps) : end
54113
+ };
54114
+ }
54115
+ var MEDIA_VISUAL_STYLE_PROPERTIES, FRAME_BOUNDARY_EPSILON;
54103
54116
  var init_parityContract = __esm({
54104
54117
  "../core/dist/inline-scripts/parityContract.js"() {
54105
54118
  "use strict";
@@ -54134,6 +54147,7 @@ var init_parityContract = __esm({
54134
54147
  "scale",
54135
54148
  "box-sizing"
54136
54149
  ];
54150
+ FRAME_BOUNDARY_EPSILON = 1e-3;
54137
54151
  }
54138
54152
  });
54139
54153
 
@@ -54443,10 +54457,28 @@ var init_startResolver = __esm({
54443
54457
  }
54444
54458
  });
54445
54459
 
54460
+ // ../core/dist/clipFacts.js
54461
+ function sameInstant(a, b) {
54462
+ if (!Number.isFinite(a) || !Number.isFinite(b))
54463
+ return a === b;
54464
+ return Math.abs(a - b) <= instantTolerance(Math.max(Math.abs(a), Math.abs(b)));
54465
+ }
54466
+ var instantTolerance;
54467
+ var init_clipFacts = __esm({
54468
+ "../core/dist/clipFacts.js"() {
54469
+ "use strict";
54470
+ instantTolerance = (time) => 4 * Number.EPSILON * Math.max(1, Math.abs(time));
54471
+ }
54472
+ });
54473
+
54446
54474
  // ../core/dist/runtime/clipWindow.js
54475
+ var hasClipStarted, isInClipWindow;
54447
54476
  var init_clipWindow = __esm({
54448
54477
  "../core/dist/runtime/clipWindow.js"() {
54449
54478
  "use strict";
54479
+ init_clipFacts();
54480
+ hasClipStarted = (time, start) => time >= start || sameInstant(time, start);
54481
+ isInClipWindow = (time, start, end) => hasClipStarted(time, start) && time < end && !sameInstant(time, end);
54450
54482
  }
54451
54483
  });
54452
54484
 
@@ -63199,6 +63231,16 @@ var init_evenDimensions = __esm({
63199
63231
  }
63200
63232
  });
63201
63233
 
63234
+ // ../engine/src/utils/sdrCaptureColor.ts
63235
+ var SDR_RGB_TO_BT709_FILTER, SDR_CAPTURE_TO_BT709_FILTER;
63236
+ var init_sdrCaptureColor = __esm({
63237
+ "../engine/src/utils/sdrCaptureColor.ts"() {
63238
+ "use strict";
63239
+ SDR_RGB_TO_BT709_FILTER = "scale=out_color_matrix=bt709:out_range=tv:flags=neighbor";
63240
+ SDR_CAPTURE_TO_BT709_FILTER = `scale=in_color_matrix=bt601:in_range=pc:flags=neighbor,format=gbrp,${SDR_RGB_TO_BT709_FILTER}`;
63241
+ }
63242
+ });
63243
+
63202
63244
  // ../engine/src/utils/runFfmpeg.ts
63203
63245
  import { spawn as spawn2 } from "child_process";
63204
63246
  function isExternalFfmpegInterruption(result) {
@@ -63235,21 +63277,20 @@ ${tail}` : windowsMessage;
63235
63277
  ffmpeg stderr (tail):
63236
63278
  ${tail}` : `FFmpeg exited with code ${exitCode}`;
63237
63279
  }
63238
- async function runFfmpeg(args, opts) {
63239
- const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
63280
+ function spawnFfmpeg(args) {
63240
63281
  const ffmpeg = spawn2(getFfmpegBinary(), args, { windowsHide: true });
63241
63282
  trackChildProcess(ffmpeg);
63242
- const managed = new ManagedChildProcess(ffmpeg, {
63243
- signal: opts?.signal,
63244
- deadlineAtMs: Date.now() + timeout,
63245
- onStderr: opts?.onStderr
63246
- });
63247
- const outcome = await managed.wait();
63283
+ return ffmpeg;
63284
+ }
63285
+ function succeeded(outcome) {
63286
+ return outcome.reason === "exit" && outcome.exitCode === 0;
63287
+ }
63288
+ function toResult(outcome, stderr = outcome.stderr) {
63248
63289
  const result = {
63249
- success: outcome.reason === "exit" && outcome.exitCode === 0,
63290
+ success: succeeded(outcome),
63250
63291
  exitCode: outcome.exitCode,
63251
63292
  signal: outcome.signal,
63252
- stderr: outcome.stderr,
63293
+ stderr,
63253
63294
  durationMs: outcome.durationMs,
63254
63295
  terminationReason: outcome.reason,
63255
63296
  error: outcome.error
@@ -63259,6 +63300,43 @@ async function runFfmpeg(args, opts) {
63259
63300
  }
63260
63301
  return result;
63261
63302
  }
63303
+ async function runFfmpeg(args, opts) {
63304
+ const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
63305
+ const managed = new ManagedChildProcess(spawnFfmpeg(args), {
63306
+ signal: opts?.signal,
63307
+ deadlineAtMs: Date.now() + timeout,
63308
+ onStderr: opts?.onStderr
63309
+ });
63310
+ return toResult(await managed.wait());
63311
+ }
63312
+ async function runFfmpegPipeline(producerArgs, consumerArgs, opts) {
63313
+ const deadlineAtMs = Date.now() + (opts?.timeout ?? DEFAULT_TIMEOUT);
63314
+ const producer = spawnFfmpeg(producerArgs);
63315
+ const consumer = spawnFfmpeg(consumerArgs);
63316
+ consumer.stdin?.on("error", () => {
63317
+ });
63318
+ for (const event of ["close", "error"]) {
63319
+ consumer.once(event, () => producer.stdout?.destroy());
63320
+ producer.once(event, () => consumer.stdin?.end());
63321
+ }
63322
+ if (consumer.stdin) producer.stdout?.pipe(consumer.stdin);
63323
+ const settleOrder = [];
63324
+ const outcomes = await Promise.all(
63325
+ [producer, consumer].map(
63326
+ (child) => new ManagedChildProcess(child, {
63327
+ signal: opts?.signal,
63328
+ deadlineAtMs,
63329
+ onStderr: opts?.onStderr
63330
+ }).wait().then((outcome) => {
63331
+ settleOrder.push(outcome);
63332
+ return outcome;
63333
+ })
63334
+ )
63335
+ );
63336
+ const reported = settleOrder.find((outcome) => !succeeded(outcome)) ?? outcomes[1];
63337
+ const stderr = [...outcomes.filter((outcome) => outcome !== reported), reported].map((outcome) => outcome.stderr).join("");
63338
+ return toResult(reported, stderr);
63339
+ }
63262
63340
  var FFMPEG_SIGTERM_EXIT_LINE, DEFAULT_TIMEOUT, DEFAULT_STDERR_TAIL_LINES;
63263
63341
  var init_runFfmpeg = __esm({
63264
63342
  "../engine/src/utils/runFfmpeg.ts"() {
@@ -63664,6 +63742,8 @@ async function extractMediaMetadata(filePath) {
63664
63742
  isVFR,
63665
63743
  hasAlpha,
63666
63744
  colorSpace,
63745
+ colorRange: videoStream.color_range,
63746
+ pixelFormat,
63667
63747
  frames
63668
63748
  };
63669
63749
  })();
@@ -64207,23 +64287,20 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
64207
64287
  "tv"
64208
64288
  );
64209
64289
  }
64290
+ const sdrFilter = options.hdr ? void 0 : SDR_CAPTURE_TO_BT709_FILTER;
64291
+ const captureFilter = sdrFilter ?? "scale=in_range=pc:out_range=tv";
64210
64292
  if (gpuEncoder === "vaapi") {
64211
64293
  const vfIdx = args.indexOf("-vf");
64212
64294
  if (vfIdx !== -1) {
64213
- args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
64295
+ args[vfIdx + 1] = `${captureFilter},${args[vfIdx + 1]}`;
64214
64296
  }
64215
64297
  } else if (shouldUseGpu) {
64216
- const vf = withEvenDimensionPad("", pixelFormat, options.width, options.height);
64298
+ const vf = withEvenDimensionPad(sdrFilter ?? "", pixelFormat, options.width, options.height);
64217
64299
  if (vf) args.push("-vf", vf);
64218
64300
  } else {
64219
64301
  args.push(
64220
64302
  "-vf",
64221
- withEvenDimensionPad(
64222
- "scale=in_range=pc:out_range=tv",
64223
- pixelFormat,
64224
- options.width,
64225
- options.height
64226
- )
64303
+ withEvenDimensionPad(captureFilter, pixelFormat, options.width, options.height)
64227
64304
  );
64228
64305
  }
64229
64306
  args.push("-video_track_timescale", "90000");
@@ -64593,6 +64670,7 @@ var init_chunkEncoder = __esm({
64593
64670
  init_gpuEncoder();
64594
64671
  init_hdr();
64595
64672
  init_evenDimensions();
64673
+ init_sdrCaptureColor();
64596
64674
  init_runFfmpeg();
64597
64675
  init_ffprobe();
64598
64676
  init_dist2();
@@ -64885,25 +64963,15 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
64885
64963
  "tv"
64886
64964
  );
64887
64965
  }
64888
- if (options.rawInputFormat) {
64966
+ const sdrFilter = options.rawInputFormat ? SDR_RGB_TO_BT709_FILTER : SDR_CAPTURE_TO_BT709_FILTER;
64967
+ if (options.rawInputFormat && options.hdr) {
64889
64968
  } else if (gpuEncoder === "vaapi") {
64890
64969
  const vfIdx = args.indexOf("-vf");
64891
64970
  if (vfIdx !== -1) {
64892
- args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
64971
+ args[vfIdx + 1] = `${sdrFilter},${args[vfIdx + 1]}`;
64893
64972
  }
64894
- } else if (shouldUseGpu) {
64895
- const vf = withEvenDimensionPad("", pixelFormat, options.width, options.height);
64896
- if (vf) args.push("-vf", vf);
64897
64973
  } else {
64898
- args.push(
64899
- "-vf",
64900
- withEvenDimensionPad(
64901
- "scale=in_range=pc:out_range=tv",
64902
- pixelFormat,
64903
- options.width,
64904
- options.height
64905
- )
64906
- );
64974
+ args.push("-vf", withEvenDimensionPad(sdrFilter, pixelFormat, options.width, options.height));
64907
64975
  }
64908
64976
  args.push("-video_track_timescale", "90000");
64909
64977
  }
@@ -65068,6 +65136,7 @@ var init_streamingEncoder = __esm({
65068
65136
  init_ffmpegBinaries();
65069
65137
  init_hdr();
65070
65138
  init_evenDimensions();
65139
+ init_sdrCaptureColor();
65071
65140
  init_config();
65072
65141
  init_dist2();
65073
65142
  init_vp9Options();
@@ -79184,6 +79253,7 @@ var init_inlineSubCompositions = __esm({
79184
79253
  });
79185
79254
 
79186
79255
  // ../core/dist/compiler/htmlBundler.js
79256
+ import { AsyncLocalStorage } from "async_hooks";
79187
79257
  import { transformSync } from "esbuild";
79188
79258
  function cssAttributeSelector(attr, value) {
79189
79259
  const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/</g, "\\3c ");
@@ -79360,7 +79430,7 @@ function declarerVariableRules(document2, layerFor) {
79360
79430
  }
79361
79431
  return rules;
79362
79432
  }
79363
- var MAX_INLINE_ASSET_BYTES;
79433
+ var bundleReads, MAX_INLINE_ASSET_BYTES;
79364
79434
  var init_htmlBundler = __esm({
79365
79435
  "../core/dist/compiler/htmlBundler.js"() {
79366
79436
  "use strict";
@@ -79384,6 +79454,7 @@ var init_htmlBundler = __esm({
79384
79454
  init_cssSelector();
79385
79455
  init_safePath();
79386
79456
  init_colorGrading();
79457
+ bundleReads = new AsyncLocalStorage();
79387
79458
  MAX_INLINE_ASSET_BYTES = 2 * 1024 * 1024;
79388
79459
  }
79389
79460
  });
@@ -79749,7 +79820,7 @@ var init_extractionCache = __esm({
79749
79820
  init_extractedFrameIndex();
79750
79821
  COMPLETE_SENTINEL = ".hf-complete";
79751
79822
  GC_MARKER = ".hf-last-gc";
79752
- SCHEMA_PREFIX = "hfcache-v4-";
79823
+ SCHEMA_PREFIX = "hfcache-v5-";
79753
79824
  KEY_HEX_CHARS = 16;
79754
79825
  CACHE_GENERATION_PREFIX = "hfcache-v";
79755
79826
  }
@@ -80097,6 +80168,21 @@ function parseImageElements(html) {
80097
80168
  }
80098
80169
  return images;
80099
80170
  }
80171
+ function chromeGuessForUntaggedMatrix(metadata) {
80172
+ const matrix = metadata.colorSpace?.colorSpace;
80173
+ if (matrix && matrix !== "unknown") return [];
80174
+ const hd = metadata.height >= 720 && !CHROME_BT601_UNTAGGED_CODECS.has(metadata.videoCodec);
80175
+ return [`setparams=colorspace=${hd ? "bt709" : "smpte170m"}`];
80176
+ }
80177
+ function restoreSourceColourFilter(metadata) {
80178
+ const tags = [
80179
+ ["range", metadata.colorRange],
80180
+ ["colorspace", metadata.colorSpace?.colorSpace],
80181
+ ["color_primaries", metadata.colorSpace?.colorPrimaries],
80182
+ ["color_trc", metadata.colorSpace?.colorTransfer]
80183
+ ].filter(([, value]) => value && value !== "unknown" && value !== "reserved");
80184
+ return tags.length > 0 ? [`setparams=${tags.map(([key2, value]) => `${key2}=${value}`).join(":")}`] : [];
80185
+ }
80100
80186
  async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
80101
80187
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
80102
80188
  const { outputDir, quality = 95 } = options;
@@ -80158,14 +80244,37 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
80158
80244
  if (options.toneMapHdrToSdr && isHdr && !isMacOS) {
80159
80245
  vfFilters.push(HDR_TO_SDR_TONEMAP_FILTER);
80160
80246
  }
80161
- if (vfFilters.length > 0) args.push("-vf", vfFilters.join(","));
80162
- if (!options.finalFrameOnly && metadata.isVFR) {
80163
- args.push("-fps_mode", "cfr", "-r", ffmpegFps);
80247
+ if (!isHdr && !options.sdrToHdrTransfer) {
80248
+ vfFilters.push(...chromeGuessForUntaggedMatrix(metadata), SDR_CANVAS_PASSTHROUGH_FILTER);
80249
+ if (format === "jpg") vfFilters.push(SDR_JPEG_AS_BT601_FULL_RANGE_FILTER);
80250
+ }
80251
+ const encodeArgs = ["-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0"];
80252
+ if (format === "png") encodeArgs.push("-compression_level", "1");
80253
+ encodeArgs.push("-y", outputPattern);
80254
+ const runOptions = { signal, timeout: ffmpegProcessTimeout };
80255
+ const resampleVfrToCfr = !options.finalFrameOnly && metadata.isVFR;
80256
+ if (resampleVfrToCfr) args.push("-fps_mode", "cfr", "-r", ffmpegFps);
80257
+ let processResult;
80258
+ if (resampleVfrToCfr && vfFilters.length > 0 && !isHdr && NUT_RAW_PIXEL_FORMATS.has(metadata.pixelFormat ?? "")) {
80259
+ processResult = await runFfmpegPipeline(
80260
+ [...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"],
80261
+ [
80262
+ "-f",
80263
+ "nut",
80264
+ "-i",
80265
+ "pipe:0",
80266
+ "-vf",
80267
+ [...restoreSourceColourFilter(metadata), ...vfFilters].join(","),
80268
+ "-fps_mode",
80269
+ "passthrough",
80270
+ ...encodeArgs
80271
+ ],
80272
+ runOptions
80273
+ );
80274
+ } else {
80275
+ if (vfFilters.length > 0) args.push("-vf", vfFilters.join(","));
80276
+ processResult = await runFfmpeg([...args, ...encodeArgs], runOptions);
80164
80277
  }
80165
- args.push("-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
80166
- if (format === "png") args.push("-compression_level", "1");
80167
- args.push("-y", outputPattern);
80168
- const processResult = await runFfmpeg(args, { signal, timeout: ffmpegProcessTimeout });
80169
80278
  if (processResult.failureReason === "external_interruption") {
80170
80279
  throw new VideoSourceExtractionError(
80171
80280
  "external_interruption",
@@ -81081,8 +81190,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
81081
81190
  };
81082
81191
  }
81083
81192
  function getFrameIndexAtTime(extracted, globalTime, videoStart, loop = false, mediaStart = 0, holdLastFrame = false, playbackRate = 1) {
81084
- let localTime = globalTime - videoStart;
81085
- if (localTime < 0) return null;
81193
+ let localTime = Math.max(0, globalTime - videoStart);
81086
81194
  const normalizedPlaybackRate = normalizeRateSpec(playbackRate);
81087
81195
  const loopDuration = timeAtSourceTime(
81088
81196
  normalizedPlaybackRate,
@@ -81104,6 +81212,7 @@ function getFrameIndexAtTime(extracted, globalTime, videoStart, loop = false, me
81104
81212
  return frameIndex;
81105
81213
  }
81106
81214
  function getFrameAtTime(extracted, globalTime, videoStart, loop = false, mediaStart = 0) {
81215
+ if (!hasClipStarted(globalTime, videoStart)) return null;
81107
81216
  const frameIndex = getFrameIndexAtTime(extracted, globalTime, videoStart, loop, mediaStart);
81108
81217
  return frameIndex == null ? null : extracted.framePaths.get(frameIndex) || null;
81109
81218
  }
@@ -81116,8 +81225,8 @@ function analyzeClipMediaFit(params) {
81116
81225
  if (shortfallSeconds <= toleranceSeconds) return null;
81117
81226
  return { shortfallSeconds, toleranceSeconds };
81118
81227
  }
81119
- function createFrameLookupTable(videos, extracted) {
81120
- const table = new FrameLookupTable();
81228
+ function createFrameLookupTable(videos, extracted, fps) {
81229
+ const table = new FrameLookupTable(fps);
81121
81230
  const extractedMap = /* @__PURE__ */ new Map();
81122
81231
  for (const ext of extracted) extractedMap.set(ext.videoId, ext);
81123
81232
  for (const video of videos) {
@@ -81128,7 +81237,7 @@ function createFrameLookupTable(videos, extracted) {
81128
81237
  }
81129
81238
  return table;
81130
81239
  }
81131
- var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, HDR_TO_SDR_TONEMAP_FILTER, HDR_TO_SDR_TRANSFORM_KEY, VideoSourceExtractionError, TRANSIENT_FFMPEG_SPAWN_CODES, FINAL_FRAME_LOGICAL_DURATION_SECONDS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
81240
+ var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, HDR_TO_SDR_TONEMAP_FILTER, HDR_TO_SDR_TRANSFORM_KEY, SDR_CANVAS_PASSTHROUGH_FILTER, SDR_JPEG_AS_BT601_FULL_RANGE_FILTER, VideoSourceExtractionError, CHROME_BT601_UNTAGGED_CODECS, NUT_RAW_PIXEL_FORMATS, TRANSIENT_FFMPEG_SPAWN_CODES, FINAL_FRAME_LOGICAL_DURATION_SECONDS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
81132
81241
  var init_videoFrameExtractor = __esm({
81133
81242
  "../engine/src/services/videoFrameExtractor.ts"() {
81134
81243
  "use strict";
@@ -81151,6 +81260,8 @@ var init_videoFrameExtractor = __esm({
81151
81260
  SDR_TO_HDR_COLORSPACE_FILTER = "colorspace=all=bt2020:iall=bt709:range=tv";
81152
81261
  HDR_TO_SDR_TONEMAP_FILTER = "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv";
81153
81262
  HDR_TO_SDR_TRANSFORM_KEY = "hdr2sdr-hable-bt709";
81263
+ SDR_CANVAS_PASSTHROUGH_FILTER = "setparams=color_primaries=bt709:color_trc=iec61966-2-1";
81264
+ SDR_JPEG_AS_BT601_FULL_RANGE_FILTER = "scale=flags=neighbor,format=gbrp,scale=out_color_matrix=bt601:out_range=pc:flags=neighbor,format=yuv420p";
81154
81265
  VideoSourceExtractionError = class extends Error {
81155
81266
  constructor(kind, retryable, message, diagnostic = message) {
81156
81267
  super(message);
@@ -81164,10 +81275,56 @@ var init_videoFrameExtractor = __esm({
81164
81275
  diagnostic;
81165
81276
  hyperframesVideoSourceExtractionError = true;
81166
81277
  };
81278
+ CHROME_BT601_UNTAGGED_CODECS = /* @__PURE__ */ new Set(["vp9", "av1"]);
81279
+ NUT_RAW_PIXEL_FORMATS = /* @__PURE__ */ new Set([
81280
+ "yuv420p",
81281
+ "yuvj420p",
81282
+ "yuv422p",
81283
+ "yuvj422p",
81284
+ "yuv444p",
81285
+ "yuvj444p",
81286
+ "yuva420p",
81287
+ "yuva444p",
81288
+ "yuv440p",
81289
+ "yuv411p",
81290
+ "yuv410p",
81291
+ "yuva422p",
81292
+ "yuv420p10le",
81293
+ "yuv422p10le",
81294
+ "yuv444p10le",
81295
+ "yuva420p10le",
81296
+ "yuva444p10le",
81297
+ "yuv420p12le",
81298
+ "nv12",
81299
+ "nv21",
81300
+ "yuyv422",
81301
+ "uyvy422",
81302
+ "gray",
81303
+ "gray10le",
81304
+ "ya8",
81305
+ "gbrp",
81306
+ "gbrap",
81307
+ "gbrp10le",
81308
+ "gbrp12le",
81309
+ "gbrap10le",
81310
+ "rgb24",
81311
+ "bgr24",
81312
+ "rgba",
81313
+ "bgra",
81314
+ "argb",
81315
+ "abgr",
81316
+ "rgb48le",
81317
+ "rgba64le"
81318
+ ]);
81167
81319
  TRANSIENT_FFMPEG_SPAWN_CODES = /* @__PURE__ */ new Set(["EAGAIN", "EMFILE", "ENFILE"]);
81168
81320
  FINAL_FRAME_LOGICAL_DURATION_SECONDS = 1e-6;
81169
81321
  ALPHA_CAPABLE_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8", "prores"]);
81170
81322
  FrameLookupTable = class {
81323
+ /** The render's `fps`, so a clip shows in exactly export visibility's frames; omit for authored times. */
81324
+ constructor(fps) {
81325
+ this.fps = fps;
81326
+ }
81327
+ fps;
81171
81328
  videos = /* @__PURE__ */ new Map();
81172
81329
  orderedVideos = [];
81173
81330
  activeVideoIds = /* @__PURE__ */ new Set();
@@ -81177,18 +81334,18 @@ var init_videoFrameExtractor = __esm({
81177
81334
  this.videos.set(extracted.videoId, {
81178
81335
  extracted,
81179
81336
  start,
81180
- end,
81337
+ clipWindow: this.fps ? exportClipWindow(start, end, this.fps) : { start, end },
81181
81338
  mediaStart,
81182
81339
  loop,
81183
81340
  playbackRate: normalizeRateSpec(playbackRate)
81184
81341
  });
81185
- this.orderedVideos = Array.from(this.videos.entries()).map(([videoId, video]) => ({ videoId, ...video })).sort((a, b) => a.start - b.start);
81342
+ this.orderedVideos = Array.from(this.videos.entries()).map(([videoId, video]) => ({ videoId, ...video })).sort((a, b) => a.clipWindow.start - b.clipWindow.start);
81186
81343
  this.resetActiveState();
81187
81344
  }
81188
81345
  getFrame(videoId, globalTime) {
81189
81346
  const video = this.videos.get(videoId);
81190
81347
  if (!video) return null;
81191
- if (globalTime < video.start || globalTime > video.end) return null;
81348
+ if (!isInClipWindow(globalTime, video.clipWindow.start, video.clipWindow.end)) return null;
81192
81349
  const frameIndex = getFrameIndexAtTime(
81193
81350
  video.extracted,
81194
81351
  globalTime,
@@ -81210,14 +81367,11 @@ var init_videoFrameExtractor = __esm({
81210
81367
  this.activeVideoIds.clear();
81211
81368
  this.startCursor = 0;
81212
81369
  for (const entry of this.orderedVideos) {
81213
- if (entry.start <= globalTime && globalTime <= entry.end) {
81370
+ if (!hasClipStarted(globalTime, entry.clipWindow.start)) break;
81371
+ if (isInClipWindow(globalTime, entry.clipWindow.start, entry.clipWindow.end)) {
81214
81372
  this.activeVideoIds.add(entry.videoId);
81215
81373
  }
81216
- if (entry.start <= globalTime) {
81217
- this.startCursor += 1;
81218
- } else {
81219
- break;
81220
- }
81374
+ this.startCursor += 1;
81221
81375
  }
81222
81376
  this.lastTime = globalTime;
81223
81377
  return;
@@ -81225,17 +81379,17 @@ var init_videoFrameExtractor = __esm({
81225
81379
  while (this.startCursor < this.orderedVideos.length) {
81226
81380
  const candidate = this.orderedVideos[this.startCursor];
81227
81381
  if (!candidate) break;
81228
- if (candidate.start > globalTime) {
81382
+ if (!hasClipStarted(globalTime, candidate.clipWindow.start)) {
81229
81383
  break;
81230
81384
  }
81231
- if (globalTime <= candidate.end) {
81385
+ if (isInClipWindow(globalTime, candidate.clipWindow.start, candidate.clipWindow.end)) {
81232
81386
  this.activeVideoIds.add(candidate.videoId);
81233
81387
  }
81234
81388
  this.startCursor += 1;
81235
81389
  }
81236
81390
  for (const videoId of Array.from(this.activeVideoIds)) {
81237
81391
  const video = this.videos.get(videoId);
81238
- if (!video || globalTime < video.start || globalTime > video.end) {
81392
+ if (!video || !isInClipWindow(globalTime, video.clipWindow.start, video.clipWindow.end)) {
81239
81393
  this.activeVideoIds.delete(videoId);
81240
81394
  }
81241
81395
  }
@@ -97587,11 +97741,11 @@ function resolveCaptureImageFormat(input) {
97587
97741
  }
97588
97742
 
97589
97743
  // ../producer/src/services/render/stages/probeStage.ts
97590
- var FRAME_BOUNDARY_EPSILON = 1e-3;
97744
+ var FRAME_BOUNDARY_EPSILON2 = 1e-3;
97591
97745
  function durationToFrameCount(duration, fps) {
97592
97746
  const rawFrameCount = duration * fps;
97593
97747
  const nearestFrame = Math.round(rawFrameCount);
97594
- return Math.abs(rawFrameCount - nearestFrame) <= FRAME_BOUNDARY_EPSILON ? nearestFrame : Math.ceil(rawFrameCount);
97748
+ return Math.abs(rawFrameCount - nearestFrame) <= FRAME_BOUNDARY_EPSILON2 ? nearestFrame : Math.ceil(rawFrameCount);
97595
97749
  }
97596
97750
  function hasScriptedAudioVolumeAutomation(html, audioCount) {
97597
97751
  if (audioCount <= 0) return false;
@@ -98471,7 +98625,11 @@ async function runExtractVideosStage(input) {
98471
98625
  materializeSymlinks
98472
98626
  });
98473
98627
  if (extractionResult.extracted.length > 0) {
98474
- frameLookup = createFrameLookupTable(composition.videos, extractionResult.extracted);
98628
+ frameLookup = createFrameLookupTable(
98629
+ composition.videos,
98630
+ extractionResult.extracted,
98631
+ resolveRenderFpsConfig(job.config.fps).value
98632
+ );
98475
98633
  }
98476
98634
  videoReadinessSkipIds = collectVideoReadinessSkipIds(
98477
98635
  nativeHdrVideoIds,
@@ -104579,8 +104737,8 @@ async function runFontLocalize(io, localize) {
104579
104737
  }
104580
104738
 
104581
104739
  // src/version.ts
104582
- var VERSION = true ? "0.8.79" : "0.0.0-dev";
104583
- var PRODUCER_VERSION = true ? "0.8.79" : "0.0.0-dev";
104740
+ var VERSION = true ? "0.8.81" : "0.0.0-dev";
104741
+ var PRODUCER_VERSION = true ? "0.8.81" : "0.0.0-dev";
104584
104742
 
104585
104743
  // src/fontLocalizeCli.ts
104586
104744
  async function readStdin() {