@hyperframes/producer 0.8.221 → 0.8.223

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.
@@ -1103,6 +1103,21 @@ function reportFrameProgress(job, stage, progress, onProgress, done, total) {
1103
1103
  total
1104
1104
  });
1105
1105
  }
1106
+ var lastExtractReportAt = /* @__PURE__ */ new WeakMap();
1107
+ function reportExtractProgress(job, done, total, onProgress) {
1108
+ if (total <= 0) return;
1109
+ const frames = Math.min(done, total);
1110
+ reportThrottled(
1111
+ lastExtractReportAt,
1112
+ job,
1113
+ "Extracting video frames",
1114
+ 10 + 10 * frames / total,
1115
+ onProgress,
1116
+ frames === total,
1117
+ { code: "extract_video", done: frames, total },
1118
+ "preprocessing"
1119
+ );
1120
+ }
1106
1121
  var lastEncodeReportAt = /* @__PURE__ */ new WeakMap();
1107
1122
  function reportEncodeProgress(job, done, total, onProgress, from) {
1108
1123
  if (total <= 0) return;
@@ -1134,6 +1149,14 @@ function reportAssembleProgress(job, done, total, onProgress) {
1134
1149
  );
1135
1150
  }
1136
1151
  var workerPhasesByJob = /* @__PURE__ */ new WeakMap();
1152
+ function forgetWorkerPhases(job) {
1153
+ workerPhasesByJob.delete(job);
1154
+ }
1155
+ function describeWorkerPhases(job) {
1156
+ const phases = workerPhasesByJob.get(job);
1157
+ if (!phases?.size) return void 0;
1158
+ return `Last phase per worker: ${[...phases].map(([id, phase]) => `worker ${id} ${phase}`).join(", ")}.`;
1159
+ }
1137
1160
  function reportWorkerStartup(job, progress, onProgress) {
1138
1161
  const phase = progress.latestWorkerPhase;
1139
1162
  if (!phase) return;
@@ -2215,6 +2238,11 @@ function aggregateChromeMemory(perfs) {
2215
2238
  samples: sampled.reduce((sum, p) => sum + (p.chromeMemorySamples ?? 0), 0)
2216
2239
  };
2217
2240
  }
2241
+ function frameWeightedAvgMs(perfs, avgOf) {
2242
+ const frames = perfs.reduce((sum, p) => sum + p.frames, 0);
2243
+ if (frames === 0) return void 0;
2244
+ return Math.round(perfs.reduce((sum, p) => sum + avgOf(p) * p.frames, 0) / frames);
2245
+ }
2218
2246
  function aggregateDedup(perfs) {
2219
2247
  if (perfs.length === 0) return void 0;
2220
2248
  const armed = perfs.some((p) => p.staticDedupArmed);
@@ -2313,6 +2341,10 @@ function buildRenderPerfSummary(input) {
2313
2341
  if (withSamples.length === 0) return void 0;
2314
2342
  return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
2315
2343
  })(),
2344
+ captureSeekAvgMs: frameWeightedAvgMs(input.dedupPerfs, (p) => p.avgSeekMs),
2345
+ captureBeforeCaptureAvgMs: frameWeightedAvgMs(input.dedupPerfs, (p) => p.avgBeforeCaptureMs),
2346
+ captureScreenshotAvgMs: frameWeightedAvgMs(input.dedupPerfs, (p) => p.avgScreenshotMs),
2347
+ encoderWaitAvgMs: input.perfStages.encoderDrainWaitMs !== void 0 && input.totalFrames > 0 ? Math.round(input.perfStages.encoderDrainWaitMs / input.totalFrames) : void 0,
2316
2348
  peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
2317
2349
  peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
2318
2350
  staticDedup: aggregateDedup(input.dedupPerfs),
@@ -4338,6 +4370,7 @@ async function runCaptureStreamingStage(input) {
4338
4370
  return {
4339
4371
  success: true,
4340
4372
  encodeMs: encodeResult.durationMs,
4373
+ encoderDrainWaitMs: encodeResult.drainWaitMs,
4341
4374
  probeSession,
4342
4375
  lastBrowserConsole,
4343
4376
  workerCount,
@@ -4437,7 +4470,7 @@ async function captureSegment(ctx, segment) {
4437
4470
  // finished; absent only in unit tests, whose fake encoder writes none.
4438
4471
  bytes: existsSync6(segmentPath) ? statSync3(segmentPath).size : 0
4439
4472
  });
4440
- return encodeResult.durationMs;
4473
+ return encodeResult;
4441
4474
  } finally {
4442
4475
  if (!encoderClosed) {
4443
4476
  await encoder.close().catch(() => {
@@ -4579,7 +4612,11 @@ async function retryAfterTargetLoss(run, worker, err, segment, segmentPath) {
4579
4612
  );
4580
4613
  run.deps.removeFile(segmentPath);
4581
4614
  await recycleWorkerSession(run, worker, "retry");
4582
- run.totals.encodeMs += await captureSegment(worker.ctx, segment);
4615
+ addSegmentEncode(run.totals, await captureSegment(worker.ctx, segment));
4616
+ }
4617
+ function addSegmentEncode(totals, result) {
4618
+ totals.encodeMs += result.durationMs;
4619
+ totals.encoderDrainWaitMs += result.drainWaitMs ?? 0;
4583
4620
  }
4584
4621
  async function runOneSegment(run, worker, segment) {
4585
4622
  const segmentPath = segmentOutputPath(run.segmentDir, segment.index);
@@ -4591,7 +4628,7 @@ async function runOneSegment(run, worker, segment) {
4591
4628
  segmentIndex: segment.index
4592
4629
  });
4593
4630
  try {
4594
- run.totals.encodeMs += await captureSegment(worker.ctx, segment);
4631
+ addSegmentEncode(run.totals, await captureSegment(worker.ctx, segment));
4595
4632
  } catch (err) {
4596
4633
  if (err instanceof SegmentEncoderSpawnError) return onSegmentSpawnFailure(run, err, segment);
4597
4634
  await retryAfterTargetLoss(run, worker, err, segment, segmentPath);
@@ -4634,7 +4671,7 @@ async function runCaptureSegmentedStage(input) {
4634
4671
  const run = {
4635
4672
  input,
4636
4673
  deps,
4637
- totals: { encodeMs: 0, segmentRetries: 0, browserRecycles: 0 },
4674
+ totals: { encodeMs: 0, encoderDrainWaitMs: 0, segmentRetries: 0, browserRecycles: 0 },
4638
4675
  segments,
4639
4676
  pending,
4640
4677
  segmentDir,
@@ -4687,6 +4724,7 @@ async function runCaptureSegmentedStage(input) {
4687
4724
  return {
4688
4725
  success: true,
4689
4726
  encodeMs: run.totals.encodeMs,
4727
+ encoderDrainWaitMs: run.totals.encoderDrainWaitMs,
4690
4728
  probeSession: null,
4691
4729
  lastBrowserConsole,
4692
4730
  workerCount: run.workerCount,
@@ -5498,16 +5536,17 @@ var RenderStageStalledError = class extends CaptureFailure2 {
5498
5536
  code = "RENDER_STAGE_STALLED";
5499
5537
  owner = "system";
5500
5538
  retryable = false;
5501
- constructor(phase, silentMs, progress) {
5539
+ constructor(phase, silentMs, progress, detail) {
5502
5540
  super({
5503
5541
  kind: "protocol_timeout",
5504
- message: `[Render] ${phase} stalled: no progress for ${silentMs}ms (stuck at ${progress}).`
5542
+ message: `[Render] ${phase} stalled: no progress for ${silentMs}ms (stuck at ${progress}).${detail ? ` ${detail}` : ""}`
5505
5543
  });
5506
5544
  this.name = "RenderStageStalledError";
5507
5545
  }
5508
5546
  };
5509
5547
  var STALL_POLL_MS = 5e3;
5510
5548
  function watchStall(phase, rule) {
5549
+ const detail = rule.detail ?? (() => void 0);
5511
5550
  let last = rule.progress();
5512
5551
  let budgetMs = rule.firstProgressMs;
5513
5552
  let lastChangeAt = Date.now();
@@ -5529,7 +5568,7 @@ function watchStall(phase, rule) {
5529
5568
  }
5530
5569
  if (current !== last) return restartWindow(rule.noProgressMs);
5531
5570
  if (Date.now() - lastChangeAt < budgetMs) return;
5532
- const error = new RenderStageStalledError(phase, budgetMs, current);
5571
+ const error = new RenderStageStalledError(phase, budgetMs, current, detail());
5533
5572
  if (attempt && !attempt.signal.aborted) {
5534
5573
  attempt.abort(error);
5535
5574
  return restartWindow(rule.noProgressMs);
@@ -5543,6 +5582,7 @@ function watchStall(phase, rule) {
5543
5582
  const openAttempt = (parent) => {
5544
5583
  attempt = new AbortController();
5545
5584
  inTail = false;
5585
+ rule.attemptOpened?.();
5546
5586
  restartWindow(rule.firstProgressMs);
5547
5587
  return parent ? AbortSignal.any([parent, attempt.signal]) : attempt.signal;
5548
5588
  };
@@ -7849,7 +7889,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
7849
7889
  const { document: document2 } = parseHTML5(html);
7850
7890
  const assetExists = (path) => existsSync10(resolve7(projectDir, path));
7851
7891
  rewriteAssetPaths(
7852
- document2.querySelectorAll("[src], [href]"),
7892
+ document2.querySelectorAll("[src], [href], [srcset]"),
7853
7893
  entryPath,
7854
7894
  (el, attr) => el.getAttribute(attr),
7855
7895
  (el, attr, value) => el.setAttribute(attr, value),
@@ -9531,6 +9571,8 @@ async function runExtractVideosStage(input) {
9531
9571
  safeVideoExtractionSourceLogMetadata(v.src)
9532
9572
  );
9533
9573
  }
9574
+ let framesPlanned = 0;
9575
+ let framesDone = 0;
9534
9576
  extractionResult = await extractAllVideoFrames(
9535
9577
  composition.videos,
9536
9578
  projectDir,
@@ -9547,7 +9589,12 @@ async function runExtractVideosStage(input) {
9547
9589
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
9548
9590
  deferRangeExtraction,
9549
9591
  contentKeyedDirs: [join18(compiledDir, REMOTE_MEDIA_SUBDIR)],
9550
- urlByAbsentLocalPath
9592
+ urlByAbsentLocalPath,
9593
+ onProgress: ({ planned = 0, done = 0 }) => {
9594
+ framesPlanned += planned;
9595
+ framesDone += done;
9596
+ reportExtractProgress(job, framesDone, framesPlanned, input.onProgress);
9597
+ }
9551
9598
  },
9552
9599
  abortSignal,
9553
9600
  cfg,
@@ -11294,6 +11341,7 @@ async function runCaptureHdrStage(input) {
11294
11341
  let lastBrowserConsole = [];
11295
11342
  let captureDurationMs = 0;
11296
11343
  let encodeMs = 0;
11344
+ let encoderDrainWaitMs;
11297
11345
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
11298
11346
  log.info(
11299
11347
  hasHdrContent ? "[Render] HDR layered composite: z-ordered DOM + native HDR video/image layers" : "[Render] Shader transition composite: z-ordered SDR DOM layers"
@@ -11531,6 +11579,7 @@ async function runCaptureHdrStage(input) {
11531
11579
  reportEncodeProgress(job, totalFrames, totalFrames, onProgress, encodeFrom);
11532
11580
  captureDurationMs = Date.now() - stageStart;
11533
11581
  encodeMs = hdrEncodeResult.durationMs;
11582
+ encoderDrainWaitMs = hdrEncodeResult.drainWaitMs;
11534
11583
  } catch (error) {
11535
11584
  lastBrowserConsole = domSession.browserConsoleBuffer;
11536
11585
  throw wrapCaptureStageError(error, lastBrowserConsole);
@@ -11563,6 +11612,7 @@ async function runCaptureHdrStage(input) {
11563
11612
  hdrPerf,
11564
11613
  captureDurationMs,
11565
11614
  encodeMs,
11615
+ encoderDrainWaitMs,
11566
11616
  warnings: cloneCaptureWarnings2(domSession.warnings)
11567
11617
  };
11568
11618
  }
@@ -12355,7 +12405,9 @@ function frameStallRule(job, totalFrames) {
12355
12405
  noProgressMs,
12356
12406
  firstProgressMs: Math.max(FIRST_FRAME_STALL_MS, noProgressMs),
12357
12407
  complete: () => (job.framesRendered ?? 0) >= totalFrames,
12358
- tailMs: Math.max(TAIL_STALL_MS, noProgressMs)
12408
+ tailMs: Math.max(TAIL_STALL_MS, noProgressMs),
12409
+ detail: () => describeWorkerPhases(job),
12410
+ attemptOpened: () => forgetWorkerPhases(job)
12359
12411
  };
12360
12412
  }
12361
12413
  function isRecoverableParallelCaptureError(error) {
@@ -12881,6 +12933,11 @@ function resolveDeSingleMinFrames(env) {
12881
12933
  function shouldPreferSingleWorkerDrawElement(args) {
12882
12934
  return args.workerCount > 1 && args.useDrawElement && !args.deCompileGate && !args.forceScreenshot && outputUsesH264Pipeline(args.outputFormat) && args.minFrames > 0 && args.totalFrames >= args.minFrames && args.singleWorkerStreamingOk && !args.layeredOrEffectRoute && !args.supersampling && !args.probeDeGated && !args.experimentalParallelDeOptIn;
12883
12935
  }
12936
+ function initialDeClampReason(args) {
12937
+ if (args.useDrawElement) return void 0;
12938
+ if (args.deCompileGate) return "compile_gate";
12939
+ return explainDrawElementDisabled(args.config);
12940
+ }
12884
12941
  function probeNeedsScreenshotFrames(session) {
12885
12942
  return (session?.deRiskFrames?.size ?? 0) > 0;
12886
12943
  }
@@ -13270,11 +13327,15 @@ async function executeRenderPipeline(input) {
13270
13327
  perfStages.compileOnlyMs = compileResult.compileOnlyMs;
13271
13328
  let captureForceScreenshot = compileResult.forceScreenshot;
13272
13329
  const deCompileGate = compileResult.deCompileGate;
13273
- let deClampReason = cfg.useDrawElement ? void 0 : explainDrawElementDisabled({
13274
- platform: process.platform,
13275
- browserGpuMode: cfg.browserGpuMode,
13276
- workerEncode: cfg.enableDrawElementWorkerEncode,
13277
- chromeCeiling: chromeMajorCeiling()
13330
+ let deClampReason = initialDeClampReason({
13331
+ useDrawElement: cfg.useDrawElement,
13332
+ deCompileGate,
13333
+ config: {
13334
+ platform: process.platform,
13335
+ browserGpuMode: cfg.browserGpuMode,
13336
+ workerEncode: cfg.enableDrawElementWorkerEncode,
13337
+ chromeCeiling: chromeMajorCeiling()
13338
+ }
13278
13339
  });
13279
13340
  let deWorkerInversion;
13280
13341
  let deParallelStreamForced = false;
@@ -13400,7 +13461,8 @@ async function executeRenderPipeline(input) {
13400
13461
  abortSignal: executionSignal,
13401
13462
  assertNotAborted,
13402
13463
  // Local staging can use links; distributed plan() alone requires real copies.
13403
- materializeSymlinks: false
13464
+ materializeSymlinks: false,
13465
+ onProgress
13404
13466
  })
13405
13467
  );
13406
13468
  const {
@@ -14264,6 +14326,7 @@ async function executeRenderPipeline(input) {
14264
14326
  perfStages.captureFrameMs = hdrRes.captureDurationMs;
14265
14327
  perfStages.captureSetupMs = Math.max(0, Date.now() - stage4Start - hdrRes.captureDurationMs);
14266
14328
  perfStages.encodeMs = hdrRes.encodeMs;
14329
+ perfStages.encoderDrainWaitMs = hdrRes.encoderDrainWaitMs ?? 0;
14267
14330
  } else {
14268
14331
  let streamingHandled = false;
14269
14332
  if (capturePlan.kind === "sdr_segmented") {
@@ -14443,6 +14506,7 @@ async function executeRenderPipeline(input) {
14443
14506
  perfStages.captureFrameMs = Date.now() - captureFrameStart;
14444
14507
  perfStages.captureSetupMs = Math.max(0, perfStages.captureMs - perfStages.captureFrameMs);
14445
14508
  perfStages.encodeMs = segmentedRes.encodeMs;
14509
+ perfStages.encoderDrainWaitMs = segmentedRes.encoderDrainWaitMs ?? 0;
14446
14510
  log.info(
14447
14511
  `[Render] Segmented capture complete: ${segmentedRes.segments} segment(s) concatenated.`,
14448
14512
  {
@@ -14620,6 +14684,7 @@ async function executeRenderPipeline(input) {
14620
14684
  perfStages.captureFrameMs = captureFrameMs;
14621
14685
  perfStages.captureSetupMs = Math.max(0, perfStages.captureMs - captureFrameMs);
14622
14686
  perfStages.encodeMs = streamingRes.encodeMs;
14687
+ perfStages.encoderDrainWaitMs = streamingRes.encoderDrainWaitMs ?? 0;
14623
14688
  } else {
14624
14689
  if (capturePlan.kind === "sdr_streaming") {
14625
14690
  capturePlan = replanAfterFailure(capturePlan, { kind: "streaming_unavailable" });
@@ -0,0 +1,19 @@
1
+ import { type DistributedRenderConfig } from "./plan.js";
2
+ export interface RenderSpanResult {
3
+ startFrame: number;
4
+ endFrame: number;
5
+ framesEncoded: number;
6
+ }
7
+ export declare class RenderSpanUnsupportedError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ /** The frames `[startFrame, endFrame)` of a `totalFrames` timeline that `[startSeconds, endSeconds)` covers. */
11
+ export declare function spanFrameRange(startSeconds: number, endSeconds: number | undefined, fps: number, totalFrames: number): {
12
+ startFrame: number;
13
+ endFrame: number;
14
+ };
15
+ /** Renders `[startSeconds, endSeconds)` as a standalone mp4 from 0, with that span's audio, by chunk sub-ranges. */
16
+ export declare function renderSpan(projectDir: string, config: DistributedRenderConfig, span: {
17
+ startSeconds: number;
18
+ endSeconds?: number;
19
+ }, outputPath: string): Promise<RenderSpanResult>;
@@ -337,6 +337,8 @@ export interface StageStallRule {
337
337
  complete?: () => boolean;
338
338
  /** Allowed silence once `complete()` holds; defaults to `noProgressMs`. */
339
339
  tailMs?: number;
340
+ detail?: () => string | undefined;
341
+ attemptOpened?: () => void;
340
342
  }
341
343
  /**
342
344
  * Classified like the per-call protocol timeout it replaces, so the capture retry ladder retries it.
@@ -346,7 +348,7 @@ export declare class RenderStageStalledError extends CaptureFailure {
346
348
  readonly code = "RENDER_STAGE_STALLED";
347
349
  readonly owner: "system";
348
350
  readonly retryable: false;
349
- constructor(phase: string, silentMs: number, progress: number);
351
+ constructor(phase: string, silentMs: number, progress: number, detail?: string);
350
352
  }
351
353
  /** Opens a capture attempt: a fresh no-progress window, and a signal the rule aborts when that window runs out. */
352
354
  export type OpenStallAttempt = (parent?: AbortSignal) => AbortSignal;
@@ -106,10 +106,14 @@ export declare function applyRenderModeHints(alreadyForced: boolean, compiled: C
106
106
  export declare function updateJobStatus(job: RenderJob, status: RenderStatus, stage: string, progress: number, onProgress?: ProgressCallback, stageProgress?: RenderStageProgress): void;
107
107
  /** Capture-loop progress: the first frame, the last frame, and at most four reports a second between. */
108
108
  export declare function reportFrameProgress(job: RenderJob, stage: string, progress: number, onProgress: ProgressCallback | undefined, done: number, total: number): void;
109
+ /** Frames ffmpeg has written for the source videos, 10% to 20%. */
110
+ export declare function reportExtractProgress(job: RenderJob, done: number, total: number, onProgress: ProgressCallback | undefined): void;
109
111
  /** Frames ffmpeg has encoded, from where capture left the bar up to 90%; same cadence as capture. */
110
112
  export declare function reportEncodeProgress(job: RenderJob, done: number, total: number, onProgress: ProgressCallback | undefined, from: number): void;
111
113
  /** Seconds of the final file written, 90% to 99%: only a complete file reaches 100. */
112
114
  export declare function reportAssembleProgress(job: RenderJob, done: number, total: number, onProgress: ProgressCallback | undefined): void;
115
+ export declare function forgetWorkerPhases(job: object): void;
116
+ export declare function describeWorkerPhases(job: object): string | undefined;
113
117
  /** Browser warm-up before the first frame, from the parallel workers' phase events. */
114
118
  export declare function reportWorkerStartup(job: RenderJob, progress: ParallelProgress, onProgress: ProgressCallback | undefined): void;
115
119
  /**
@@ -84,6 +84,7 @@ export interface CaptureHdrStageResult {
84
84
  captureDurationMs: number;
85
85
  /** ffmpeg-reported encode duration; overlapped with capture. */
86
86
  encodeMs: number;
87
+ encoderDrainWaitMs?: number;
87
88
  warnings: CaptureWarning[];
88
89
  }
89
90
  export declare function runCaptureHdrStage(input: CaptureHdrStageInput): Promise<CaptureHdrStageResult>;
@@ -75,6 +75,7 @@ export interface CaptureSegmentedStageInput {
75
75
  export type CaptureSegmentedStageResult = {
76
76
  success: true;
77
77
  encodeMs: number;
78
+ encoderDrainWaitMs?: number;
78
79
  probeSession: null;
79
80
  lastBrowserConsole: string[];
80
81
  workerCount: number;
@@ -112,6 +112,7 @@ export type CaptureStreamingStageResult = {
112
112
  success: true;
113
113
  /** Wall-clock ms for the encode phase (overlapped with capture; from the encoder's own report). */
114
114
  encodeMs: number;
115
+ encoderDrainWaitMs?: number;
115
116
  probeSession: CaptureSession | null;
116
117
  lastBrowserConsole: string[];
117
118
  workerCount: number;
@@ -30,6 +30,7 @@
30
30
  import { type CaptureVideoMetadataHint, type EngineConfig, type ExtractionResult, type FrameLookupTable, type HdrTransfer, type VideoExtractionFailureKind, type VideoColorSpace, classifyVideoExtractionError, extractAllVideoFrames } from "@hyperframes/engine";
31
31
  import { type RenderJob } from "../../renderOrchestrator.js";
32
32
  import { type CompositionMetadata } from "../shared.js";
33
+ import type { ProgressCallback } from "../../renderOrchestrator.js";
33
34
  import type { ProducerLogger } from "../../../logger.js";
34
35
  import { type ExtractionFailureMetadataV1 } from "../extractionFailureMetadata.js";
35
36
  export interface ExtractVideosStageInput {
@@ -52,6 +53,7 @@ export interface ExtractVideosStageInput {
52
53
  materializeSymlinks?: boolean;
53
54
  deferRangeExtraction?: boolean;
54
55
  urlByAbsentLocalPath?: ReadonlyMap<string, string>;
56
+ onProgress?: ProgressCallback;
55
57
  }
56
58
  export interface ExtractVideosStageResult {
57
59
  /** Result of `extractAllVideoFrames`, or `null` if the composition has no videos. */
@@ -1,5 +1,5 @@
1
1
  import { type CanvasResolution, type Fps, type FpsInput, type HfVfxCapture } from "@hyperframes/core";
2
- import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ProresProfile, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
2
+ import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ProresProfile, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, explainDrawElementDisabled, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
3
3
  import { type ProducerLogger } from "../logger.js";
4
4
  import { type RenderOutputFormat } from "./render/renderFormat.js";
5
5
  import { type RenderCaptureObservability, type RenderObservabilitySummary, type OpenStallAttempt, type StageStallRule } from "./render/observability.js";
@@ -200,6 +200,11 @@ export interface RenderPerfSummary {
200
200
  * captured the most frames when parallel workers report separately.
201
201
  */
202
202
  captureP50Ms?: number;
203
+ /** Per-frame capture phases, frame-weighted across sessions; encoder wait is FFmpeg back-pressure. */
204
+ captureSeekAvgMs?: number;
205
+ captureBeforeCaptureAvgMs?: number;
206
+ captureScreenshotAvgMs?: number;
207
+ encoderWaitAvgMs?: number;
203
208
  /** Worst sub-composition timeline wait outcome across sessions. */
204
209
  subTimelineWait?: SubTimelineWaitOutcome;
205
210
  capturePeakMs?: number;
@@ -875,6 +880,12 @@ export declare function shouldPreferSingleWorkerDrawElement(args: {
875
880
  */
876
881
  experimentalParallelDeOptIn: boolean;
877
882
  }): boolean;
883
+ /** Why drawElement is off before capture; the compile stage's content gates are not config refusals. */
884
+ export declare function initialDeClampReason(args: {
885
+ useDrawElement: boolean;
886
+ deCompileGate: string | undefined;
887
+ config: Parameters<typeof explainDrawElementDisabled>[0];
888
+ }): string | undefined;
878
889
  /**
879
890
  * Plan the self-verify retry for an inverted render: the inversion bet on
880
891
  * drawElement and lost, so the re-render returns to the pre-inversion parallel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.221",
3
+ "version": "0.8.223",
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.11.0",
37
37
  "puppeteer-core": "^25.11.0",
38
- "@hyperframes/engine": "0.8.221",
39
- "@hyperframes/lint": "0.8.221",
40
- "@hyperframes/parsers": "0.8.221",
41
- "@hyperframes/studio-server": "0.8.221",
42
- "@hyperframes/core": "0.8.221"
38
+ "@hyperframes/core": "0.8.223",
39
+ "@hyperframes/engine": "0.8.223",
40
+ "@hyperframes/lint": "0.8.223",
41
+ "@hyperframes/parsers": "0.8.223",
42
+ "@hyperframes/studio-server": "0.8.223"
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.221"
51
+ "@hyperframes/aws-lambda": "0.8.223"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"