MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / captureFrameCore

Function captureFrameCore

packages/engine/src/services/frameCapture.ts:1686–1768  ·  view source on GitHub ↗

* Internal core: prepare, screenshot, and track perf. * Shared by captureFrame (disk) and captureFrameToBuffer (buffer). * Returns the screenshot buffer, quantized time, and total capture time.

(
  session: CaptureSession,
  frameIndex: number,
  time: number,
)

Source from the content-addressed store, hash-verified

1684 * Returns the screenshot buffer, quantized time, and total capture time.
1685 */
1686async function captureFrameCore(
1687 session: CaptureSession,
1688 frameIndex: number,
1689 time: number,
1690): Promise<{ buffer: Buffer; quantizedTime: number; captureTimeMs: number }> {
1691 const { page, options } = session;
1692 const startTime = Date.now();
1693
1694 // Static-frame dedup: this frame is byte-identical to its predecessor (predicted +
1695 // anchor-verified at init) → reuse the prior buffer, skip the seek + screenshot.
1696 // KEY: index by the ABSOLUTE composition frame (derived from `time`), NOT the
1697 // `frameIndex` arg — chunked/parallel/distributed callers pass a chunk-RELATIVE
1698 // frameIndex (captureStage passes the loop `i`, parallelCoordinator passes
1699 // `i-outputFrameOffset`) while staticFrames is keyed in absolute frames. Using `time`
1700 // is correct on every path (sequential, per-worker range, distributed chunk) because
1701 // `time` is always the absolute composition time for the frame. Each session captures
1702 // its range in ascending order, so lastFrameBuffer is the correct in-range anchor (and
1703 // since a static run is verified identical, reusing the run's first in-range capture
1704 // equals reusing the global anchor). Telemetry: count reuses separately; do NOT bump
1705 // capturePerf.frames (that would dilute the per-frame timing averages).
1706 // Use the SAME floor+epsilon idiom as quantizeTimeToFrame so the dedup lookup agrees
1707 // with the frame the seek actually lands on, even if `time` ever isn't exactly i/fps.
1708 const absFrameIndex = Math.floor(time * fpsToNumber(options.fps) + 1e-9);
1709 if (session.staticFrames?.has(absFrameIndex) && session.lastFrameBuffer) {
1710 session.staticDedupCount = (session.staticDedupCount ?? 0) + 1;
1711 return {
1712 buffer: session.lastFrameBuffer,
1713 quantizedTime: quantizeTimeToFrame(time, fpsToNumber(options.fps)),
1714 captureTimeMs: Date.now() - startTime,
1715 };
1716 }
1717
1718 try {
1719 const { quantizedTime, seekMs, beforeCaptureMs } = await prepareFrameForCapture(
1720 session,
1721 frameIndex,
1722 time,
1723 );
1724
1725 const screenshotStart = Date.now();
1726 let screenshotBuffer: Buffer;
1727
1728 if (session.captureMode === "beginframe") {
1729 const frameTimeTicks =
1730 session.beginFrameTimeTicks + frameIndex * session.beginFrameIntervalMs;
1731 const result = await beginFrameCapture(
1732 page,
1733 options,
1734 frameTimeTicks,
1735 session.beginFrameIntervalMs,
1736 );
1737 if (result.hasDamage) session.beginFrameHasDamageCount++;
1738 else session.beginFrameNoDamageCount++;
1739 screenshotBuffer = result.buffer;
1740 } else {
1741 screenshotBuffer = await pageScreenshotCapture(page, options);
1742 }
1743

Callers 2

captureFrameFunction · 0.85
captureFrameToBufferFunction · 0.85

Calls 7

fpsToNumberFunction · 0.90
quantizeTimeToFrameFunction · 0.90
prepareFrameForCaptureFunction · 0.85
beginFrameCaptureFunction · 0.85
pageScreenshotCaptureFunction · 0.85
nowMethod · 0.80

Tested by

no test coverage detected