@hyperframes/engine 0.4.6 → 0.4.8

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 (84) hide show
  1. package/dist/config.d.ts +6 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +9 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.d.ts +11 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +10 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/services/browserManager.d.ts.map +1 -1
  10. package/dist/services/browserManager.js +6 -3
  11. package/dist/services/browserManager.js.map +1 -1
  12. package/dist/services/chunkEncoder.d.ts +14 -7
  13. package/dist/services/chunkEncoder.d.ts.map +1 -1
  14. package/dist/services/chunkEncoder.js +25 -9
  15. package/dist/services/chunkEncoder.js.map +1 -1
  16. package/dist/services/chunkEncoder.types.d.ts +4 -0
  17. package/dist/services/chunkEncoder.types.d.ts.map +1 -1
  18. package/dist/services/hdrCapture.d.ts +62 -0
  19. package/dist/services/hdrCapture.d.ts.map +1 -0
  20. package/dist/services/hdrCapture.js +259 -0
  21. package/dist/services/hdrCapture.js.map +1 -0
  22. package/dist/services/screenshotService.d.ts +115 -0
  23. package/dist/services/screenshotService.d.ts.map +1 -1
  24. package/dist/services/screenshotService.js +252 -19
  25. package/dist/services/screenshotService.js.map +1 -1
  26. package/dist/services/streamingEncoder.d.ts +18 -3
  27. package/dist/services/streamingEncoder.d.ts.map +1 -1
  28. package/dist/services/streamingEncoder.js +104 -50
  29. package/dist/services/streamingEncoder.js.map +1 -1
  30. package/dist/services/videoFrameExtractor.d.ts.map +1 -1
  31. package/dist/services/videoFrameExtractor.js +109 -18
  32. package/dist/services/videoFrameExtractor.js.map +1 -1
  33. package/dist/services/videoFrameInjector.d.ts +59 -0
  34. package/dist/services/videoFrameInjector.d.ts.map +1 -1
  35. package/dist/services/videoFrameInjector.js +290 -0
  36. package/dist/services/videoFrameInjector.js.map +1 -1
  37. package/dist/types.d.ts +32 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/utils/alphaBlit.d.ts +105 -0
  40. package/dist/utils/alphaBlit.d.ts.map +1 -0
  41. package/dist/utils/alphaBlit.js +550 -0
  42. package/dist/utils/alphaBlit.js.map +1 -0
  43. package/dist/utils/ffprobe.d.ts +10 -0
  44. package/dist/utils/ffprobe.d.ts.map +1 -1
  45. package/dist/utils/ffprobe.js +7 -0
  46. package/dist/utils/ffprobe.js.map +1 -1
  47. package/dist/utils/hdr.d.ts +82 -0
  48. package/dist/utils/hdr.d.ts.map +1 -0
  49. package/dist/utils/hdr.js +87 -0
  50. package/dist/utils/hdr.js.map +1 -0
  51. package/dist/utils/layerCompositor.d.ts +36 -0
  52. package/dist/utils/layerCompositor.d.ts.map +1 -0
  53. package/dist/utils/layerCompositor.js +49 -0
  54. package/dist/utils/layerCompositor.js.map +1 -0
  55. package/dist/utils/shaderTransitions.d.ts +166 -0
  56. package/dist/utils/shaderTransitions.d.ts.map +1 -0
  57. package/dist/utils/shaderTransitions.js +894 -0
  58. package/dist/utils/shaderTransitions.js.map +1 -0
  59. package/package.json +3 -2
  60. package/src/config.ts +16 -0
  61. package/src/index.ts +61 -0
  62. package/src/services/browserManager.ts +6 -3
  63. package/src/services/chunkEncoder.test.ts +88 -0
  64. package/src/services/chunkEncoder.ts +35 -9
  65. package/src/services/chunkEncoder.types.ts +3 -0
  66. package/src/services/hdrCapture.test.ts +159 -0
  67. package/src/services/hdrCapture.ts +354 -0
  68. package/src/services/screenshotService.ts +271 -17
  69. package/src/services/streamingEncoder.test.ts +228 -0
  70. package/src/services/streamingEncoder.ts +153 -63
  71. package/src/services/videoFrameExtractor.ts +135 -31
  72. package/src/services/videoFrameInjector.ts +342 -0
  73. package/src/types.ts +34 -0
  74. package/src/utils/alphaBlit.test.ts +993 -0
  75. package/src/utils/alphaBlit.ts +643 -0
  76. package/src/utils/ffprobe.ts +22 -0
  77. package/src/utils/hdr.test.ts +191 -0
  78. package/src/utils/hdr.ts +137 -0
  79. package/src/utils/layerCompositor.test.ts +141 -0
  80. package/src/utils/layerCompositor.ts +58 -0
  81. package/src/utils/shaderTransitions.test.ts +674 -0
  82. package/src/utils/shaderTransitions.ts +1130 -0
  83. package/src/utils/uint16-alignment-audit.test.ts +125 -0
  84. package/tsconfig.json +2 -1
@@ -104,6 +104,212 @@ export async function pageScreenshotCapture(page, options) {
104
104
  });
105
105
  return Buffer.from(result.data, "base64");
106
106
  }
107
+ /**
108
+ * Capture a screenshot with transparent background (PNG + alpha channel).
109
+ *
110
+ * Used in the two-pass HDR compositing pipeline — captures DOM content
111
+ * (text, graphics, SDR overlays) with transparency where the background shows,
112
+ * so it can be overlaid on top of native HDR video frames in FFmpeg.
113
+ *
114
+ * Sets and restores the background color override on every call. For sessions
115
+ * that capture many frames, prefer calling initTransparentBackground() once
116
+ * at session init, then captureAlphaPng() per frame to avoid the 2× CDP
117
+ * round-trip overhead.
118
+ */
119
+ export async function captureScreenshotWithAlpha(page, width, height) {
120
+ const client = await getCdpSession(page);
121
+ // Force transparent background so the screenshot has a real alpha channel
122
+ await client.send("Emulation.setDefaultBackgroundColorOverride", {
123
+ color: { r: 0, g: 0, b: 0, a: 0 },
124
+ });
125
+ try {
126
+ const result = await client.send("Page.captureScreenshot", {
127
+ format: "png",
128
+ fromSurface: true,
129
+ captureBeyondViewport: false,
130
+ optimizeForSpeed: false, // `true` uses a zero-alpha-aware fast path that crushes real alpha values — observed empirically, CDP docs don't spell it out
131
+ clip: { x: 0, y: 0, width, height, scale: 1 },
132
+ });
133
+ return Buffer.from(result.data, "base64");
134
+ }
135
+ finally {
136
+ // Restore opaque background even if captureScreenshot throws, otherwise
137
+ // subsequent opaque captures keep a transparent background.
138
+ await client.send("Emulation.setDefaultBackgroundColorOverride", {}).catch(() => { });
139
+ }
140
+ }
141
+ /**
142
+ * Set the page background to transparent once for a dedicated HDR DOM session.
143
+ *
144
+ * Call this once after session initialization. Then use captureAlphaPng() per
145
+ * frame instead of captureScreenshotWithAlpha() to skip the per-frame CDP
146
+ * background override round-trips.
147
+ *
148
+ * Only use on sessions that are exclusively dedicated to transparent capture
149
+ * (e.g., the HDR two-pass DOM layer session) — the background will stay
150
+ * transparent for the lifetime of the session.
151
+ *
152
+ * NOTE on the injected stylesheet: `Emulation.setDefaultBackgroundColorOverride`
153
+ * only replaces the *default* page background. Compositions almost always set
154
+ * `body { background: ... }` and `#root { background: ... }`, which paint over
155
+ * the override and ruin alpha capture for layered HDR compositing — the
156
+ * composition root's full-frame background paints across the entire viewport
157
+ * and wipes out HDR content captured beneath it.
158
+ *
159
+ * We force `html`, `body`, and any element marked as a composition root
160
+ * (`[data-composition-id]`) to transparent. In HDR layered compositing the HDR
161
+ * video itself is the backdrop, so DOM layers must only contribute their
162
+ * foreground UI pixels — never a page-spanning solid backdrop.
163
+ */
164
+ export const TRANSPARENT_BG_STYLE_ID = "__hf_transparent_bg__";
165
+ export async function initTransparentBackground(page) {
166
+ const client = await getCdpSession(page);
167
+ await client.send("Emulation.setDefaultBackgroundColorOverride", {
168
+ color: { r: 0, g: 0, b: 0, a: 0 },
169
+ });
170
+ await page.evaluate((styleId) => {
171
+ if (document.getElementById(styleId))
172
+ return;
173
+ const style = document.createElement("style");
174
+ style.id = styleId;
175
+ style.textContent =
176
+ "html,body,[data-composition-id]{background:transparent !important;background-color:transparent !important;background-image:none !important;}";
177
+ document.head.appendChild(style);
178
+ }, TRANSPARENT_BG_STYLE_ID);
179
+ }
180
+ /**
181
+ * Capture a transparent-background PNG screenshot without setting the
182
+ * background color override. Requires initTransparentBackground() to have
183
+ * been called once on this session.
184
+ *
185
+ * Faster than captureScreenshotWithAlpha() for per-frame use in the HDR
186
+ * two-pass compositing loop.
187
+ */
188
+ export async function captureAlphaPng(page, width, height) {
189
+ const client = await getCdpSession(page);
190
+ const result = await client.send("Page.captureScreenshot", {
191
+ format: "png",
192
+ fromSurface: true,
193
+ captureBeyondViewport: false,
194
+ optimizeForSpeed: false, // must be false to preserve alpha
195
+ clip: { x: 0, y: 0, width, height, scale: 1 },
196
+ });
197
+ return Buffer.from(result.data, "base64");
198
+ }
199
+ /**
200
+ * Stylesheet ID used by applyDomLayerMask / removeDomLayerMask. Exposed so
201
+ * tests can assert presence/absence of the mask between captures.
202
+ */
203
+ export const DOM_LAYER_MASK_STYLE_ID = "__hf_dom_layer_mask__";
204
+ /**
205
+ * Mask the DOM so a single layer screenshot captures ONLY the layer's pixels.
206
+ *
207
+ * The HDR layered compositor walks z-ordered layers and blits each one over a
208
+ * shared canvas. DOM layers are full-page screenshots — a naive screenshot
209
+ * captures every painted pixel on the page, which means root background +
210
+ * static overlays + sibling-scene content all overwrite previously composited
211
+ * HDR content beneath. The mask narrows each screenshot to the elements that
212
+ * actually belong to this layer.
213
+ *
214
+ * Strategy:
215
+ *
216
+ * 1. Inject a stylesheet that hides every body descendant
217
+ * (`body * { visibility: hidden !important }`) and re-shows the layer's
218
+ * elements (and their descendants and their injected `__render_frame_*`
219
+ * siblings) via `visibility: visible !important`. CSS `visibility: visible`
220
+ * on a descendant overrides an ancestor's `visibility: hidden`, so deep
221
+ * layer elements remain visible even though intermediate parents are
222
+ * hidden by the mass-hide rule.
223
+ * 2. Inline-hide each `extraHideId` (and its `__render_frame_*` sibling) with
224
+ * `visibility: hidden !important`. Inline `!important` beats stylesheet
225
+ * `!important`, so this overrides the show rule for elements that fall
226
+ * under a show selector but should NOT paint — typically other-layer
227
+ * elements that are descendants of a container layer (for example HDR
228
+ * videos and other-layer SDR videos are descendants of `#root` when we
229
+ * capture the root DOM layer).
230
+ *
231
+ * Only `visibility` is set on extraHideIds — never `opacity`. CSS opacity is
232
+ * multiplicative through the descendant chain and a descendant cannot escape
233
+ * an ancestor's `opacity: 0`. If `#root` is in `extraHideIds` and we set
234
+ * `opacity: 0` on it, every descendant — including `#vid-5-b` and its
235
+ * `__render_frame_vid-5-b__` IMG — becomes invisible even with
236
+ * `visibility: visible !important`. `visibility` does NOT have this problem:
237
+ * a descendant with `visibility: visible` overrides an ancestor's
238
+ * `visibility: hidden`.
239
+ *
240
+ * Layout is preserved (visibility doesn't trigger reflow), so border-radius
241
+ * clipping, overflow:hidden, and absolute positioning continue to apply to
242
+ * the visible layer elements. Opacity is also preserved — an ancestor at
243
+ * `opacity: 0` (e.g. an inactive scene during a transition) still
244
+ * propagates to its descendants, which is the desired behavior during
245
+ * cross-scene blends.
246
+ *
247
+ * Idempotent across calls: an existing mask stylesheet is removed before a
248
+ * new one is installed, so consecutive `applyDomLayerMask` invocations leave
249
+ * exactly one stylesheet attached.
250
+ */
251
+ export async function applyDomLayerMask(page, showIds, extraHideIds) {
252
+ await page.evaluate((args) => {
253
+ const existing = document.getElementById(args.styleId);
254
+ if (existing)
255
+ existing.remove();
256
+ const showSelectors = [];
257
+ for (const id of args.show) {
258
+ const escaped = CSS.escape(id);
259
+ showSelectors.push(`#${escaped}`, `#${escaped} *`);
260
+ const renderEscaped = CSS.escape(`__render_frame_${id}__`);
261
+ showSelectors.push(`#${renderEscaped}`, `#${renderEscaped} *`);
262
+ }
263
+ const massHideRule = "body *{visibility:hidden !important;}";
264
+ const showRule = showSelectors.length === 0
265
+ ? ""
266
+ : `${showSelectors.join(",")}{visibility:visible !important;}`;
267
+ const style = document.createElement("style");
268
+ style.id = args.styleId;
269
+ style.textContent = `${massHideRule}\n${showRule}`;
270
+ document.head.appendChild(style);
271
+ for (const id of args.hide) {
272
+ const el = document.getElementById(id);
273
+ if (el) {
274
+ el.style.setProperty("visibility", "hidden", "important");
275
+ }
276
+ const img = document.getElementById(`__render_frame_${id}__`);
277
+ if (img) {
278
+ img.style.setProperty("visibility", "hidden", "important");
279
+ }
280
+ }
281
+ }, { show: showIds, hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID });
282
+ }
283
+ /**
284
+ * Tear down the mask installed by applyDomLayerMask.
285
+ *
286
+ * Removes the mask stylesheet and clears the inline `visibility` properties
287
+ * set on `extraHideIds` (and their `__render_frame_*` siblings).
288
+ *
289
+ * IMPORTANT: We do NOT strip inline `opacity` here. applyDomLayerMask only
290
+ * ever sets `visibility` (never `opacity`), so any inline opacity present on
291
+ * a wrapper was put there by user animation code (typically GSAP) and must
292
+ * survive across per-layer captures. GSAP's seek with suppress-events does
293
+ * not re-apply tweens when the timeline is already at the target time, so if
294
+ * we strip opacity here and then seek to the same time for the next layer,
295
+ * GSAP won't put it back and the wrapper will render fully opaque.
296
+ */
297
+ export async function removeDomLayerMask(page, extraHideIds) {
298
+ await page.evaluate((args) => {
299
+ const style = document.getElementById(args.styleId);
300
+ if (style)
301
+ style.remove();
302
+ for (const id of args.hide) {
303
+ const el = document.getElementById(id);
304
+ if (el) {
305
+ el.style.removeProperty("visibility");
306
+ }
307
+ const img = document.getElementById(`__render_frame_${id}__`);
308
+ if (img)
309
+ img.style.removeProperty("visibility");
310
+ }
311
+ }, { hide: extraHideIds, styleId: DOM_LAYER_MASK_STYLE_ID });
312
+ }
107
313
  export async function injectVideoFramesBatch(page, updates) {
108
314
  if (updates.length === 0)
109
315
  return;
@@ -116,6 +322,16 @@ export async function injectVideoFramesBatch(page, updates) {
116
322
  let img = video.nextElementSibling;
117
323
  const isNewImage = !img || !img.classList.contains("__render_frame__");
118
324
  const computedStyle = window.getComputedStyle(video);
325
+ // GSAP seeks re-apply tween values during an active tween, but do not
326
+ // re-apply tweens that have already completed. After an opacity fade-in
327
+ // finishes, GSAP's last set value is overwritten on subsequent frames
328
+ // by the `opacity: 0 !important` we apply at the bottom of this
329
+ // function to hide the native <video>. That leaves `computedOpacity`
330
+ // stuck at 0 even though the user's intent is opacity 1 (the tween's
331
+ // end state). The `|| 1` fallback treats computedOpacity === 0 as a
332
+ // hidden-native-video artifact and recovers opacity 1, matching the
333
+ // final on-screen state for the vast majority of compositions.
334
+ // For active tweens in the [0,1] exclusive range this is a no-op.
119
335
  const computedOpacity = parseFloat(computedStyle.opacity) || 1;
120
336
  const sourceIsStatic = !computedStyle.position || computedStyle.position === "static";
121
337
  if (isNewImage) {
@@ -127,17 +343,11 @@ export async function injectVideoFramesBatch(page, updates) {
127
343
  }
128
344
  if (!img)
129
345
  continue;
130
- if (!sourceIsStatic) {
131
- img.style.position = computedStyle.position;
132
- img.style.width = computedStyle.width;
133
- img.style.height = computedStyle.height;
134
- img.style.top = computedStyle.top;
135
- img.style.left = computedStyle.left;
136
- img.style.right = computedStyle.right;
137
- img.style.bottom = computedStyle.bottom;
138
- img.style.inset = computedStyle.inset;
139
- }
140
- else {
346
+ // Always use absolute positioning so the <img> overlays the <video>
347
+ // instead of flowing below it. With position:relative, both elements
348
+ // stack vertically — the <img> lands below the video and gets clipped
349
+ // by any overflow:hidden ancestor (e.g., border-radius wrappers).
350
+ {
141
351
  const videoRect = video.getBoundingClientRect();
142
352
  const offsetLeft = Number.isFinite(video.offsetLeft) ? video.offsetLeft : 0;
143
353
  const offsetTop = Number.isFinite(video.offsetTop) ? video.offsetTop : 0;
@@ -156,6 +366,15 @@ export async function injectVideoFramesBatch(page, updates) {
156
366
  img.style.objectPosition = computedStyle.objectPosition;
157
367
  img.style.zIndex = computedStyle.zIndex;
158
368
  for (const property of visualProperties) {
369
+ // Opacity is handled explicitly via `computedOpacity` below — copying
370
+ // via the generic loop would race against the opacity:0 hide applied
371
+ // to the <video> at the end of this function. GSAP may animate
372
+ // opacity either on a wrapper (the <img> inherits via the stacking
373
+ // context) or directly on the <video> (we must copy it to the <img>
374
+ // since they are siblings). Reading computedStyle.opacity before
375
+ // hiding the <video> handles both cases correctly.
376
+ if (property === "opacity")
377
+ continue;
159
378
  if (sourceIsStatic &&
160
379
  (property === "top" ||
161
380
  property === "left" ||
@@ -191,15 +410,29 @@ export async function syncVideoFrameVisibility(page, activeVideoIds) {
191
410
  const active = new Set(ids);
192
411
  const videos = Array.from(document.querySelectorAll("video[data-start]"));
193
412
  for (const video of videos) {
194
- if (active.has(video.id))
195
- continue;
196
- video.style.removeProperty("display");
197
- video.style.setProperty("visibility", "hidden", "important");
198
- video.style.setProperty("opacity", "0", "important");
199
- video.style.setProperty("pointer-events", "none", "important");
200
413
  const img = video.nextElementSibling;
201
- if (img && img.classList.contains("__render_frame__")) {
202
- img.style.visibility = "hidden";
414
+ const hasImg = img && img.classList.contains("__render_frame__");
415
+ if (active.has(video.id)) {
416
+ // Active video: show injected <img>, hide native <video>.
417
+ // Do NOT clobber inline opacity here — GSAP-controlled opacity must
418
+ // survive until injectVideoFramesBatch reads it via getComputedStyle.
419
+ // visibility:hidden alone hides the native element without affecting
420
+ // its computed opacity.
421
+ video.style.setProperty("visibility", "hidden", "important");
422
+ video.style.setProperty("pointer-events", "none", "important");
423
+ if (hasImg) {
424
+ img.style.visibility = "visible";
425
+ }
426
+ }
427
+ else {
428
+ // Inactive video: hide both
429
+ video.style.removeProperty("display");
430
+ video.style.setProperty("visibility", "hidden", "important");
431
+ video.style.setProperty("opacity", "0", "important");
432
+ video.style.setProperty("pointer-events", "none", "important");
433
+ if (hasImg) {
434
+ img.style.visibility = "hidden";
435
+ }
203
436
  }
204
437
  }
205
438
  }, activeVideoIds);
@@ -1 +1 @@
1
- {"version":3,"file":"screenshotService.js","sourceRoot":"","sources":["../../src/services/screenshotService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshotService.js","sourceRoot":"","sources":["../../src/services/screenshotService.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Streaming Encoder Service
3
3
  *
4
- * Pipes frame screenshot buffers directly to FFmpeg's stdin instead of writing
5
- * them to disk and reading them back in a separate encode stage. Follows the
6
- * Remotion pattern of image2pipe FFmpeg.
4
+ * Pipes frame screenshot buffers directly to FFmpeg's stdin via `-f image2pipe`
5
+ * instead of writing them to disk and reading them back in a separate encode
6
+ * stage. Inspired by Remotion's approach to browser-based video rendering.
7
7
  *
8
8
  * Two building blocks:
9
9
  * 1. Frame reorder buffer – ensures out-of-order parallel workers feed
@@ -11,6 +11,7 @@
11
11
  * 2. Streaming FFmpeg encoder – spawns FFmpeg with `-f image2pipe` and
12
12
  * exposes a `writeFrame(buffer)` + `close()` API.
13
13
  */
14
+ import { type GpuEncoder } from "../utils/gpuEncoder.js";
14
15
  import { type EngineConfig } from "../config.js";
15
16
  export type { EncoderOptions } from "./chunkEncoder.types.js";
16
17
  export interface FrameReorderBuffer {
@@ -30,6 +31,11 @@ export interface StreamingEncoderOptions {
30
31
  pixelFormat?: string;
31
32
  useGpu?: boolean;
32
33
  imageFormat?: "jpeg" | "png";
34
+ hdr?: {
35
+ transfer: import("../utils/hdr.js").HdrTransfer;
36
+ };
37
+ /** When set, use rawvideo input instead of image2pipe. For HDR PQ-encoded frames. */
38
+ rawInputFormat?: "rgb48le";
33
39
  }
34
40
  export interface StreamingEncoderResult {
35
41
  success: boolean;
@@ -42,6 +48,15 @@ export interface StreamingEncoder {
42
48
  close: () => Promise<StreamingEncoderResult>;
43
49
  getExitStatus: () => "running" | "success" | "error";
44
50
  }
51
+ /**
52
+ * Build FFmpeg args for streaming (image2pipe) input.
53
+ * Reuses the same codec/quality/GPU logic as chunkEncoder's buildEncoderArgs
54
+ * but with `-f image2pipe` instead of `-i <pattern>`.
55
+ *
56
+ * Exported so unit tests can assert on the constructed CLI without spawning
57
+ * FFmpeg — see streamingEncoder.test.ts.
58
+ */
59
+ export declare function buildStreamingArgs(options: StreamingEncoderOptions, outputPath: string, gpuEncoder?: GpuEncoder): string[];
45
60
  /**
46
61
  * Spawn a streaming FFmpeg encoder that accepts frame buffers on stdin.
47
62
  */
@@ -1 +1 @@
1
- {"version":3,"file":"streamingEncoder.d.ts","sourceRoot":"","sources":["../../src/services/streamingEncoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM9D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA6BjG;AAMD,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,KAAK,EAAE,MAAM,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CACtD;AA0ID;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,uBAAuB,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,wBAAwB,CAAC,CAAC,GAC7D,OAAO,CAAC,gBAAgB,CAAC,CAgH3B"}
1
+ {"version":3,"file":"streamingEncoder.d.ts","sourceRoot":"","sources":["../../src/services/streamingEncoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,EAAE,KAAK,UAAU,EAA0C,MAAM,wBAAwB,CAAC;AAGjG,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAc9D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA4CjG;AAMD,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,GAAG,CAAC,EAAE;QAAE,QAAQ,EAAE,OAAO,iBAAiB,EAAE,WAAW,CAAA;KAAE,CAAC;IAC1D,qFAAqF;IACrF,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,KAAK,EAAE,MAAM,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CACtD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,uBAAuB,EAChC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,UAAiB,GAC5B,MAAM,EAAE,CAgLV;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,uBAAuB,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,wBAAwB,CAAC,CAAC,GAC7D,OAAO,CAAC,gBAAgB,CAAC,CAyH3B"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Streaming Encoder Service
3
3
  *
4
- * Pipes frame screenshot buffers directly to FFmpeg's stdin instead of writing
5
- * them to disk and reading them back in a separate encode stage. Follows the
6
- * Remotion pattern of image2pipe FFmpeg.
4
+ * Pipes frame screenshot buffers directly to FFmpeg's stdin via `-f image2pipe`
5
+ * instead of writing them to disk and reading them back in a separate encode
6
+ * stage. Inspired by Remotion's approach to browser-based video rendering.
7
7
  *
8
8
  * Two building blocks:
9
9
  * 1. Frame reorder buffer – ensures out-of-order parallel workers feed
@@ -15,54 +15,78 @@ import { spawn } from "child_process";
15
15
  import { existsSync, mkdirSync, statSync } from "fs";
16
16
  import { dirname } from "path";
17
17
  import { getCachedGpuEncoder, getGpuEncoderName } from "../utils/gpuEncoder.js";
18
+ import { getHdrEncoderColorParams } from "../utils/hdr.js";
18
19
  import { DEFAULT_CONFIG } from "../config.js";
19
20
  export function createFrameReorderBuffer(startFrame, endFrame) {
20
- let nextFrame = startFrame;
21
- let waiters = [];
22
- const resolveWaiters = () => {
23
- for (const waiter of waiters.slice()) {
24
- if (waiter.frame === nextFrame) {
25
- waiter.resolve();
26
- waiters = waiters.filter((w) => w !== waiter);
27
- }
21
+ let cursor = startFrame;
22
+ const pending = new Map();
23
+ const enqueueAt = (frame, resolve) => {
24
+ const list = pending.get(frame);
25
+ if (list === undefined) {
26
+ pending.set(frame, [resolve]);
27
+ }
28
+ else {
29
+ list.push(resolve);
28
30
  }
29
31
  };
30
- return {
31
- waitForFrame: (frame) => new Promise((resolve) => {
32
- waiters.push({ frame, resolve });
33
- resolveWaiters();
34
- }),
35
- advanceTo: (frame) => {
36
- nextFrame = frame;
37
- resolveWaiters();
38
- },
39
- waitForAllDone: () => new Promise((resolve) => {
40
- waiters.push({ frame: endFrame, resolve });
41
- resolveWaiters();
42
- }),
32
+ const flushAt = (frame) => {
33
+ const list = pending.get(frame);
34
+ if (list === undefined)
35
+ return;
36
+ pending.delete(frame);
37
+ for (const resolve of list)
38
+ resolve();
43
39
  };
40
+ const waitForFrame = (frame) => new Promise((resolve) => {
41
+ if (frame === cursor) {
42
+ resolve();
43
+ return;
44
+ }
45
+ enqueueAt(frame, resolve);
46
+ });
47
+ const advanceTo = (frame) => {
48
+ cursor = frame;
49
+ flushAt(frame);
50
+ };
51
+ const waitForAllDone = () => new Promise((resolve) => {
52
+ if (cursor >= endFrame) {
53
+ resolve();
54
+ return;
55
+ }
56
+ enqueueAt(endFrame, resolve);
57
+ });
58
+ return { waitForFrame, advanceTo, waitForAllDone };
44
59
  }
45
60
  /**
46
61
  * Build FFmpeg args for streaming (image2pipe) input.
47
62
  * Reuses the same codec/quality/GPU logic as chunkEncoder's buildEncoderArgs
48
63
  * but with `-f image2pipe` instead of `-i <pattern>`.
64
+ *
65
+ * Exported so unit tests can assert on the constructed CLI without spawning
66
+ * FFmpeg — see streamingEncoder.test.ts.
49
67
  */
50
- function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
68
+ export function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
51
69
  const { fps, codec = "h264", preset = "medium", quality = 23, bitrate, pixelFormat = "yuv420p", useGpu = false, imageFormat = "jpeg", } = options;
52
70
  // Input args: pipe from stdin
53
- const inputCodec = imageFormat === "png" ? "png" : "mjpeg";
54
- const args = [
55
- "-f",
56
- "image2pipe",
57
- "-vcodec",
58
- inputCodec,
59
- "-framerate",
60
- String(fps),
61
- "-i",
62
- "-",
63
- "-r",
64
- String(fps),
65
- ];
71
+ const args = [];
72
+ if (options.rawInputFormat) {
73
+ // Raw pixel input (HLG/PQ-encoded rgb48le from FFmpeg extraction).
74
+ // Tag the input with the correct color space so FFmpeg uses the right
75
+ // YUV matrix when converting rgb48le → yuv420p10le for encoding.
76
+ // Without these tags FFmpeg assumes bt709 and applies the wrong matrix.
77
+ const hdrTransfer = options.hdr?.transfer;
78
+ const inputColorTrc = hdrTransfer === "pq" ? "smpte2084" : hdrTransfer === "hlg" ? "arib-std-b67" : undefined;
79
+ args.push("-f", "rawvideo", "-pix_fmt", options.rawInputFormat, "-s", `${options.width}x${options.height}`, "-framerate", String(fps));
80
+ if (inputColorTrc) {
81
+ args.push("-color_primaries", "bt2020", "-color_trc", inputColorTrc, "-colorspace", "bt2020nc");
82
+ }
83
+ args.push("-i", "-");
84
+ }
85
+ else {
86
+ const inputCodec = imageFormat === "png" ? "png" : "mjpeg";
87
+ args.push("-f", "image2pipe", "-vcodec", inputCodec, "-framerate", String(fps), "-i", "-");
88
+ }
89
+ args.push("-r", String(fps));
66
90
  const shouldUseGpu = useGpu && gpuEncoder !== null;
67
91
  if (codec === "h264" || codec === "h265") {
68
92
  if (shouldUseGpu) {
@@ -109,17 +133,25 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
109
133
  args.push("-b:v", bitrate);
110
134
  else
111
135
  args.push("-crf", String(quality));
112
- // Encoder-specific params: anti-banding + bt709 color space.
113
- // aq-mode=3 redistributes bits to dark flat areas (gradients).
114
- // colorprim/transfer/colormatrix embed bt709 in the H.264/H.265 VUI.
136
+ // Encoder-specific params: anti-banding + color space tagging.
137
+ // For HDR, getHdrEncoderColorParams also emits the SMPTE ST 2086
138
+ // mastering-display and CTA-861.3 MaxCLL/MaxFALL SEI messages
139
+ // without them, players (Apple, YouTube, HDR TVs) treat the file
140
+ // as SDR BT.2020 and tone-map incorrectly.
115
141
  const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
116
- const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
142
+ const colorParams = options.rawInputFormat && options.hdr
143
+ ? getHdrEncoderColorParams(options.hdr.transfer).x265ColorParams
144
+ : "colorprim=bt709:transfer=bt709:colormatrix=bt709";
117
145
  if (preset === "ultrafast") {
118
146
  args.push(xParamsFlag, `aq-mode=3:${colorParams}`);
119
147
  }
120
148
  else {
121
149
  args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
122
150
  }
151
+ // Apple devices require hvc1 tag for HEVC playback (default hev1 won't open in QuickTime)
152
+ if (codec === "h265") {
153
+ args.push("-tag:v", "hvc1");
154
+ }
123
155
  }
124
156
  }
125
157
  else if (codec === "vp9") {
@@ -136,18 +168,31 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
136
168
  args.push("-pix_fmt", pixelFormat);
137
169
  return [...args, "-y", outputPath];
138
170
  }
139
- // BT.709 color space metadata — Chrome screenshots are sRGB which maps to bt709.
140
- // Tags the output so players interpret colors correctly across devices.
171
+ // Color space metadata.
172
+ // When rawInputFormat is set, data comes from the WebGPU HDR pipeline
173
+ // (PQ-encoded) — tag with bt2020/PQ truthfully.
174
+ // Otherwise, Chrome captures sRGB — tag as bt709.
141
175
  if (codec === "h264" || codec === "h265") {
142
- args.push("-colorspace:v", "bt709", "-color_primaries:v", "bt709", "-color_trc:v", "bt709", "-color_range", "tv");
143
- // Convert full-range RGB input (Chrome screenshots) to limited/TV range for H.264.
144
- if (gpuEncoder === "vaapi") {
176
+ if (options.rawInputFormat && options.hdr) {
177
+ args.push("-colorspace:v", "bt2020nc", "-color_primaries:v", "bt2020", "-color_trc:v", options.hdr.transfer === "pq" ? "smpte2084" : "arib-std-b67", "-color_range", "tv");
178
+ }
179
+ else {
180
+ args.push("-colorspace:v", "bt709", "-color_primaries:v", "bt709", "-color_trc:v", "bt709", "-color_range", "tv");
181
+ }
182
+ // Video filter for range/color conversion.
183
+ // Raw HDR input (from WebGPU pipeline) is already PQ-encoded — no conversion needed.
184
+ // Chrome screenshots need full→TV range conversion.
185
+ if (options.rawInputFormat) {
186
+ // No filter needed — PQ data goes straight to encoder
187
+ }
188
+ else if (gpuEncoder === "vaapi") {
145
189
  const vfIdx = args.indexOf("-vf");
146
190
  if (vfIdx !== -1) {
147
191
  args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
148
192
  }
149
193
  }
150
194
  else if (!shouldUseGpu) {
195
+ // Range conversion: Chrome screenshots are full-range RGB.
151
196
  args.push("-vf", "scale=in_range=pc:out_range=tv");
152
197
  }
153
198
  // Fixed timescale for consistent A/V timing across platforms.
@@ -220,16 +265,25 @@ export async function spawnStreamingEncoder(outputPath, options, signal, config)
220
265
  if (exitStatus !== "running" || !ffmpeg.stdin || ffmpeg.stdin.destroyed) {
221
266
  return false;
222
267
  }
223
- return ffmpeg.stdin.write(buffer);
268
+ // Copy the buffer before writing — Node streams hold a reference to the
269
+ // provided buffer and drain it asynchronously. The HDR path's compositor
270
+ // reuses pre-allocated transOutput/normalCanvas buffers across frames,
271
+ // so without this copy the pipe would read partially-overwritten data
272
+ // and flicker. The SDR path doesn't invoke writeFrame at all (it pipes
273
+ // PNG files via encodeFramesFromDir), so the memcpy here is HDR-only
274
+ // and justified by correctness.
275
+ const copy = Buffer.from(buffer);
276
+ return ffmpeg.stdin.write(copy);
224
277
  },
225
278
  close: async () => {
226
279
  clearTimeout(timer);
227
280
  if (signal)
228
281
  signal.removeEventListener("abort", onAbort);
229
282
  // Close stdin to signal end of input
230
- if (ffmpeg.stdin && !ffmpeg.stdin.destroyed) {
283
+ const stdin = ffmpeg.stdin;
284
+ if (stdin && !stdin.destroyed) {
231
285
  await new Promise((resolve) => {
232
- ffmpeg.stdin.end(() => resolve());
286
+ stdin.end(() => resolve());
233
287
  });
234
288
  }
235
289
  // Wait for FFmpeg to finish