@hyperframes/producer 0.8.183 → 0.8.185

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.
@@ -5233,6 +5233,7 @@ async function runCaptureStage(input) {
5233
5233
  if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
5234
5234
  return;
5235
5235
  }
5236
+ if (progress.landedFrame) input.onFrameLanded?.(progress.landedFrame);
5236
5237
  job.framesRendered = progress.capturedFrames;
5237
5238
  checkDiskProjection(progress.capturedFrames);
5238
5239
  const frameProgress = progress.capturedFrames / progress.totalFrames;
@@ -5473,7 +5474,8 @@ import {
5473
5474
  formatFfmpegError,
5474
5475
  MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
5475
5476
  resolveConfig,
5476
- runFfmpeg as runFfmpeg3
5477
+ runFfmpeg as runFfmpeg3,
5478
+ spawnStreamingEncoder as spawnStreamingEncoder4
5477
5479
  } from "@hyperframes/engine";
5478
5480
  import {
5479
5481
  clearGifFramesBeforeNext,
@@ -5610,6 +5612,30 @@ function startEncodeProgress(job, stage, onProgress) {
5610
5612
  });
5611
5613
  return (frames = total) => reportEncodeProgress(job, frames, total, onProgress, 75);
5612
5614
  }
5615
+ function resolveEngineConfig(input) {
5616
+ return input.engineConfig ?? input.job.config.producerConfig ?? resolveConfig();
5617
+ }
5618
+ function videoEncoderOptions(input, engineCfg) {
5619
+ const { job, preset } = input;
5620
+ return {
5621
+ fps: job.config.fps,
5622
+ width: input.width,
5623
+ height: input.height,
5624
+ codec: preset.codec,
5625
+ preset: preset.preset,
5626
+ quality: input.effectiveQuality,
5627
+ bitrate: input.effectiveBitrate,
5628
+ pixelFormat: preset.pixelFormat,
5629
+ vp9CpuUsed: engineCfg.vp9CpuUsed,
5630
+ useGpu: job.config.useGpu,
5631
+ hdr: preset.hdr,
5632
+ // Distributed chunk renders pass these so the encoder writes closed-GOP
5633
+ // keyframes that survive `-f concat -c copy` at assemble time. In-process
5634
+ // renders leave both undefined → preserves the existing open-GOP output.
5635
+ lockGopForChunkConcat: input.lockGopForChunkConcat === true,
5636
+ gopSize: input.gopSize
5637
+ };
5638
+ }
5613
5639
  async function runEncodeStage(input) {
5614
5640
  const {
5615
5641
  job,
@@ -5617,16 +5643,11 @@ async function runEncodeStage(input) {
5617
5643
  outputPath,
5618
5644
  framesDir,
5619
5645
  videoOnlyPath,
5620
- width,
5621
- height,
5622
5646
  needsAlpha,
5623
5647
  hasAudio,
5624
5648
  audioOutputPath,
5625
5649
  isPngSequence,
5626
5650
  isGif,
5627
- preset,
5628
- effectiveQuality,
5629
- effectiveBitrate,
5630
5651
  enableChunkedEncode,
5631
5652
  chunkedEncodeSize,
5632
5653
  abortSignal,
@@ -5656,7 +5677,7 @@ async function runEncodeStage(input) {
5656
5677
  encoded();
5657
5678
  return { encodeMs: Date.now() - stage5Start };
5658
5679
  }
5659
- const engineCfg = input.engineConfig ?? job.config.producerConfig ?? resolveConfig();
5680
+ const engineCfg = resolveEngineConfig(input);
5660
5681
  if (isGif) {
5661
5682
  const encoded = startEncodeProgress(job, "Encoding GIF", onProgress);
5662
5683
  if (hasAudio) {
@@ -5686,24 +5707,7 @@ async function runEncodeStage(input) {
5686
5707
  const videoEngineCfg = scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout ? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout } : engineCfg;
5687
5708
  const frameExt = frameFileExtension2(input.captureImageFormat);
5688
5709
  const framePattern = `frame_%06d.${frameExt}`;
5689
- const encoderOpts = {
5690
- fps: job.config.fps,
5691
- width,
5692
- height,
5693
- codec: preset.codec,
5694
- preset: preset.preset,
5695
- quality: effectiveQuality,
5696
- bitrate: effectiveBitrate,
5697
- pixelFormat: preset.pixelFormat,
5698
- vp9CpuUsed: engineCfg.vp9CpuUsed,
5699
- useGpu: job.config.useGpu,
5700
- hdr: preset.hdr,
5701
- // Distributed chunk renders pass these so the encoder writes closed-GOP
5702
- // keyframes that survive `-f concat -c copy` at assemble time. In-process
5703
- // renders leave both undefined → preserves the existing open-GOP output.
5704
- lockGopForChunkConcat: input.lockGopForChunkConcat === true,
5705
- gopSize: input.gopSize
5706
- };
5710
+ const encoderOpts = videoEncoderOptions(input, engineCfg);
5707
5711
  const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
5708
5712
  framesDir,
5709
5713
  framePattern,
@@ -5986,7 +5990,6 @@ async function enforceAacTruePeak(input) {
5986
5990
  try {
5987
5991
  scratchDir = mkdtempSync2(join13(dirname6(input.audioPath), ".true-peak-"));
5988
5992
  const correctedPath = join13(scratchDir, "audio.m4a");
5989
- const correctionTargetDbfs = AAC_DELIVERY_TRUE_PEAK_DBFS - AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB;
5990
5993
  const measurements = [];
5991
5994
  let attenuationDb = 0;
5992
5995
  let measuredPath = input.audioPath;
@@ -6004,7 +6007,8 @@ async function enforceAacTruePeak(input) {
6004
6007
  return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
6005
6008
  }
6006
6009
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
6007
- attenuationDb += correctionTargetDbfs - truePeakDbfs;
6010
+ const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
6011
+ attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
6008
6012
  const result = await input.encode(
6009
6013
  input.audioPath,
6010
6014
  correctedPath,
@@ -6847,7 +6851,14 @@ var RenderChunkValidationError = class extends Error {
6847
6851
  }
6848
6852
  };
6849
6853
  function createChunkVideoFrameInjectorFactory(frameLookup) {
6850
- return () => createVideoFrameInjector2(frameLookup);
6854
+ if (!(frameLookup instanceof Promise)) return () => createVideoFrameInjector2(frameLookup);
6855
+ return () => {
6856
+ let hook;
6857
+ return async (page, time, heldVideoTime) => {
6858
+ hook ??= createVideoFrameInjector2(await frameLookup);
6859
+ await hook?.(page, time, heldVideoTime);
6860
+ };
6861
+ };
6851
6862
  }
6852
6863
  function frameNumberFromFileName(name) {
6853
6864
  const match = /(\d+)(?=\.[^.]+$)/.exec(name);
@@ -10245,6 +10256,12 @@ function validatePlanVideosForChunk(value) {
10245
10256
  );
10246
10257
  }
10247
10258
  }
10259
+ var MIN_FRAMES_PER_CHUNK_BROWSER = 20;
10260
+ function resolveChunkCaptureWorkers(framesInChunk, machineSized, maxCaptureWorkers) {
10261
+ if (maxCaptureWorkers === void 0) return machineSized;
10262
+ const byFrames = Math.floor(framesInChunk / MIN_FRAMES_PER_CHUNK_BROWSER);
10263
+ return Math.max(machineSized, Math.floor(Math.min(maxCaptureWorkers, byFrames)));
10264
+ }
10248
10265
  function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
10249
10266
  if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
10250
10267
  const { startFrame, endFrame } = frameRange;
@@ -10481,7 +10498,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10481
10498
  deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
10482
10499
  )
10483
10500
  });
10484
- const chunkWorkerCount = calculateOptimalWorkers2(framesInChunk, void 0, cfg);
10501
+ const chunkWorkerCount = resolveChunkCaptureWorkers(
10502
+ framesInChunk,
10503
+ calculateOptimalWorkers2(framesInChunk, void 0, cfg),
10504
+ options?.maxCaptureWorkers
10505
+ );
10485
10506
  let session = null;
10486
10507
  let outputKind;
10487
10508
  let framesEncoded = 0;
@@ -10493,6 +10514,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10493
10514
  const captureAttempts = [];
10494
10515
  let forceScreenshotForChunk = encoder.forceScreenshot;
10495
10516
  let shownBoxes;
10517
+ let extraction;
10496
10518
  try {
10497
10519
  if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
10498
10520
  const bootStarted = Date.now();
@@ -10549,7 +10571,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10549
10571
  session = null;
10550
10572
  }
10551
10573
  }
10552
- const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
10574
+ const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
10553
10575
  planDir,
10554
10576
  planVideos,
10555
10577
  frames: chunkFrameNumbers,
@@ -10558,19 +10580,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10558
10580
  cfg,
10559
10581
  remoteSources,
10560
10582
  shownBoxes
10561
- }) : [];
10562
- const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable5(
10563
- planVideos.videos,
10564
- [
10565
- ...rebuildExtractedFramesFromPlanDir(
10566
- planDir,
10567
- planVideos.extracted.filter((video) => !video.deferredRange),
10568
- v2Manifest === null ? "dense-v1" : "sparse-v2"
10569
- ),
10570
- ...deferredVideoFrames
10571
- ],
10572
- resolveRenderFpsConfig(job.config.fps).value
10583
+ }) : Promise.resolve([]);
10584
+ extraction = deferredVideoFrames;
10585
+ const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
10586
+ (deferred) => createFrameLookupTable5(
10587
+ planVideos.videos,
10588
+ [
10589
+ ...rebuildExtractedFramesFromPlanDir(
10590
+ planDir,
10591
+ planVideos.extracted.filter((video) => !video.deferredRange),
10592
+ v2Manifest === null ? "dense-v1" : "sparse-v2"
10593
+ ),
10594
+ ...deferred
10595
+ ],
10596
+ resolveRenderFpsConfig(job.config.fps).value
10597
+ )
10573
10598
  ) : null;
10599
+ videoFrameLookup?.catch(() => {
10600
+ });
10574
10601
  const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
10575
10602
  const captureStarted = Date.now();
10576
10603
  captureMode = await runCaptureWithScreenshotFallback({
@@ -10653,7 +10680,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10653
10680
  }
10654
10681
  );
10655
10682
  }
10683
+ }).catch(async (captureError) => {
10684
+ await deferredVideoFrames;
10685
+ throw captureError;
10656
10686
  });
10687
+ await deferredVideoFrames;
10657
10688
  captureStageMs = Date.now() - captureStarted;
10658
10689
  applyRenderWarningPolicy(
10659
10690
  job,
@@ -10732,6 +10763,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10732
10763
  }
10733
10764
  encodeStageMs = Date.now() - encodeStarted;
10734
10765
  } finally {
10766
+ await extraction?.catch(() => {
10767
+ });
10735
10768
  if (session) {
10736
10769
  try {
10737
10770
  await closeCaptureSession10(session);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.1.0",
3
3
  "buildId": "dev",
4
- "sha256": "ba14a236a2e876f93d6683f19606311590ca5ee22b8f08cbe0e1205eabf24464",
4
+ "sha256": "6b080917689b34014c8f3884d89beea5dcd39986c93335d117c29fa055e0ecd8",
5
5
  "artifacts": {
6
6
  "iife": "hyperframe.runtime.iife.js",
7
7
  "esm": "hyperframe.runtime.mjs"