@hyperframes/engine 0.1.2 → 0.1.4

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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/config.d.ts +62 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +92 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/index.d.ts +49 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +55 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/services/audioMixer.d.ts +11 -0
  11. package/dist/services/audioMixer.d.ts.map +1 -0
  12. package/dist/services/audioMixer.js +327 -0
  13. package/dist/services/audioMixer.js.map +1 -0
  14. package/dist/services/audioMixer.types.d.ts +27 -0
  15. package/dist/services/audioMixer.types.d.ts.map +1 -0
  16. package/dist/services/audioMixer.types.js +2 -0
  17. package/dist/services/audioMixer.types.js.map +1 -0
  18. package/dist/services/browserManager.d.ts +29 -0
  19. package/dist/services/browserManager.d.ts.map +1 -0
  20. package/dist/services/browserManager.js +175 -0
  21. package/dist/services/browserManager.js.map +1 -0
  22. package/dist/services/chunkEncoder.d.ts +32 -0
  23. package/dist/services/chunkEncoder.d.ts.map +1 -0
  24. package/dist/services/chunkEncoder.js +368 -0
  25. package/dist/services/chunkEncoder.js.map +1 -0
  26. package/dist/services/chunkEncoder.types.d.ts +26 -0
  27. package/dist/services/chunkEncoder.types.d.ts.map +1 -0
  28. package/dist/services/chunkEncoder.types.js +2 -0
  29. package/dist/services/chunkEncoder.types.js.map +1 -0
  30. package/dist/services/fileServer.d.ts +25 -0
  31. package/dist/services/fileServer.d.ts.map +1 -0
  32. package/dist/services/fileServer.js +138 -0
  33. package/dist/services/fileServer.js.map +1 -0
  34. package/dist/services/frameCapture.d.ts +52 -0
  35. package/dist/services/frameCapture.d.ts.map +1 -0
  36. package/dist/services/frameCapture.js +317 -0
  37. package/dist/services/frameCapture.js.map +1 -0
  38. package/dist/services/parallelCoordinator.d.ts +40 -0
  39. package/dist/services/parallelCoordinator.d.ts.map +1 -0
  40. package/dist/services/parallelCoordinator.js +192 -0
  41. package/dist/services/parallelCoordinator.js.map +1 -0
  42. package/dist/services/screenshotService.d.ts +28 -0
  43. package/dist/services/screenshotService.d.ts.map +1 -0
  44. package/dist/services/screenshotService.js +190 -0
  45. package/dist/services/screenshotService.js.map +1 -0
  46. package/dist/services/streamingEncoder.d.ts +49 -0
  47. package/dist/services/streamingEncoder.d.ts.map +1 -0
  48. package/dist/services/streamingEncoder.js +227 -0
  49. package/dist/services/streamingEncoder.js.map +1 -0
  50. package/dist/services/videoFrameExtractor.d.ts +65 -0
  51. package/dist/services/videoFrameExtractor.d.ts.map +1 -0
  52. package/dist/services/videoFrameExtractor.js +308 -0
  53. package/dist/services/videoFrameExtractor.js.map +1 -0
  54. package/dist/services/videoFrameInjector.d.ts +17 -0
  55. package/dist/services/videoFrameInjector.d.ts.map +1 -0
  56. package/dist/services/videoFrameInjector.js +94 -0
  57. package/dist/services/videoFrameInjector.js.map +1 -0
  58. package/dist/types.d.ts +79 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +8 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/ffprobe.d.ts +18 -0
  63. package/dist/utils/ffprobe.d.ts.map +1 -0
  64. package/dist/utils/ffprobe.js +153 -0
  65. package/dist/utils/ffprobe.js.map +1 -0
  66. package/dist/utils/gpuEncoder.d.ts +11 -0
  67. package/dist/utils/gpuEncoder.d.ts.map +1 -0
  68. package/dist/utils/gpuEncoder.js +55 -0
  69. package/dist/utils/gpuEncoder.js.map +1 -0
  70. package/dist/utils/parityContract.d.ts +3 -0
  71. package/dist/utils/parityContract.d.ts.map +1 -0
  72. package/dist/utils/parityContract.js +35 -0
  73. package/dist/utils/parityContract.js.map +1 -0
  74. package/dist/utils/runFfmpeg.d.ts +19 -0
  75. package/dist/utils/runFfmpeg.d.ts.map +1 -0
  76. package/dist/utils/runFfmpeg.js +62 -0
  77. package/dist/utils/runFfmpeg.js.map +1 -0
  78. package/dist/utils/urlDownloader.d.ts +3 -0
  79. package/dist/utils/urlDownloader.d.ts.map +1 -0
  80. package/dist/utils/urlDownloader.js +68 -0
  81. package/dist/utils/urlDownloader.js.map +1 -0
  82. package/package.json +9 -9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frameCapture.d.ts","sourceRoot":"","sources":["../../src/services/frameCapture.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,EAAsC,MAAM,gBAAgB,CAAC;AAM7F,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;AAEvF,wGAAwG;AACxG,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,WAAW,EAAE;QACX,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,WAAW,EAAE,WAAW,CAAC;IAEzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wBAAwB,EAAE,MAAM,CAAC;IACjC,uBAAuB,EAAE,MAAM,CAAC;IAChC,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAChC;AAMD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,eAAe,GAAE,iBAAiB,GAAG,IAAW,EAChD,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC7B,OAAO,CAAC,cAAc,CAAC,CA4DzB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoI9E;AA4ID,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,CAcxB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAI9B;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhF;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,iBAAiB,GAAG,IAAI,GACxC,IAAI,CAeN;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMrF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CASjF"}
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Frame Capture Service
3
+ *
4
+ * Uses Puppeteer to capture frames from any web page implementing the
5
+ * window.__hf seek protocol. Navigates to a file server URL, waits for
6
+ * the page to expose window.__hf, then captures frames deterministically
7
+ * via Chrome's BeginFrame API or Page.captureScreenshot fallback.
8
+ */
9
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
10
+ import { join } from "path";
11
+ import { quantizeTimeToFrame } from "@hyperframes/core";
12
+ // ── Extracted modules ───────────────────────────────────────────────────────
13
+ import { acquireBrowser, releaseBrowser, buildChromeArgs, resolveHeadlessShellPath, } from "./browserManager.js";
14
+ import { beginFrameCapture, getCdpSession, pageScreenshotCapture } from "./screenshotService.js";
15
+ import { DEFAULT_CONFIG } from "../config.js";
16
+ // Circular buffer for browser console messages dumped on render failure diagnostics.
17
+ // Complex compositions produce 100+ messages; 50 was too small to capture relevant errors.
18
+ const BROWSER_CONSOLE_BUFFER_SIZE = 200;
19
+ export async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
20
+ if (!existsSync(outputDir))
21
+ mkdirSync(outputDir, { recursive: true });
22
+ // Determine capture mode before building args — BeginFrame flags only apply on Linux
23
+ const headlessShell = resolveHeadlessShellPath(config);
24
+ const isLinux = process.platform === "linux";
25
+ const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG.forceScreenshot;
26
+ const preMode = headlessShell && isLinux && !forceScreenshot ? "beginframe" : "screenshot";
27
+ const chromeArgs = buildChromeArgs({ width: options.width, height: options.height, captureMode: preMode }, config);
28
+ const { browser, captureMode } = await acquireBrowser(chromeArgs, config);
29
+ const page = await browser.newPage();
30
+ const browserVersion = await browser.version();
31
+ const expectedMajor = config?.expectedChromiumMajor;
32
+ if (Number.isFinite(expectedMajor)) {
33
+ const actualChromiumMajor = Number.parseInt((browserVersion.match(/(\d+)\./) || [])[1] || "", 10);
34
+ if (Number.isFinite(actualChromiumMajor) && actualChromiumMajor !== expectedMajor) {
35
+ throw new Error(`[FrameCapture] Chromium major mismatch expected=${expectedMajor} actual=${actualChromiumMajor} raw=${browserVersion}`);
36
+ }
37
+ }
38
+ const viewport = {
39
+ width: options.width,
40
+ height: options.height,
41
+ deviceScaleFactor: options.deviceScaleFactor || 1,
42
+ };
43
+ await page.setViewport(viewport);
44
+ return {
45
+ browser,
46
+ page,
47
+ options,
48
+ serverUrl,
49
+ outputDir,
50
+ onBeforeCapture,
51
+ isInitialized: false,
52
+ browserConsoleBuffer: [],
53
+ capturePerf: {
54
+ frames: 0,
55
+ seekMs: 0,
56
+ beforeCaptureMs: 0,
57
+ screenshotMs: 0,
58
+ totalMs: 0,
59
+ },
60
+ captureMode,
61
+ beginFrameTimeTicks: 0,
62
+ beginFrameIntervalMs: 1000 / Math.max(1, options.fps),
63
+ beginFrameHasDamageCount: 0,
64
+ beginFrameNoDamageCount: 0,
65
+ config,
66
+ };
67
+ }
68
+ export async function initializeSession(session) {
69
+ const { page, serverUrl } = session;
70
+ // Forward browser console to host with [Browser] prefix
71
+ page.on("console", (msg) => {
72
+ const type = msg.type();
73
+ const text = msg.text();
74
+ const prefix = type === "error" ? "[Browser:ERROR]" : type === "warn" ? "[Browser:WARN]" : "[Browser]";
75
+ console.log(`${prefix} ${text}`);
76
+ session.browserConsoleBuffer.push(`${prefix} ${text}`);
77
+ if (session.browserConsoleBuffer.length > BROWSER_CONSOLE_BUFFER_SIZE) {
78
+ session.browserConsoleBuffer.shift();
79
+ }
80
+ });
81
+ page.on("pageerror", (err) => {
82
+ const text = `[Browser:PAGEERROR] ${err instanceof Error ? err.message : String(err)}`;
83
+ console.error(text);
84
+ session.browserConsoleBuffer.push(text);
85
+ if (session.browserConsoleBuffer.length > BROWSER_CONSOLE_BUFFER_SIZE) {
86
+ session.browserConsoleBuffer.shift();
87
+ }
88
+ });
89
+ // Navigate to the file server
90
+ const url = `${serverUrl}/index.html`;
91
+ if (session.captureMode === "screenshot") {
92
+ // Screenshot mode: standard navigation, rAF works normally
93
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });
94
+ const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
95
+ await page.waitForFunction(`!!(window.__hf && typeof window.__hf.seek === "function" && window.__hf.duration > 0)`, { timeout: pageReadyTimeout });
96
+ // Wait for all video elements to have loaded metadata (dimensions + duration)
97
+ // Without this, frame 0 captures videos at their 300x150 default size
98
+ await page.waitForFunction(`document.querySelectorAll("video").length === 0 || Array.from(document.querySelectorAll("video")).every(v => v.readyState >= 1)`, { timeout: pageReadyTimeout });
99
+ await page.evaluate(`document.fonts?.ready`);
100
+ session.isInitialized = true;
101
+ return;
102
+ }
103
+ // In BeginFrame mode, Chrome's event loop is paused until we issue frames.
104
+ // Start a warmup loop to drive rAF/setTimeout callbacks during page load.
105
+ let warmupRunning = true;
106
+ let warmupTicks = 0;
107
+ let warmupFrameTime = 0;
108
+ const warmupIntervalMs = 33; // ~30fps
109
+ let warmupClient = null;
110
+ const warmupLoop = async () => {
111
+ try {
112
+ warmupClient = await getCdpSession(page);
113
+ await warmupClient.send("HeadlessExperimental.enable");
114
+ }
115
+ catch {
116
+ /* page not ready yet */
117
+ }
118
+ while (warmupRunning) {
119
+ if (warmupClient) {
120
+ try {
121
+ await warmupClient.send("HeadlessExperimental.beginFrame", {
122
+ frameTimeTicks: warmupFrameTime,
123
+ interval: warmupIntervalMs,
124
+ noDisplayUpdates: true,
125
+ });
126
+ warmupFrameTime += warmupIntervalMs;
127
+ warmupTicks++;
128
+ }
129
+ catch {
130
+ /* ignore warmup errors */
131
+ }
132
+ }
133
+ await new Promise((r) => setTimeout(r, warmupIntervalMs));
134
+ }
135
+ };
136
+ warmupLoop().catch(() => { });
137
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });
138
+ // Poll for window.__hf readiness using manual evaluate loop (waitForFunction
139
+ // uses rAF polling internally, which won't fire in beginFrame mode).
140
+ const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
141
+ const pollDeadline = Date.now() + pageReadyTimeout;
142
+ while (Date.now() < pollDeadline) {
143
+ const ready = await page.evaluate(`!!(window.__hf && typeof window.__hf.seek === "function" && window.__hf.duration > 0)`);
144
+ if (ready)
145
+ break;
146
+ await new Promise((r) => setTimeout(r, 100));
147
+ }
148
+ const pageReady = await page.evaluate(`!!(window.__hf && typeof window.__hf.seek === "function" && window.__hf.duration > 0)`);
149
+ if (!pageReady) {
150
+ warmupRunning = false;
151
+ throw new Error(`[FrameCapture] window.__hf not ready after ${pageReadyTimeout}ms. Page must expose window.__hf = { duration, seek }.`);
152
+ }
153
+ // Wait for all video elements to have loaded metadata (dimensions + duration).
154
+ // Without this, frame 0 captures videos at their 300x150 default size.
155
+ const videoDeadline = Date.now() + (session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout);
156
+ while (Date.now() < videoDeadline) {
157
+ const videosReady = await page.evaluate(`document.querySelectorAll("video").length === 0 || Array.from(document.querySelectorAll("video")).every(v => v.readyState >= 1)`);
158
+ if (videosReady)
159
+ break;
160
+ await new Promise((r) => setTimeout(r, 100));
161
+ }
162
+ // Font check (no rAF dependency — uses fonts.ready API directly)
163
+ await page.evaluate(`document.fonts?.ready`);
164
+ // Stop warmup
165
+ warmupRunning = false;
166
+ // Set base frame time ticks past warmup range
167
+ session.beginFrameTimeTicks = (warmupTicks + 10) * session.beginFrameIntervalMs;
168
+ session.isInitialized = true;
169
+ }
170
+ async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
171
+ try {
172
+ const diagnosticsDir = join(session.outputDir, "diagnostics");
173
+ if (!existsSync(diagnosticsDir))
174
+ mkdirSync(diagnosticsDir, { recursive: true });
175
+ const base = join(diagnosticsDir, `frame-error-${frameIndex}`);
176
+ await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
177
+ const html = await session.page.content();
178
+ writeFileSync(`${base}.html`, html, "utf-8");
179
+ writeFileSync(`${base}.json`, JSON.stringify({
180
+ frameIndex,
181
+ time,
182
+ error: error.message,
183
+ stack: error.stack,
184
+ browserConsoleTail: session.browserConsoleBuffer.slice(-30),
185
+ }, null, 2), "utf-8");
186
+ return `${base}.json`;
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ /**
193
+ * Internal helper: seek timeline and inject video frames.
194
+ * Shared by captureFrame (disk) and captureFrameToBuffer (buffer).
195
+ * Returns timing breakdown for perf tracking.
196
+ */
197
+ async function prepareFrameForCapture(session, frameIndex, time) {
198
+ const { page, options } = session;
199
+ if (!session.isInitialized) {
200
+ throw new Error("[FrameCapture] Session not initialized");
201
+ }
202
+ const quantizedTime = quantizeTimeToFrame(time, options.fps);
203
+ const seekStart = Date.now();
204
+ // Seek via the __hf protocol. The page's seek() implementation handles
205
+ // all framework-specific logic (GSAP stepping, CSS animation sync, etc.)
206
+ await page.evaluate((t) => {
207
+ if (window.__hf && typeof window.__hf.seek === "function") {
208
+ window.__hf.seek(t);
209
+ }
210
+ }, quantizedTime);
211
+ const seekMs = Date.now() - seekStart;
212
+ // Before-capture hook (e.g. video frame injection)
213
+ const beforeCaptureStart = Date.now();
214
+ if (session.onBeforeCapture) {
215
+ await session.onBeforeCapture(page, quantizedTime);
216
+ }
217
+ const beforeCaptureMs = Date.now() - beforeCaptureStart;
218
+ return { quantizedTime, seekMs, beforeCaptureMs };
219
+ }
220
+ /**
221
+ * Internal core: prepare, screenshot, and track perf.
222
+ * Shared by captureFrame (disk) and captureFrameToBuffer (buffer).
223
+ * Returns the screenshot buffer, quantized time, and total capture time.
224
+ */
225
+ async function captureFrameCore(session, frameIndex, time) {
226
+ const { page, options } = session;
227
+ const startTime = Date.now();
228
+ try {
229
+ const { quantizedTime, seekMs, beforeCaptureMs } = await prepareFrameForCapture(session, frameIndex, time);
230
+ const screenshotStart = Date.now();
231
+ let screenshotBuffer;
232
+ if (session.captureMode === "beginframe") {
233
+ const frameTimeTicks = session.beginFrameTimeTicks + frameIndex * session.beginFrameIntervalMs;
234
+ const result = await beginFrameCapture(page, options, frameTimeTicks, session.beginFrameIntervalMs);
235
+ if (result.hasDamage)
236
+ session.beginFrameHasDamageCount++;
237
+ else
238
+ session.beginFrameNoDamageCount++;
239
+ screenshotBuffer = result.buffer;
240
+ }
241
+ else {
242
+ screenshotBuffer = await pageScreenshotCapture(page, options);
243
+ }
244
+ const screenshotMs = Date.now() - screenshotStart;
245
+ const captureTimeMs = Date.now() - startTime;
246
+ session.capturePerf.frames += 1;
247
+ session.capturePerf.seekMs += seekMs;
248
+ session.capturePerf.beforeCaptureMs += beforeCaptureMs;
249
+ session.capturePerf.screenshotMs += screenshotMs;
250
+ session.capturePerf.totalMs += captureTimeMs;
251
+ return { buffer: screenshotBuffer, quantizedTime, captureTimeMs };
252
+ }
253
+ catch (captureError) {
254
+ if (session.isInitialized) {
255
+ await captureFrameErrorDiagnostics(session, frameIndex, time, captureError instanceof Error ? captureError : new Error(String(captureError)));
256
+ }
257
+ throw captureError;
258
+ }
259
+ }
260
+ export async function captureFrame(session, frameIndex, time) {
261
+ const { options, outputDir } = session;
262
+ const { buffer, quantizedTime, captureTimeMs } = await captureFrameCore(session, frameIndex, time);
263
+ const ext = options.format === "png" ? "png" : "jpg";
264
+ const frameName = `frame_${String(frameIndex).padStart(6, "0")}.${ext}`;
265
+ const framePath = join(outputDir, frameName);
266
+ writeFileSync(framePath, buffer);
267
+ return { frameIndex, time: quantizedTime, path: framePath, captureTimeMs };
268
+ }
269
+ /**
270
+ * Capture a frame and return the screenshot as a Buffer instead of writing to disk.
271
+ * Used by the streaming encode pipeline to pipe frames directly to FFmpeg stdin.
272
+ */
273
+ export async function captureFrameToBuffer(session, frameIndex, time) {
274
+ const { buffer, captureTimeMs } = await captureFrameCore(session, frameIndex, time);
275
+ return { buffer, captureTimeMs };
276
+ }
277
+ export async function closeCaptureSession(session) {
278
+ if (session.page)
279
+ await session.page.close().catch(() => { });
280
+ if (session.browser)
281
+ await releaseBrowser(session.browser, session.config);
282
+ session.isInitialized = false;
283
+ }
284
+ export function prepareCaptureSessionForReuse(session, outputDir, onBeforeCapture) {
285
+ if (!existsSync(outputDir)) {
286
+ mkdirSync(outputDir, { recursive: true });
287
+ }
288
+ session.outputDir = outputDir;
289
+ session.onBeforeCapture = onBeforeCapture;
290
+ session.capturePerf = {
291
+ frames: 0,
292
+ seekMs: 0,
293
+ beforeCaptureMs: 0,
294
+ screenshotMs: 0,
295
+ totalMs: 0,
296
+ };
297
+ session.beginFrameHasDamageCount = 0;
298
+ session.beginFrameNoDamageCount = 0;
299
+ }
300
+ export async function getCompositionDuration(session) {
301
+ if (!session.isInitialized)
302
+ throw new Error("[FrameCapture] Session not initialized");
303
+ return session.page.evaluate(() => {
304
+ return window.__hf?.duration ?? 0;
305
+ });
306
+ }
307
+ export function getCapturePerfSummary(session) {
308
+ const frames = Math.max(1, session.capturePerf.frames);
309
+ return {
310
+ frames: session.capturePerf.frames,
311
+ avgTotalMs: Math.round(session.capturePerf.totalMs / frames),
312
+ avgSeekMs: Math.round(session.capturePerf.seekMs / frames),
313
+ avgBeforeCaptureMs: Math.round(session.capturePerf.beforeCaptureMs / frames),
314
+ avgScreenshotMs: Math.round(session.capturePerf.screenshotMs / frames),
315
+ };
316
+ }
317
+ //# sourceMappingURL=frameCapture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frameCapture.js","sourceRoot":"","sources":["../../src/services/frameCapture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Parallel Coordinator Service
3
+ *
4
+ * Coordinates parallel frame capture across multiple Puppeteer sessions.
5
+ * Auto-detects optimal worker count based on CPU/memory.
6
+ */
7
+ import { type CaptureOptions, type CapturePerfSummary, type BeforeCaptureHook } from "./frameCapture.js";
8
+ import { type EngineConfig } from "../config.js";
9
+ export interface WorkerTask {
10
+ workerId: number;
11
+ startFrame: number;
12
+ endFrame: number;
13
+ outputDir: string;
14
+ }
15
+ export interface WorkerResult {
16
+ workerId: number;
17
+ framesCaptured: number;
18
+ startFrame: number;
19
+ endFrame: number;
20
+ durationMs: number;
21
+ perf?: CapturePerfSummary;
22
+ error?: string;
23
+ }
24
+ export interface ParallelProgress {
25
+ totalFrames: number;
26
+ capturedFrames: number;
27
+ activeWorkers: number;
28
+ workerProgress: Map<number, number>;
29
+ }
30
+ export declare function calculateOptimalWorkers(totalFrames: number, requested?: number, config?: Partial<Pick<EngineConfig, "concurrency" | "coresPerWorker" | "minParallelFrames" | "largeRenderThreshold">>): number;
31
+ export declare function distributeFrames(totalFrames: number, workerCount: number, workDir: string): WorkerTask[];
32
+ export declare function executeParallelCapture(serverUrl: string, workDir: string, tasks: WorkerTask[], captureOptions: CaptureOptions, createBeforeCaptureHook: () => BeforeCaptureHook | null, signal?: AbortSignal, onProgress?: (progress: ParallelProgress) => void, onFrameBuffer?: (frameIndex: number, buffer: Buffer) => Promise<void>, config?: Partial<EngineConfig>): Promise<WorkerResult[]>;
33
+ export declare function mergeWorkerFrames(workDir: string, tasks: WorkerTask[], outputDir: string): Promise<number>;
34
+ export declare function getSystemResources(): {
35
+ cpuCores: number;
36
+ totalMemoryMB: number;
37
+ freeMemoryMB: number;
38
+ recommendedWorkers: number;
39
+ };
40
+ //# sourceMappingURL=parallelCoordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallelCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/parallelCoordinator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,EAQL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAQD,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,CACd,IAAI,CACF,YAAY,EACZ,aAAa,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,sBAAsB,CAChF,CACF,GACA,MAAM,CAgDR;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,UAAU,EAAE,CAkBd;AA0ED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,EAAE,EACnB,cAAc,EAAE,cAAc,EAC9B,uBAAuB,EAAE,MAAM,iBAAiB,GAAG,IAAI,EACvD,MAAM,CAAC,EAAE,WAAW,EACpB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,EACjD,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EACrE,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC7B,OAAO,CAAC,YAAY,EAAE,CAAC,CA2CzB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAgB,kBAAkB,IAAI;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAOA"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Parallel Coordinator Service
3
+ *
4
+ * Coordinates parallel frame capture across multiple Puppeteer sessions.
5
+ * Auto-detects optimal worker count based on CPU/memory.
6
+ */
7
+ import { cpus, freemem, totalmem } from "os";
8
+ import { existsSync, mkdirSync, readdirSync } from "fs";
9
+ import { copyFile, rename } from "fs/promises";
10
+ import { join } from "path";
11
+ import { createCaptureSession, initializeSession, closeCaptureSession, captureFrame, captureFrameToBuffer, getCapturePerfSummary, } from "./frameCapture.js";
12
+ import { DEFAULT_CONFIG } from "../config.js";
13
+ const MEMORY_PER_WORKER_MB = 256;
14
+ const MIN_WORKERS = 1;
15
+ const ABSOLUTE_MAX_WORKERS = 10;
16
+ const DEFAULT_SAFE_MAX_WORKERS = 6;
17
+ const MIN_FRAMES_PER_WORKER = 30;
18
+ export function calculateOptimalWorkers(totalFrames, requested, config) {
19
+ // Resolve effective values: config overrides → DEFAULT_CONFIG fallback.
20
+ const effectiveMaxWorkers = (() => {
21
+ const concurrency = config?.concurrency ?? DEFAULT_CONFIG.concurrency;
22
+ if (concurrency !== "auto") {
23
+ return Math.max(MIN_WORKERS, Math.min(ABSOLUTE_MAX_WORKERS, Math.floor(concurrency)));
24
+ }
25
+ return DEFAULT_SAFE_MAX_WORKERS;
26
+ })();
27
+ const effectiveCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG.coresPerWorker;
28
+ const effectiveMinParallelFrames = config?.minParallelFrames ?? DEFAULT_CONFIG.minParallelFrames;
29
+ const effectiveLargeRenderThreshold = config?.largeRenderThreshold ?? DEFAULT_CONFIG.largeRenderThreshold;
30
+ if (requested !== undefined) {
31
+ return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
32
+ }
33
+ if (totalFrames < MIN_FRAMES_PER_WORKER * 2)
34
+ return 1;
35
+ const cpuCount = cpus().length;
36
+ const cpuBasedWorkers = Math.max(1, cpuCount - 2);
37
+ // Use total memory instead of free memory — macOS reports misleadingly low
38
+ // freemem() because it aggressively caches files in "inactive" memory that
39
+ // is immediately reclaimable.
40
+ const totalMemoryMB = Math.round(totalmem() / (1024 * 1024));
41
+ const memoryBasedWorkers = Math.max(1, Math.floor((totalMemoryMB * 0.5) / MEMORY_PER_WORKER_MB));
42
+ const frameBasedWorkers = Math.floor(totalFrames / MIN_FRAMES_PER_WORKER);
43
+ const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
44
+ const minWorkersForJob = totalFrames >= effectiveMinParallelFrames ? 2 : MIN_WORKERS;
45
+ let finalWorkers = Math.max(minWorkersForJob, Math.min(effectiveMaxWorkers, optimal));
46
+ // Adaptive scaling: cap workers for large renders to prevent CPU contention.
47
+ // Each Chrome process (with SwiftShader) is CPU-heavy; too many on a long
48
+ // render causes protocol timeouts from compositor starvation.
49
+ // Scale proportionally to CPU count: ~3 cores per worker (benchmarked).
50
+ // 8 cores → 2 workers, 16 cores → 5 workers, 32 cores → 10 workers.
51
+ if (totalFrames >= effectiveLargeRenderThreshold) {
52
+ const cpuScaledMax = Math.max(2, Math.floor(cpuCount / effectiveCoresPerWorker));
53
+ if (finalWorkers > cpuScaledMax) {
54
+ finalWorkers = cpuScaledMax;
55
+ }
56
+ }
57
+ return finalWorkers;
58
+ }
59
+ export function distributeFrames(totalFrames, workerCount, workDir) {
60
+ const tasks = [];
61
+ const framesPerWorker = Math.ceil(totalFrames / workerCount);
62
+ for (let i = 0; i < workerCount; i++) {
63
+ const startFrame = i * framesPerWorker;
64
+ const endFrame = Math.min((i + 1) * framesPerWorker, totalFrames);
65
+ if (startFrame >= totalFrames)
66
+ break;
67
+ tasks.push({
68
+ workerId: i,
69
+ startFrame,
70
+ endFrame,
71
+ outputDir: join(workDir, `worker-${i}`),
72
+ });
73
+ }
74
+ return tasks;
75
+ }
76
+ async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
77
+ const startTime = Date.now();
78
+ let framesCaptured = 0;
79
+ if (!existsSync(task.outputDir))
80
+ mkdirSync(task.outputDir, { recursive: true });
81
+ let session = null;
82
+ let perf;
83
+ try {
84
+ session = await createCaptureSession(serverUrl, task.outputDir, captureOptions, createBeforeCaptureHook(), config);
85
+ await initializeSession(session);
86
+ for (let i = task.startFrame; i < task.endFrame; i++) {
87
+ if (signal?.aborted) {
88
+ throw new Error("Parallel worker cancelled");
89
+ }
90
+ const time = i / captureOptions.fps;
91
+ if (onFrameBuffer) {
92
+ // Streaming mode: capture to buffer and invoke callback
93
+ const { buffer } = await captureFrameToBuffer(session, i, time);
94
+ await onFrameBuffer(i, buffer);
95
+ }
96
+ else {
97
+ // Disk mode: capture to file
98
+ await captureFrame(session, i, time);
99
+ }
100
+ framesCaptured++;
101
+ if (onFrameCaptured)
102
+ onFrameCaptured(task.workerId, i);
103
+ }
104
+ perf = getCapturePerfSummary(session);
105
+ return {
106
+ workerId: task.workerId,
107
+ framesCaptured,
108
+ startFrame: task.startFrame,
109
+ endFrame: task.endFrame,
110
+ durationMs: Date.now() - startTime,
111
+ perf,
112
+ };
113
+ }
114
+ catch (error) {
115
+ const errMsg = error instanceof Error ? error.message : String(error);
116
+ return {
117
+ workerId: task.workerId,
118
+ framesCaptured,
119
+ startFrame: task.startFrame,
120
+ endFrame: task.endFrame,
121
+ durationMs: Date.now() - startTime,
122
+ perf,
123
+ error: errMsg,
124
+ };
125
+ }
126
+ finally {
127
+ if (session)
128
+ await closeCaptureSession(session).catch(() => { });
129
+ }
130
+ }
131
+ export async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions, createBeforeCaptureHook, signal, onProgress, onFrameBuffer, config) {
132
+ const totalFrames = tasks.reduce((sum, t) => sum + (t.endFrame - t.startFrame), 0);
133
+ const workerProgress = new Map();
134
+ for (const task of tasks)
135
+ workerProgress.set(task.workerId, 0);
136
+ const onFrameCaptured = (workerId, _frameIndex) => {
137
+ const current = workerProgress.get(workerId) || 0;
138
+ workerProgress.set(workerId, current + 1);
139
+ if (onProgress) {
140
+ const capturedFrames = Array.from(workerProgress.values()).reduce((a, b) => a + b, 0);
141
+ onProgress({
142
+ totalFrames,
143
+ capturedFrames,
144
+ activeWorkers: tasks.length,
145
+ workerProgress: new Map(workerProgress),
146
+ });
147
+ }
148
+ };
149
+ const results = await Promise.all(tasks.map((task) => executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config)));
150
+ const errors = results.filter((r) => r.error);
151
+ if (errors.length > 0) {
152
+ const errorMessages = errors.map((e) => `Worker ${e.workerId}: ${e.error}`).join("; ");
153
+ throw new Error(`[Parallel] Capture failed: ${errorMessages}`);
154
+ }
155
+ return results;
156
+ }
157
+ export async function mergeWorkerFrames(workDir, tasks, outputDir) {
158
+ if (!existsSync(outputDir))
159
+ mkdirSync(outputDir, { recursive: true });
160
+ let totalFrames = 0;
161
+ const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
162
+ for (const task of sortedTasks) {
163
+ if (!existsSync(task.outputDir)) {
164
+ continue;
165
+ }
166
+ const files = readdirSync(task.outputDir)
167
+ .filter((f) => f.startsWith("frame_") && (f.endsWith(".jpg") || f.endsWith(".png")))
168
+ .sort();
169
+ const copyTasks = files.map(async (file) => {
170
+ const sourcePath = join(task.outputDir, file);
171
+ const targetPath = join(outputDir, file);
172
+ try {
173
+ await rename(sourcePath, targetPath);
174
+ }
175
+ catch {
176
+ await copyFile(sourcePath, targetPath);
177
+ }
178
+ });
179
+ await Promise.all(copyTasks);
180
+ totalFrames += files.length;
181
+ }
182
+ return totalFrames;
183
+ }
184
+ export function getSystemResources() {
185
+ return {
186
+ cpuCores: cpus().length,
187
+ totalMemoryMB: Math.round(totalmem() / (1024 * 1024)),
188
+ freeMemoryMB: Math.round(freemem() / (1024 * 1024)),
189
+ recommendedWorkers: calculateOptimalWorkers(1000),
190
+ };
191
+ }
192
+ //# sourceMappingURL=parallelCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallelCoordinator.js","sourceRoot":"","sources":["../../src/services/parallelCoordinator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Screenshot Service
3
+ *
4
+ * BeginFrame-based deterministic screenshot capture and video frame injection.
5
+ */
6
+ import { type Page } from "puppeteer-core";
7
+ import { type CaptureOptions } from "../types.js";
8
+ export declare const cdpSessionCache: WeakMap<Page, import("puppeteer-core").CDPSession>;
9
+ export declare function getCdpSession(page: Page): Promise<import("puppeteer-core").CDPSession>;
10
+ /**
11
+ * BeginFrame result with screenshot data and damage detection.
12
+ */
13
+ export interface BeginFrameResult {
14
+ buffer: Buffer;
15
+ hasDamage: boolean;
16
+ }
17
+ export declare function beginFrameCapture(page: Page, options: CaptureOptions, frameTimeTicks: number, interval: number): Promise<BeginFrameResult>;
18
+ /**
19
+ * Capture a screenshot using standard Page.captureScreenshot CDP call.
20
+ * Fallback for environments where BeginFrame is unavailable (macOS, Windows).
21
+ */
22
+ export declare function pageScreenshotCapture(page: Page, options: CaptureOptions): Promise<Buffer>;
23
+ export declare function injectVideoFramesBatch(page: Page, updates: Array<{
24
+ videoId: string;
25
+ dataUri: string;
26
+ }>): Promise<void>;
27
+ export declare function syncVideoFrameVisibility(page: Page, activeVideoIds: string[]): Promise<void>;
28
+ //# sourceMappingURL=screenshotService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshotService.d.ts","sourceRoot":"","sources":["../../src/services/screenshotService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,eAAe,oDAA2D,CAAC;AAExF,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,gBAAgB,EAAE,UAAU,CAAC,CAO5F;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAkBD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,CAAC,CA4C3B;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAShG;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CAyFf;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAgBf"}