@hyperframes/producer 0.8.146 → 0.8.148

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/index.js CHANGED
@@ -1893,7 +1893,8 @@ import {
1893
1893
  groupIntoLayers,
1894
1894
  blitRgb48leAffine,
1895
1895
  parseTransformMatrix,
1896
- convertTransfer
1896
+ convertTransfer,
1897
+ isVideoHiddenBeforeStreamStart
1897
1898
  } from "@hyperframes/engine";
1898
1899
 
1899
1900
  // src/services/render/hdrPerf.ts
@@ -2066,6 +2067,8 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrVideoFrameSources, hdrStart
2066
2067
  frameSource.loop
2067
2068
  );
2068
2069
  if (effectiveIndex === null) return;
2070
+ const mediaTime = (frameSource.mediaStart ?? 0) + effectiveIndex / fps;
2071
+ if (isVideoHiddenBeforeStreamStart(frameSource.videoStreamLeadSeconds, mediaTime)) return;
2069
2072
  const frameOffset = effectiveIndex * frameSource.frameSize;
2070
2073
  try {
2071
2074
  if (hdrPerf) hdrPerf.hdrVideoLayerBlits += 1;
@@ -2687,7 +2690,10 @@ async function extractHdrVideoFrames(args) {
2687
2690
  initialWindow,
2688
2691
  abortSignal
2689
2692
  );
2690
- extractionWindows.set(videoId, window2);
2693
+ extractionWindows.set(videoId, {
2694
+ window: window2,
2695
+ videoStreamLeadSeconds: metadata.videoStreamLeadSeconds
2696
+ });
2691
2697
  prep.hdrVideoStartTimes.set(videoId, window2.compositionStart);
2692
2698
  plannedVideos.push({
2693
2699
  durationSeconds: window2.durationSeconds,
@@ -2706,14 +2712,16 @@ async function extractHdrVideoFrames(args) {
2706
2712
  try {
2707
2713
  for (const [videoId, srcPath] of prep.hdrVideoSrcPaths) {
2708
2714
  const video = composition.videos.find((v) => v.id === videoId);
2709
- const window2 = extractionWindows.get(videoId);
2710
- if (!video || !window2) continue;
2715
+ const planned = extractionWindows.get(videoId);
2716
+ if (!video || !planned) continue;
2717
+ const { window: window2 } = planned;
2711
2718
  mkdirSync2(framesDir, { recursive: true });
2712
2719
  const frameDir = mkdtempSync2(join7(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
2713
2720
  createdFrameDirs.add(frameDir);
2714
2721
  const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
2715
2722
  const rawPath = join7(frameDir, "frames.rgb48le");
2716
- const extractionStart = String(window2.extractionMediaStart ?? window2.mediaStart);
2723
+ const extractionMediaStart = window2.extractionMediaStart ?? window2.mediaStart;
2724
+ const extractionStart = String(extractionMediaStart);
2717
2725
  const ffmpegArgs = [];
2718
2726
  if (window2.finalFrameOnly) {
2719
2727
  ffmpegArgs.push("-i", srcPath, "-ss", extractionStart, "-frames:v", "1");
@@ -2773,7 +2781,9 @@ async function extractHdrVideoFrames(args) {
2773
2781
  frameSize,
2774
2782
  frameCount,
2775
2783
  scratch: Buffer.allocUnsafe(frameSize),
2776
- loop: video.loop
2784
+ loop: video.loop,
2785
+ mediaStart: extractionMediaStart,
2786
+ videoStreamLeadSeconds: planned.videoStreamLeadSeconds
2777
2787
  });
2778
2788
  handedOff = true;
2779
2789
  } finally {
@@ -3360,12 +3370,13 @@ function buildRenderPerfSummary(input) {
3360
3370
  // src/services/render/stages/captureStreamingStage.ts
3361
3371
  var DEFAULT_CAPTURE_STALL_MS = 6e4;
3362
3372
  var DE_STALL_POLL_MS = 5e3;
3373
+ var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
3363
3374
  var ParallelCaptureStallError = class extends Error {
3364
3375
  variant;
3365
3376
  constructor(args) {
3366
3377
  const phases = args.phaseSummary ? ` Last worker phases: ${args.phaseSummary}.` : "";
3367
3378
  super(
3368
- args.variant === "drain" ? `[Render] Parallel ${args.captureLabel} capture stalled: no frame progress for ${args.stallTimeoutMs}ms (stuck at ${args.lastCapturedFrames}/${args.totalFrames}).${phases}` : `[Render] Parallel ${args.captureLabel} capture stalled after ${args.stallTimeoutMs}ms with no frame progress (last frame ${args.lastCapturedFrames}/${args.totalFrames}); retrying on a fresh screenshot session.${phases}`
3379
+ (args.variant === "drain" ? `[Render] Parallel ${args.captureLabel} capture stalled: no frame progress for ${args.stallTimeoutMs}ms (stuck at ${args.lastCapturedFrames}/${args.totalFrames}).${phases}` : `[Render] Parallel ${args.captureLabel} capture stalled after ${args.stallTimeoutMs}ms with no frame progress (last frame ${args.lastCapturedFrames}/${args.totalFrames}); retrying on a fresh screenshot session.${phases}`) + ` ${STALL_ADVICE}`
3369
3380
  );
3370
3381
  this.name = "ParallelCaptureStallError";
3371
3382
  this.variant = args.variant;
@@ -3383,12 +3394,13 @@ function captureModeLabel(mode) {
3383
3394
  }
3384
3395
  var SequentialCaptureStallError = class extends Error {
3385
3396
  captureMode;
3386
- constructor(captureMode, stallTimeoutMs, frameIndex, totalFrames) {
3397
+ constructor(session, stallTimeoutMs, frameIndex, totalFrames) {
3398
+ const step = session.captureStep ? ` while ${session.captureStep}` : "";
3387
3399
  super(
3388
- `[Render] Sequential ${captureModeLabel(captureMode)} capture stalled: no frame progress for ${stallTimeoutMs}ms (stuck at frame ${frameIndex}/${totalFrames}).`
3400
+ `[Render] Sequential ${captureModeLabel(session.captureMode)} capture stalled: no frame progress for ${stallTimeoutMs}ms (stuck at frame ${frameIndex}/${totalFrames}${step}). ` + STALL_ADVICE
3389
3401
  );
3390
3402
  this.name = "SequentialCaptureStallError";
3391
- this.captureMode = captureMode;
3403
+ this.captureMode = session.captureMode;
3392
3404
  }
3393
3405
  };
3394
3406
  function raceAgainstStall(promise, deadlineMs, input, signal) {
@@ -3397,9 +3409,9 @@ function raceAgainstStall(promise, deadlineMs, input, signal) {
3397
3409
  () => {
3398
3410
  reject(
3399
3411
  signal?.aborted ? new Error(
3400
- `[Render] Sequential ${captureModeLabel(input.captureMode)} capture aborted while a capture call was in flight (frame ${input.frameIndex}/${input.totalFrames}).`
3412
+ `[Render] Sequential ${captureModeLabel(input.session.captureMode)} capture aborted while a capture call was in flight (frame ${input.frameIndex}/${input.totalFrames}).`
3401
3413
  ) : new SequentialCaptureStallError(
3402
- input.captureMode,
3414
+ input.session,
3403
3415
  input.stallTimeoutMs,
3404
3416
  input.frameIndex,
3405
3417
  input.totalFrames
@@ -3527,7 +3539,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
3527
3539
  const captureWithStallGuard = (idx, promise) => raceAgainstStall(
3528
3540
  promise,
3529
3541
  stallTimeoutMs - (Date.now() - lastProgressAt),
3530
- { captureMode: session.captureMode, frameIndex: idx, totalFrames, stallTimeoutMs },
3542
+ { session, frameIndex: idx, totalFrames, stallTimeoutMs },
3531
3543
  abortSignal
3532
3544
  );
3533
3545
  const drainPrev = async () => {
@@ -3881,7 +3893,7 @@ async function runCaptureStreamingStage(input) {
3881
3893
  const { buffer } = await raceAgainstStall(
3882
3894
  captureFrameToBuffer(session, i, time),
3883
3895
  stallTimeoutMs - (Date.now() - lastProgressAt),
3884
- { captureMode: session.captureMode, frameIndex: i, totalFrames, stallTimeoutMs },
3896
+ { session, frameIndex: i, totalFrames, stallTimeoutMs },
3885
3897
  abortSignal
3886
3898
  );
3887
3899
  await reorderBuffer.waitForFrame(i);
@@ -3970,7 +3982,7 @@ async function captureSegmentFrames(ctx, segment, encoder) {
3970
3982
  ctx.captureFrame(ctx.session, i, time),
3971
3983
  ctx.stallTimeoutMs - (Date.now() - lastProgressAt),
3972
3984
  {
3973
- captureMode: ctx.session.captureMode,
3985
+ session: ctx.session,
3974
3986
  frameIndex: i,
3975
3987
  totalFrames: ctx.totalFrames,
3976
3988
  stallTimeoutMs: ctx.stallTimeoutMs
@@ -15270,6 +15282,7 @@ function readVideoMetadata(value, field) {
15270
15282
  // overwhelmingly common start-at-zero domain. Preserve that compatibility
15271
15283
  // while carrying non-zero edit-list/transport timestamps in new plans.
15272
15284
  videoStreamStartSeconds: record.videoStreamStartSeconds === void 0 ? 0 : readFiniteNumber(record.videoStreamStartSeconds, `${field}.videoStreamStartSeconds`),
15285
+ videoStreamLeadSeconds: record.videoStreamLeadSeconds === void 0 ? 0 : readFiniteNumber(record.videoStreamLeadSeconds, `${field}.videoStreamLeadSeconds`),
15273
15286
  width: readPositiveInteger(record.width, `${field}.width`),
15274
15287
  height: readPositiveInteger(record.height, `${field}.height`),
15275
15288
  fps: readFiniteNumber(record.fps, `${field}.fps`),
@@ -1912,7 +1912,8 @@ import {
1912
1912
  groupIntoLayers,
1913
1913
  blitRgb48leAffine,
1914
1914
  parseTransformMatrix,
1915
- convertTransfer
1915
+ convertTransfer,
1916
+ isVideoHiddenBeforeStreamStart
1916
1917
  } from "@hyperframes/engine";
1917
1918
 
1918
1919
  // src/services/render/hdrPerf.ts
@@ -2085,6 +2086,8 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrVideoFrameSources, hdrStart
2085
2086
  frameSource.loop
2086
2087
  );
2087
2088
  if (effectiveIndex === null) return;
2089
+ const mediaTime = (frameSource.mediaStart ?? 0) + effectiveIndex / fps;
2090
+ if (isVideoHiddenBeforeStreamStart(frameSource.videoStreamLeadSeconds, mediaTime)) return;
2088
2091
  const frameOffset = effectiveIndex * frameSource.frameSize;
2089
2092
  try {
2090
2093
  if (hdrPerf) hdrPerf.hdrVideoLayerBlits += 1;
@@ -2706,7 +2709,10 @@ async function extractHdrVideoFrames(args) {
2706
2709
  initialWindow,
2707
2710
  abortSignal
2708
2711
  );
2709
- extractionWindows.set(videoId, window2);
2712
+ extractionWindows.set(videoId, {
2713
+ window: window2,
2714
+ videoStreamLeadSeconds: metadata.videoStreamLeadSeconds
2715
+ });
2710
2716
  prep.hdrVideoStartTimes.set(videoId, window2.compositionStart);
2711
2717
  plannedVideos.push({
2712
2718
  durationSeconds: window2.durationSeconds,
@@ -2725,14 +2731,16 @@ async function extractHdrVideoFrames(args) {
2725
2731
  try {
2726
2732
  for (const [videoId, srcPath] of prep.hdrVideoSrcPaths) {
2727
2733
  const video = composition.videos.find((v) => v.id === videoId);
2728
- const window2 = extractionWindows.get(videoId);
2729
- if (!video || !window2) continue;
2734
+ const planned = extractionWindows.get(videoId);
2735
+ if (!video || !planned) continue;
2736
+ const { window: window2 } = planned;
2730
2737
  mkdirSync2(framesDir, { recursive: true });
2731
2738
  const frameDir = mkdtempSync2(join7(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`));
2732
2739
  createdFrameDirs.add(frameDir);
2733
2740
  const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
2734
2741
  const rawPath = join7(frameDir, "frames.rgb48le");
2735
- const extractionStart = String(window2.extractionMediaStart ?? window2.mediaStart);
2742
+ const extractionMediaStart = window2.extractionMediaStart ?? window2.mediaStart;
2743
+ const extractionStart = String(extractionMediaStart);
2736
2744
  const ffmpegArgs = [];
2737
2745
  if (window2.finalFrameOnly) {
2738
2746
  ffmpegArgs.push("-i", srcPath, "-ss", extractionStart, "-frames:v", "1");
@@ -2792,7 +2800,9 @@ async function extractHdrVideoFrames(args) {
2792
2800
  frameSize,
2793
2801
  frameCount,
2794
2802
  scratch: Buffer.allocUnsafe(frameSize),
2795
- loop: video.loop
2803
+ loop: video.loop,
2804
+ mediaStart: extractionMediaStart,
2805
+ videoStreamLeadSeconds: planned.videoStreamLeadSeconds
2796
2806
  });
2797
2807
  handedOff = true;
2798
2808
  } finally {
@@ -3379,12 +3389,13 @@ function buildRenderPerfSummary(input) {
3379
3389
  // src/services/render/stages/captureStreamingStage.ts
3380
3390
  var DEFAULT_CAPTURE_STALL_MS = 6e4;
3381
3391
  var DE_STALL_POLL_MS = 5e3;
3392
+ var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
3382
3393
  var ParallelCaptureStallError = class extends Error {
3383
3394
  variant;
3384
3395
  constructor(args) {
3385
3396
  const phases = args.phaseSummary ? ` Last worker phases: ${args.phaseSummary}.` : "";
3386
3397
  super(
3387
- args.variant === "drain" ? `[Render] Parallel ${args.captureLabel} capture stalled: no frame progress for ${args.stallTimeoutMs}ms (stuck at ${args.lastCapturedFrames}/${args.totalFrames}).${phases}` : `[Render] Parallel ${args.captureLabel} capture stalled after ${args.stallTimeoutMs}ms with no frame progress (last frame ${args.lastCapturedFrames}/${args.totalFrames}); retrying on a fresh screenshot session.${phases}`
3398
+ (args.variant === "drain" ? `[Render] Parallel ${args.captureLabel} capture stalled: no frame progress for ${args.stallTimeoutMs}ms (stuck at ${args.lastCapturedFrames}/${args.totalFrames}).${phases}` : `[Render] Parallel ${args.captureLabel} capture stalled after ${args.stallTimeoutMs}ms with no frame progress (last frame ${args.lastCapturedFrames}/${args.totalFrames}); retrying on a fresh screenshot session.${phases}`) + ` ${STALL_ADVICE}`
3388
3399
  );
3389
3400
  this.name = "ParallelCaptureStallError";
3390
3401
  this.variant = args.variant;
@@ -3402,12 +3413,13 @@ function captureModeLabel(mode) {
3402
3413
  }
3403
3414
  var SequentialCaptureStallError = class extends Error {
3404
3415
  captureMode;
3405
- constructor(captureMode, stallTimeoutMs, frameIndex, totalFrames) {
3416
+ constructor(session, stallTimeoutMs, frameIndex, totalFrames) {
3417
+ const step = session.captureStep ? ` while ${session.captureStep}` : "";
3406
3418
  super(
3407
- `[Render] Sequential ${captureModeLabel(captureMode)} capture stalled: no frame progress for ${stallTimeoutMs}ms (stuck at frame ${frameIndex}/${totalFrames}).`
3419
+ `[Render] Sequential ${captureModeLabel(session.captureMode)} capture stalled: no frame progress for ${stallTimeoutMs}ms (stuck at frame ${frameIndex}/${totalFrames}${step}). ` + STALL_ADVICE
3408
3420
  );
3409
3421
  this.name = "SequentialCaptureStallError";
3410
- this.captureMode = captureMode;
3422
+ this.captureMode = session.captureMode;
3411
3423
  }
3412
3424
  };
3413
3425
  function raceAgainstStall(promise, deadlineMs, input, signal) {
@@ -3416,9 +3428,9 @@ function raceAgainstStall(promise, deadlineMs, input, signal) {
3416
3428
  () => {
3417
3429
  reject(
3418
3430
  signal?.aborted ? new Error(
3419
- `[Render] Sequential ${captureModeLabel(input.captureMode)} capture aborted while a capture call was in flight (frame ${input.frameIndex}/${input.totalFrames}).`
3431
+ `[Render] Sequential ${captureModeLabel(input.session.captureMode)} capture aborted while a capture call was in flight (frame ${input.frameIndex}/${input.totalFrames}).`
3420
3432
  ) : new SequentialCaptureStallError(
3421
- input.captureMode,
3433
+ input.session,
3422
3434
  input.stallTimeoutMs,
3423
3435
  input.frameIndex,
3424
3436
  input.totalFrames
@@ -3546,7 +3558,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
3546
3558
  const captureWithStallGuard = (idx, promise) => raceAgainstStall(
3547
3559
  promise,
3548
3560
  stallTimeoutMs - (Date.now() - lastProgressAt),
3549
- { captureMode: session.captureMode, frameIndex: idx, totalFrames, stallTimeoutMs },
3561
+ { session, frameIndex: idx, totalFrames, stallTimeoutMs },
3550
3562
  abortSignal
3551
3563
  );
3552
3564
  const drainPrev = async () => {
@@ -3900,7 +3912,7 @@ async function runCaptureStreamingStage(input) {
3900
3912
  const { buffer } = await raceAgainstStall(
3901
3913
  captureFrameToBuffer(session, i, time),
3902
3914
  stallTimeoutMs - (Date.now() - lastProgressAt),
3903
- { captureMode: session.captureMode, frameIndex: i, totalFrames, stallTimeoutMs },
3915
+ { session, frameIndex: i, totalFrames, stallTimeoutMs },
3904
3916
  abortSignal
3905
3917
  );
3906
3918
  await reorderBuffer.waitForFrame(i);
@@ -3989,7 +4001,7 @@ async function captureSegmentFrames(ctx, segment, encoder) {
3989
4001
  ctx.captureFrame(ctx.session, i, time),
3990
4002
  ctx.stallTimeoutMs - (Date.now() - lastProgressAt),
3991
4003
  {
3992
- captureMode: ctx.session.captureMode,
4004
+ session: ctx.session,
3993
4005
  frameIndex: i,
3994
4006
  totalFrames: ctx.totalFrames,
3995
4007
  stallTimeoutMs: ctx.stallTimeoutMs
@@ -43,6 +43,8 @@ export interface HdrVideoFrameSource {
43
43
  scratch: Buffer;
44
44
  /** The raw file contains one playable source cycle and must wrap at EOF. */
45
45
  loop?: boolean;
46
+ mediaStart?: number;
47
+ videoStreamLeadSeconds?: number;
46
48
  }
47
49
  export declare function closeHdrVideoFrameSource(source: HdrVideoFrameSource, log?: ProducerLogger): void;
48
50
  export declare function resolveHdrVideoFrameIndex(time: number, startTime: number, fps: number, frameCount: number, loop?: boolean): number | null;
@@ -44,8 +44,9 @@ import type { ProducerLogger } from "../../../logger.js";
44
44
  import type { ProgressCallback, RenderJob } from "../../renderOrchestrator.js";
45
45
  import type { SdrStreamingCapturePlan } from "../capturePlan.js";
46
46
  export declare function resolveCaptureStallTimeoutMs(): number;
47
+ type StalledSession = Pick<CaptureSession, "captureMode" | "captureStep">;
47
48
  export declare function raceAgainstStall<T>(promise: Promise<T>, deadlineMs: number, input: {
48
- captureMode: CaptureSession["captureMode"];
49
+ session: StalledSession;
49
50
  frameIndex: number;
50
51
  totalFrames: number;
51
52
  stallTimeoutMs: number;
@@ -123,3 +124,4 @@ export type CaptureStreamingStageResult = {
123
124
  success: false;
124
125
  };
125
126
  export declare function runCaptureStreamingStage(input: CaptureStreamingStageInput): Promise<CaptureStreamingStageResult>;
127
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.146",
3
+ "version": "0.8.148",
4
4
  "description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,11 +35,11 @@
35
35
  "postcss": "^8.4.0",
36
36
  "puppeteer": "^25.2.1",
37
37
  "puppeteer-core": "^25.2.1",
38
- "@hyperframes/lint": "0.8.146",
39
- "@hyperframes/studio-server": "0.8.146",
40
- "@hyperframes/core": "0.8.146",
41
- "@hyperframes/parsers": "0.8.146",
42
- "@hyperframes/engine": "0.8.146"
38
+ "@hyperframes/core": "0.8.148",
39
+ "@hyperframes/lint": "0.8.148",
40
+ "@hyperframes/studio-server": "0.8.148",
41
+ "@hyperframes/parsers": "0.8.148",
42
+ "@hyperframes/engine": "0.8.148"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^25.0.10",
@@ -48,7 +48,7 @@
48
48
  "tsx": "^4.21.0",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^4.1.11",
51
- "@hyperframes/aws-lambda": "0.8.146"
51
+ "@hyperframes/aws-lambda": "0.8.148"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"