@hyperframes/engine 0.4.5 → 0.4.7

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 (78) 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 +8 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +7 -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/frameCapture.d.ts.map +1 -1
  19. package/dist/services/frameCapture.js +18 -2
  20. package/dist/services/frameCapture.js.map +1 -1
  21. package/dist/services/hdrCapture.d.ts +62 -0
  22. package/dist/services/hdrCapture.d.ts.map +1 -0
  23. package/dist/services/hdrCapture.js +259 -0
  24. package/dist/services/hdrCapture.js.map +1 -0
  25. package/dist/services/screenshotService.d.ts +34 -0
  26. package/dist/services/screenshotService.d.ts.map +1 -1
  27. package/dist/services/screenshotService.js +97 -19
  28. package/dist/services/screenshotService.js.map +1 -1
  29. package/dist/services/streamingEncoder.d.ts +18 -3
  30. package/dist/services/streamingEncoder.d.ts.map +1 -1
  31. package/dist/services/streamingEncoder.js +104 -50
  32. package/dist/services/streamingEncoder.js.map +1 -1
  33. package/dist/services/videoFrameExtractor.d.ts.map +1 -1
  34. package/dist/services/videoFrameExtractor.js +109 -18
  35. package/dist/services/videoFrameExtractor.js.map +1 -1
  36. package/dist/services/videoFrameInjector.d.ts +54 -0
  37. package/dist/services/videoFrameInjector.d.ts.map +1 -1
  38. package/dist/services/videoFrameInjector.js +159 -0
  39. package/dist/services/videoFrameInjector.js.map +1 -1
  40. package/dist/utils/alphaBlit.d.ts +105 -0
  41. package/dist/utils/alphaBlit.d.ts.map +1 -0
  42. package/dist/utils/alphaBlit.js +550 -0
  43. package/dist/utils/alphaBlit.js.map +1 -0
  44. package/dist/utils/ffprobe.d.ts +10 -0
  45. package/dist/utils/ffprobe.d.ts.map +1 -1
  46. package/dist/utils/ffprobe.js +7 -0
  47. package/dist/utils/ffprobe.js.map +1 -1
  48. package/dist/utils/hdr.d.ts +82 -0
  49. package/dist/utils/hdr.d.ts.map +1 -0
  50. package/dist/utils/hdr.js +87 -0
  51. package/dist/utils/hdr.js.map +1 -0
  52. package/dist/utils/layerCompositor.d.ts +36 -0
  53. package/dist/utils/layerCompositor.d.ts.map +1 -0
  54. package/dist/utils/layerCompositor.js +49 -0
  55. package/dist/utils/layerCompositor.js.map +1 -0
  56. package/package.json +3 -2
  57. package/src/config.ts +16 -0
  58. package/src/index.ts +42 -0
  59. package/src/services/browserManager.ts +6 -3
  60. package/src/services/chunkEncoder.test.ts +88 -0
  61. package/src/services/chunkEncoder.ts +35 -9
  62. package/src/services/chunkEncoder.types.ts +3 -0
  63. package/src/services/frameCapture.ts +31 -4
  64. package/src/services/hdrCapture.test.ts +159 -0
  65. package/src/services/hdrCapture.ts +354 -0
  66. package/src/services/screenshotService.ts +102 -17
  67. package/src/services/streamingEncoder.test.ts +228 -0
  68. package/src/services/streamingEncoder.ts +153 -63
  69. package/src/services/videoFrameExtractor.ts +135 -31
  70. package/src/services/videoFrameInjector.ts +200 -0
  71. package/src/utils/alphaBlit.test.ts +993 -0
  72. package/src/utils/alphaBlit.ts +643 -0
  73. package/src/utils/ffprobe.ts +22 -0
  74. package/src/utils/hdr.test.ts +191 -0
  75. package/src/utils/hdr.ts +137 -0
  76. package/src/utils/layerCompositor.test.ts +141 -0
  77. package/src/utils/layerCompositor.ts +58 -0
  78. package/tsconfig.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ffprobe.js","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,+FAA+F;AAC/F,SAAS,UAAU,CAAC,IAAc;IAChC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACpC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9B,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9B,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC;YAC5E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,MAAM,CAAC,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC,CAAC;YAC1E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,MAAc;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4CAA4C,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CACjF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAkC,CAAC;AACrE,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAkC,CAAC;AA0CrE,SAAS,cAAc,CAAC,YAAgC;IACtD,IAAI,CAAC,YAAY;QAAE,OAAO,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC5D,CAAC;IACD,OAAO,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IACzD,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,YAAY,GAAG,CAAC,KAAK,IAA4B,EAAE;QACvD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;YAC9B,IAAI;YACJ,OAAO;YACP,eAAe;YACf,MAAM;YACN,cAAc;YACd,eAAe;YACf,QAAQ;SACT,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC;QACzE,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAEpE,MAAM,IAAI,GAAG,cAAc,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC;QAC3B,uFAAuF;QACvF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;QAE/F,OAAO;YACL,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAChF,KAAK,EAAE,WAAW,CAAC,KAAK,IAAI,CAAC;YAC7B,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,CAAC;YAC/B,GAAG;YACH,UAAU,EAAE,WAAW,CAAC,UAAU,IAAI,SAAS;YAC/C,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC;YAC9D,KAAK;SACN,CAAC;IACJ,CAAC,CAAC,EAAE,CAAC;IAEL,kBAAkB,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE;QACtB,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,YAAY,EAAE,CAAC;YACtD,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IACzD,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,YAAY,GAAG,CAAC,KAAK,IAA4B,EAAE;QACvD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;YAC9B,IAAI;YACJ,OAAO;YACP,eAAe;YACf,MAAM;YACN,cAAc;YACd,eAAe;YACf,QAAQ;SACT,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC;QACzE,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAEpE,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAExF,OAAO;YACL,eAAe;YACf,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK;YAC/E,QAAQ,EAAE,WAAW,CAAC,QAAQ,IAAI,CAAC;YACnC,UAAU,EAAE,WAAW,CAAC,UAAU,IAAI,SAAS;YAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;SAC/E,CAAC;IACJ,CAAC,CAAC,EAAE,CAAC;IAEL,kBAAkB,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE;QACtB,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,YAAY,EAAE,CAAC;YACtD,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,YAAY,CAAC;AACtB,CAAC;AASD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,QAAgB;IAC7D,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,OAAO,GAAG,gCAAgC,CAAC,QAAQ,CAAC,CAAC;IAC3D,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;QACjB,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,OAAO,EAAE,CAAC;YAC5C,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,gCAAgC,CAAC,QAAgB;IAC9D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;QAC9B,IAAI;QACJ,OAAO;QACP,iBAAiB;QACjB,KAAK;QACL,aAAa;QACb,OAAO;QACP,eAAe;QACf,gBAAgB;QAChB,KAAK;QACL,SAAS;QACT,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM;SACtB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAErC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,kBAAkB,EAAE,CAAC;YACrB,kBAAkB,EAAE,CAAC;YACrB,aAAa,EAAE,UAAU,CAAC,MAAM;YAChC,aAAa,EAAE,KAAK;SACrB,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,aAAa,IAAI,QAAQ,CAAC;QAC1B,IAAI,QAAQ,GAAG,WAAW;YAAE,WAAW,GAAG,QAAQ,CAAC;IACrD,CAAC;IAED,MAAM,WAAW,GAAG,aAAa,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC5D,OAAO;QACL,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG;QACvD,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG;QACvD,aAAa,EAAE,UAAU,CAAC,MAAM;QAChC,aAAa,EAAE,WAAW,GAAG,CAAC;KAC/B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ffprobe.js","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * HDR Color Space Utilities
3
+ *
4
+ * Centralized HDR detection, transfer type handling, and FFmpeg color
5
+ * parameter generation for the HDR rendering pipeline.
6
+ */
7
+ import type { VideoColorSpace } from "./ffprobe.js";
8
+ export type HdrTransfer = "hlg" | "pq";
9
+ /**
10
+ * Check if a video's color space indicates HDR content.
11
+ * Re-exported from videoFrameExtractor for backward compatibility.
12
+ */
13
+ export declare function isHdrColorSpace(cs: VideoColorSpace | null): boolean;
14
+ /**
15
+ * Determine the HDR transfer function from a video's color space metadata.
16
+ *
17
+ * IMPORTANT: Callers must gate on `isHdrColorSpace(cs)` first. This function
18
+ * assumes the input has already been classified as HDR and defaults ambiguous
19
+ * inputs to "hlg" — calling it with an SDR color space silently returns "hlg",
20
+ * which is wrong for SDR.
21
+ *
22
+ * Returns "pq" for SMPTE 2084, "hlg" for ARIB STD-B67, defaults to "hlg".
23
+ */
24
+ export declare function detectTransfer(cs: VideoColorSpace | null): HdrTransfer;
25
+ /**
26
+ * HDR static metadata for the encoded stream.
27
+ *
28
+ * `masterDisplay` is the SMPTE ST 2086 mastering-display color volume string
29
+ * accepted by x265 (`G(Gx,Gy)B(Bx,By)R(Rx,Ry)WP(WPx,WPy)L(Lmax,Lmin)`).
30
+ * Chromaticity values are scaled by 50000 (0.00002 cd/m² per unit) and
31
+ * luminance values by 10000 (0.0001 cd/m² per unit).
32
+ *
33
+ * `maxCll` is the CTA-861.3 Content Light Level pair `MaxCLL,MaxFALL` in
34
+ * cd/m². Without these SEI messages, downstream players (Apple QuickTime,
35
+ * YouTube, HDR TVs) treat the stream as SDR BT.2020 and tone-map incorrectly
36
+ * — see packages/producer/scripts/hdr-smoke.ts for the regression assertion.
37
+ */
38
+ export interface HdrMasteringMetadata {
39
+ masterDisplay: string;
40
+ maxCll: string;
41
+ }
42
+ /**
43
+ * Default HDR10 mastering metadata: P3-D65 primaries inside a BT.2020
44
+ * container, mastered for 0.0001–1000 cd/m² with MaxCLL=1000, MaxFALL=400.
45
+ *
46
+ * These are conservative defaults that match how most HDR10 grading suites
47
+ * (Premiere, DaVinci Resolve) tag content when per-frame measured values
48
+ * aren't available. A future PR can plumb measured MaxCLL through `--hdr-opt`.
49
+ */
50
+ export declare const DEFAULT_HDR10_MASTERING: HdrMasteringMetadata;
51
+ export interface HdrEncoderColorParams {
52
+ colorPrimaries: string;
53
+ colorTrc: string;
54
+ colorspace: string;
55
+ pixelFormat: string;
56
+ /**
57
+ * Full x265-params string including color tagging and HDR static metadata.
58
+ * Pass directly to `-x265-params` (concatenate with other options via `:`).
59
+ */
60
+ x265ColorParams: string;
61
+ /** The mastering metadata that was baked into `x265ColorParams`. */
62
+ mastering: HdrMasteringMetadata;
63
+ }
64
+ /**
65
+ * Get FFmpeg encoder color parameters for a given HDR transfer function.
66
+ *
67
+ * The returned `x265ColorParams` includes both color tagging
68
+ * (`colorprim`/`transfer`/`colormatrix`) and HDR static metadata
69
+ * (`master-display`/`max-cll`). Without the static metadata the encoded
70
+ * stream is rejected as SDR by most HDR-aware players and CDNs.
71
+ */
72
+ export declare function getHdrEncoderColorParams(transfer: HdrTransfer, mastering?: HdrMasteringMetadata): HdrEncoderColorParams;
73
+ export interface CompositionHdrInfo {
74
+ hasHdr: boolean;
75
+ dominantTransfer: HdrTransfer | null;
76
+ }
77
+ /**
78
+ * Analyze a set of video color spaces to determine if the composition
79
+ * contains HDR content and what the dominant transfer function is.
80
+ */
81
+ export declare function analyzeCompositionHdr(colorSpaces: Array<VideoColorSpace | null>): CompositionHdrInfo;
82
+ //# sourceMappingURL=hdr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hdr.d.ts","sourceRoot":"","sources":["../../src/utils/hdr.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,IAAI,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAQnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,GAAG,WAAW,CAGtE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,EAAE,oBAGrC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,WAAW,EACrB,SAAS,GAAE,oBAA8C,GACxD,qBAAqB,CAYvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GACzC,kBAAkB,CAepB"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * HDR Color Space Utilities
3
+ *
4
+ * Centralized HDR detection, transfer type handling, and FFmpeg color
5
+ * parameter generation for the HDR rendering pipeline.
6
+ */
7
+ /**
8
+ * Check if a video's color space indicates HDR content.
9
+ * Re-exported from videoFrameExtractor for backward compatibility.
10
+ */
11
+ export function isHdrColorSpace(cs) {
12
+ if (!cs)
13
+ return false;
14
+ return (cs.colorPrimaries.includes("bt2020") ||
15
+ cs.colorSpace.includes("bt2020") ||
16
+ cs.colorTransfer === "smpte2084" ||
17
+ cs.colorTransfer === "arib-std-b67");
18
+ }
19
+ /**
20
+ * Determine the HDR transfer function from a video's color space metadata.
21
+ *
22
+ * IMPORTANT: Callers must gate on `isHdrColorSpace(cs)` first. This function
23
+ * assumes the input has already been classified as HDR and defaults ambiguous
24
+ * inputs to "hlg" — calling it with an SDR color space silently returns "hlg",
25
+ * which is wrong for SDR.
26
+ *
27
+ * Returns "pq" for SMPTE 2084, "hlg" for ARIB STD-B67, defaults to "hlg".
28
+ */
29
+ export function detectTransfer(cs) {
30
+ if (cs?.colorTransfer === "smpte2084")
31
+ return "pq";
32
+ return "hlg";
33
+ }
34
+ /**
35
+ * Default HDR10 mastering metadata: P3-D65 primaries inside a BT.2020
36
+ * container, mastered for 0.0001–1000 cd/m² with MaxCLL=1000, MaxFALL=400.
37
+ *
38
+ * These are conservative defaults that match how most HDR10 grading suites
39
+ * (Premiere, DaVinci Resolve) tag content when per-frame measured values
40
+ * aren't available. A future PR can plumb measured MaxCLL through `--hdr-opt`.
41
+ */
42
+ export const DEFAULT_HDR10_MASTERING = {
43
+ masterDisplay: "G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1)",
44
+ maxCll: "1000,400",
45
+ };
46
+ /**
47
+ * Get FFmpeg encoder color parameters for a given HDR transfer function.
48
+ *
49
+ * The returned `x265ColorParams` includes both color tagging
50
+ * (`colorprim`/`transfer`/`colormatrix`) and HDR static metadata
51
+ * (`master-display`/`max-cll`). Without the static metadata the encoded
52
+ * stream is rejected as SDR by most HDR-aware players and CDNs.
53
+ */
54
+ export function getHdrEncoderColorParams(transfer, mastering = DEFAULT_HDR10_MASTERING) {
55
+ const colorTrc = transfer === "pq" ? "smpte2084" : "arib-std-b67";
56
+ const tagging = `colorprim=bt2020:transfer=${colorTrc}:colormatrix=bt2020nc`;
57
+ const metadata = `master-display=${mastering.masterDisplay}:max-cll=${mastering.maxCll}`;
58
+ return {
59
+ colorPrimaries: "bt2020",
60
+ colorTrc,
61
+ colorspace: "bt2020nc",
62
+ pixelFormat: "yuv420p10le",
63
+ x265ColorParams: `${tagging}:${metadata}`,
64
+ mastering,
65
+ };
66
+ }
67
+ /**
68
+ * Analyze a set of video color spaces to determine if the composition
69
+ * contains HDR content and what the dominant transfer function is.
70
+ */
71
+ export function analyzeCompositionHdr(colorSpaces) {
72
+ let hasPq = false;
73
+ let hasHdr = false;
74
+ for (const cs of colorSpaces) {
75
+ if (!isHdrColorSpace(cs))
76
+ continue;
77
+ hasHdr = true;
78
+ if (cs?.colorTransfer === "smpte2084")
79
+ hasPq = true;
80
+ }
81
+ if (!hasHdr)
82
+ return { hasHdr: false, dominantTransfer: null };
83
+ // PQ takes priority — it's the more common HDR10 format
84
+ const dominantTransfer = hasPq ? "pq" : "hlg";
85
+ return { hasHdr: true, dominantTransfer };
86
+ }
87
+ //# sourceMappingURL=hdr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hdr.js","sourceRoot":"","sources":["../../src/utils/hdr.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,EAA0B;IACxD,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,OAAO,CACL,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACpC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAChC,EAAE,CAAC,aAAa,KAAK,WAAW;QAChC,EAAE,CAAC,aAAa,KAAK,cAAc,CACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,EAA0B;IACvD,IAAI,EAAE,EAAE,aAAa,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,KAAK,CAAC;AACf,CAAC;AAoBD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAyB;IAC3D,aAAa,EAAE,sEAAsE;IACrF,MAAM,EAAE,UAAU;CACnB,CAAC;AAgBF;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAqB,EACrB,YAAkC,uBAAuB;IAEzD,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC;IAClE,MAAM,OAAO,GAAG,6BAA6B,QAAQ,uBAAuB,CAAC;IAC7E,MAAM,QAAQ,GAAG,kBAAkB,SAAS,CAAC,aAAa,YAAY,SAAS,CAAC,MAAM,EAAE,CAAC;IACzF,OAAO;QACL,cAAc,EAAE,QAAQ;QACxB,QAAQ;QACR,UAAU,EAAE,UAAU;QACtB,WAAW,EAAE,aAAa;QAC1B,eAAe,EAAE,GAAG,OAAO,IAAI,QAAQ,EAAE;QACzC,SAAS;KACV,CAAC;AACJ,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,WAA0C;IAE1C,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC7B,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAAE,SAAS;QACnC,MAAM,GAAG,IAAI,CAAC;QACd,IAAI,EAAE,EAAE,aAAa,KAAK,WAAW;YAAE,KAAK,GAAG,IAAI,CAAC;IACtD,CAAC;IAED,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;IAE9D,wDAAwD;IACxD,MAAM,gBAAgB,GAAgB,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3D,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Layer Compositor — z-order analysis for multi-layer HDR compositing.
3
+ *
4
+ * Groups timed elements into z-ordered layers (DOM or HDR) for the
5
+ * per-frame compositing loop. Adjacent DOM elements merge into a single
6
+ * layer to minimize Chrome screenshots.
7
+ */
8
+ import type { ElementStackingInfo } from "../services/videoFrameInjector.js";
9
+ export type { ElementStackingInfo };
10
+ export type CompositeLayer = {
11
+ type: "dom";
12
+ elementIds: string[];
13
+ } | {
14
+ type: "hdr";
15
+ element: ElementStackingInfo;
16
+ };
17
+ /**
18
+ * Group z-sorted elements into composite layers. Adjacent DOM elements merge
19
+ * into a single layer; each HDR video/image is its own layer.
20
+ *
21
+ * Elements are sorted by \`zIndex\` ascending (back to front). Ties fall
22
+ * through to V8's stable sort, which preserves \`querySelectorAll\` DOM order —
23
+ * this is the same order Chrome uses for equal-z elements in a stacking
24
+ * context, so the blit order matches what the user sees in-browser.
25
+ *
26
+ * The DOM merge doesn't lose information: DOM layers are rendered via a
27
+ * full-page screenshot with non-layer elements hidden, so within-layer
28
+ * z-order is handled by Chrome itself.
29
+ *
30
+ * Invisible elements ARE included (video elements are hidden by the frame
31
+ * injector, but their injected \`<img>\` replacements are visible — they must
32
+ * stay in the correct z-ordered layer so sibling layers' DOM screenshots
33
+ * hide them).
34
+ */
35
+ export declare function groupIntoLayers(elements: ElementStackingInfo[]): CompositeLayer[];
36
+ //# sourceMappingURL=layerCompositor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layerCompositor.d.ts","sourceRoot":"","sources":["../../src/utils/layerCompositor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,cAAc,EAAE,CAuBjF"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Layer Compositor — z-order analysis for multi-layer HDR compositing.
3
+ *
4
+ * Groups timed elements into z-ordered layers (DOM or HDR) for the
5
+ * per-frame compositing loop. Adjacent DOM elements merge into a single
6
+ * layer to minimize Chrome screenshots.
7
+ */
8
+ /**
9
+ * Group z-sorted elements into composite layers. Adjacent DOM elements merge
10
+ * into a single layer; each HDR video/image is its own layer.
11
+ *
12
+ * Elements are sorted by \`zIndex\` ascending (back to front). Ties fall
13
+ * through to V8's stable sort, which preserves \`querySelectorAll\` DOM order —
14
+ * this is the same order Chrome uses for equal-z elements in a stacking
15
+ * context, so the blit order matches what the user sees in-browser.
16
+ *
17
+ * The DOM merge doesn't lose information: DOM layers are rendered via a
18
+ * full-page screenshot with non-layer elements hidden, so within-layer
19
+ * z-order is handled by Chrome itself.
20
+ *
21
+ * Invisible elements ARE included (video elements are hidden by the frame
22
+ * injector, but their injected \`<img>\` replacements are visible — they must
23
+ * stay in the correct z-ordered layer so sibling layers' DOM screenshots
24
+ * hide them).
25
+ */
26
+ export function groupIntoLayers(elements) {
27
+ // Include ALL elements regardless of visibility. Video elements are hidden by
28
+ // the frame injector (HEVC can't decode in headless Chrome) but their injected
29
+ // <img> replacements ARE visible. We need them in the correct z-ordered layer
30
+ // so they get hidden from other layers' DOM screenshots.
31
+ const sorted = [...elements].sort((a, b) => a.zIndex - b.zIndex);
32
+ const layers = [];
33
+ for (const el of sorted) {
34
+ if (el.isHdr) {
35
+ layers.push({ type: "hdr", element: el });
36
+ }
37
+ else {
38
+ const last = layers[layers.length - 1];
39
+ if (last && last.type === "dom") {
40
+ last.elementIds.push(el.id);
41
+ }
42
+ else {
43
+ layers.push({ type: "dom", elementIds: [el.id] });
44
+ }
45
+ }
46
+ }
47
+ return layers;
48
+ }
49
+ //# sourceMappingURL=layerCompositor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layerCompositor.js","sourceRoot":"","sources":["../../src/utils/layerCompositor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAAC,QAA+B;IAC7D,8EAA8E;IAC9E,+EAA+E;IAC/E,8EAA8E;IAC9E,yDAAyD;IACzD,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAqB,EAAE,CAAC;IAEpC,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvC,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/engine",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Seekable web page to video rendering engine (Puppeteer + FFmpeg)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,10 +19,11 @@
19
19
  "linkedom": "^0.18.12",
20
20
  "puppeteer": "^24.0.0",
21
21
  "puppeteer-core": "^24.39.1",
22
- "@hyperframes/core": "^0.4.5"
22
+ "@hyperframes/core": "^0.4.7"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.10.1",
26
+ "@webgpu/types": "^0.1.69",
26
27
  "typescript": "^5.7.2",
27
28
  "vitest": "^3.2.4"
28
29
  },
package/src/config.ts CHANGED
@@ -52,6 +52,12 @@ export interface EngineConfig {
52
52
  /** Timeout for FFmpeg streaming encode (ms). Default: 600_000 */
53
53
  ffmpegStreamingTimeout: number;
54
54
 
55
+ // ── HDR ──────────────────────────────────────────────────────────────
56
+ /** HDR output transfer function. false = SDR output (default). */
57
+ hdr: { transfer: "hlg" | "pq" } | false;
58
+ /** Auto-detect HDR from video sources when hdr is not explicitly set. */
59
+ hdrAutoDetect: boolean;
60
+
55
61
  // ── Media ────────────────────────────────────────────────────────────
56
62
  audioGain: number;
57
63
  frameDataUriCacheLimit: number;
@@ -96,6 +102,9 @@ export const DEFAULT_CONFIG: EngineConfig = {
96
102
  ffmpegProcessTimeout: 300_000,
97
103
  ffmpegStreamingTimeout: 600_000,
98
104
 
105
+ hdr: false,
106
+ hdrAutoDetect: true,
107
+
99
108
  audioGain: 1.35,
100
109
  frameDataUriCacheLimit: 256,
101
110
 
@@ -170,6 +179,13 @@ export function resolveConfig(overrides?: Partial<EngineConfig>): EngineConfig {
170
179
  DEFAULT_CONFIG.ffmpegStreamingTimeout,
171
180
  ),
172
181
 
182
+ hdr: (() => {
183
+ const raw = env("PRODUCER_HDR_TRANSFER");
184
+ if (raw === "hlg" || raw === "pq") return { transfer: raw };
185
+ return undefined;
186
+ })(),
187
+ hdrAutoDetect: envBool("PRODUCER_HDR_AUTO_DETECT", DEFAULT_CONFIG.hdrAutoDetect),
188
+
173
189
  audioGain: envNum("PRODUCER_AUDIO_GAIN", DEFAULT_CONFIG.audioGain),
174
190
  frameDataUriCacheLimit: Math.max(
175
191
  32,
package/src/index.ts CHANGED
@@ -77,6 +77,8 @@ export {
77
77
  injectVideoFramesBatch,
78
78
  syncVideoFrameVisibility,
79
79
  cdpSessionCache,
80
+ initTransparentBackground,
81
+ captureAlphaPng,
80
82
  type BeginFrameResult,
81
83
  } from "./services/screenshotService.js";
82
84
 
@@ -153,3 +155,43 @@ export {
153
155
  } from "./utils/ffprobe.js";
154
156
 
155
157
  export { downloadToTemp, isHttpUrl } from "./utils/urlDownloader.js";
158
+
159
+ export {
160
+ decodePng,
161
+ decodePngToRgb48le,
162
+ blitRgba8OverRgb48le,
163
+ blitRgb48leRegion,
164
+ getSrgbToHdrLut,
165
+ } from "./utils/alphaBlit.js";
166
+
167
+ export { groupIntoLayers, type CompositeLayer } from "./utils/layerCompositor.js";
168
+
169
+ export {
170
+ initHdrReadback,
171
+ uploadAndReadbackHdrFrame,
172
+ float16ToPqRgb,
173
+ buildHdrChromeArgs,
174
+ launchHdrBrowser,
175
+ } from "./services/hdrCapture.js";
176
+
177
+ export { captureScreenshotWithAlpha } from "./services/screenshotService.js";
178
+
179
+ export {
180
+ hideVideoElements,
181
+ showVideoElements,
182
+ queryVideoElementBounds,
183
+ queryElementStacking,
184
+ type VideoElementBounds,
185
+ type ElementStackingInfo,
186
+ } from "./services/videoFrameInjector.js";
187
+
188
+ export {
189
+ isHdrColorSpace,
190
+ detectTransfer,
191
+ getHdrEncoderColorParams,
192
+ analyzeCompositionHdr,
193
+ type HdrTransfer,
194
+ type HdrEncoderColorParams,
195
+ type CompositionHdrInfo,
196
+ } from "./utils/hdr.js";
197
+ export type { VideoColorSpace } from "./utils/ffprobe.js";
@@ -244,8 +244,10 @@ export function buildChromeArgs(
244
244
  options: BuildChromeArgsOptions,
245
245
  config?: Partial<Pick<EngineConfig, "disableGpu" | "chromePath">>,
246
246
  ): string[] {
247
- // Chrome flags tuned for headless rendering performance.
248
- // Based on Remotion's open-browser.ts flags with additions for our use case.
247
+ // Chrome flags tuned for headless rendering performance. The set below is a
248
+ // fairly standard "headless-for-capture" configuration similar profiles
249
+ // appear in Puppeteer's defaults, Playwright, Remotion, and Chrome's own
250
+ // headless-shell guidance.
249
251
  const chromeArgs = [
250
252
  "--no-sandbox",
251
253
  "--disable-setuid-sandbox",
@@ -257,7 +259,8 @@ export function buildChromeArgs(
257
259
  "--font-render-hinting=none",
258
260
  "--force-color-profile=srgb",
259
261
  `--window-size=${options.width},${options.height}`,
260
- // Remotion perf flags — prevent Chrome from throttling background tabs/timers
262
+ // Prevent Chrome from throttling background tabs/timers — critical when the
263
+ // page is offscreen during headless capture
261
264
  "--disable-background-timer-throttling",
262
265
  "--disable-backgrounding-occluded-windows",
263
266
  "--disable-renderer-backgrounding",
@@ -247,3 +247,91 @@ describe("buildEncoderArgs color space", () => {
247
247
  expect(args).not.toContain("-video_track_timescale");
248
248
  });
249
249
  });
250
+
251
+ describe("getEncoderPreset HDR", () => {
252
+ it("returns h265 with 10-bit for HDR HLG", () => {
253
+ const preset = getEncoderPreset("standard", "mp4", { transfer: "hlg" });
254
+ expect(preset.codec).toBe("h265");
255
+ expect(preset.pixelFormat).toBe("yuv420p10le");
256
+ expect(preset.hdr).toEqual({ transfer: "hlg" });
257
+ });
258
+
259
+ it("returns h265 with 10-bit for HDR PQ", () => {
260
+ const preset = getEncoderPreset("high", "mp4", { transfer: "pq" });
261
+ expect(preset.codec).toBe("h265");
262
+ expect(preset.pixelFormat).toBe("yuv420p10le");
263
+ expect(preset.hdr).toEqual({ transfer: "pq" });
264
+ });
265
+
266
+ it("avoids ultrafast preset for HDR (upgrades to fast)", () => {
267
+ const preset = getEncoderPreset("draft", "mp4", { transfer: "hlg" });
268
+ expect(preset.preset).toBe("fast");
269
+ });
270
+
271
+ it("ignores HDR for webm format", () => {
272
+ const preset = getEncoderPreset("standard", "webm", { transfer: "hlg" });
273
+ expect(preset.codec).toBe("vp9");
274
+ expect(preset.hdr).toBeUndefined();
275
+ });
276
+
277
+ it("ignores HDR for mov format", () => {
278
+ const preset = getEncoderPreset("standard", "mov", { transfer: "pq" });
279
+ expect(preset.codec).toBe("prores");
280
+ expect(preset.hdr).toBeUndefined();
281
+ });
282
+ });
283
+
284
+ describe("buildEncoderArgs HDR color space", () => {
285
+ const baseOptions = { fps: 30, width: 1920, height: 1080 };
286
+ const inputArgs = ["-framerate", "30", "-i", "frames/%04d.png"];
287
+
288
+ it("keeps bt709 color tags when HDR flag is set but frames are still Chrome sRGB captures", () => {
289
+ // HDR flag gives H.265 + 10-bit encoding but pixels are still sRGB/bt709.
290
+ // Tagging as bt2020 causes orange shift — so we tag truthfully as bt709.
291
+ const args = buildEncoderArgs(
292
+ { ...baseOptions, codec: "h265", preset: "medium", quality: 23, hdr: { transfer: "hlg" } },
293
+ inputArgs,
294
+ "out.mp4",
295
+ );
296
+ expect(args[args.indexOf("-colorspace:v") + 1]).toBe("bt709");
297
+ expect(args[args.indexOf("-color_primaries:v") + 1]).toBe("bt709");
298
+ expect(args[args.indexOf("-color_trc:v") + 1]).toBe("bt709");
299
+ const paramIdx = args.indexOf("-x265-params");
300
+ expect(args[paramIdx + 1]).toContain("colorprim=bt709");
301
+ expect(args[paramIdx + 1]).toContain("transfer=bt709");
302
+ });
303
+
304
+ it("uses bt709 when HDR is not set", () => {
305
+ const args = buildEncoderArgs(
306
+ { ...baseOptions, codec: "h265", preset: "medium", quality: 23 },
307
+ inputArgs,
308
+ "out.mp4",
309
+ );
310
+ expect(args[args.indexOf("-colorspace:v") + 1]).toBe("bt709");
311
+ expect(args[args.indexOf("-color_trc:v") + 1]).toBe("bt709");
312
+ });
313
+
314
+ it("uses range conversion (not colorspace) for HDR CPU encoding", () => {
315
+ // Chrome screenshots are sRGB — we don't convert primaries (causes color shifts).
316
+ // Just range-convert and let the bt2020 container metadata + 10-bit handle the rest.
317
+ const args = buildEncoderArgs(
318
+ { ...baseOptions, codec: "h265", preset: "medium", quality: 23, hdr: { transfer: "hlg" } },
319
+ inputArgs,
320
+ "out.mp4",
321
+ );
322
+ const vfIdx = args.indexOf("-vf");
323
+ expect(vfIdx).toBeGreaterThan(-1);
324
+ expect(args[vfIdx + 1]).toContain("scale=in_range=pc:out_range=tv");
325
+ expect(args[vfIdx + 1]).not.toContain("colorspace");
326
+ });
327
+
328
+ it("uses same range conversion for SDR CPU encoding", () => {
329
+ const args = buildEncoderArgs(
330
+ { ...baseOptions, codec: "h264", preset: "medium", quality: 23 },
331
+ inputArgs,
332
+ "out.mp4",
333
+ );
334
+ const vfIdx = args.indexOf("-vf");
335
+ expect(args[vfIdx + 1]).toContain("scale=in_range=pc:out_range=tv");
336
+ });
337
+ });
@@ -10,6 +10,7 @@ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSy
10
10
  import { join, dirname } from "path";
11
11
  import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
12
12
  import { type GpuEncoder, getCachedGpuEncoder, getGpuEncoderName } from "../utils/gpuEncoder.js";
13
+ import { type HdrTransfer } from "../utils/hdr.js";
13
14
  import { runFfmpeg } from "../utils/runFfmpeg.js";
14
15
  import type { EncoderOptions, EncodeResult, MuxResult } from "./chunkEncoder.types.js";
15
16
 
@@ -21,15 +22,24 @@ export const ENCODER_PRESETS = {
21
22
  high: { preset: "slow", quality: 15, codec: "h264" as const },
22
23
  };
23
24
 
25
+ export interface EncoderPreset {
26
+ preset: string;
27
+ quality: number;
28
+ codec: "h264" | "h265" | "vp9" | "prores";
29
+ pixelFormat: string;
30
+ hdr?: { transfer: HdrTransfer };
31
+ }
32
+
24
33
  /**
25
34
  * Get encoder preset for a given quality and output format.
26
- * WebM uses VP9 with alpha-capable pixel format; MP4 uses h264;
35
+ * WebM uses VP9 with alpha-capable pixel format; MP4 uses h264 (or h265 for HDR);
27
36
  * MOV uses ProRes 4444 with alpha for editor-compatible transparency.
28
37
  */
29
38
  export function getEncoderPreset(
30
39
  quality: "draft" | "standard" | "high",
31
40
  format: "mp4" | "webm" | "mov" = "mp4",
32
- ): { preset: string; quality: number; codec: "h264" | "vp9" | "prores"; pixelFormat: string } {
41
+ hdr?: { transfer: HdrTransfer },
42
+ ): EncoderPreset {
33
43
  const base = ENCODER_PRESETS[quality];
34
44
  if (format === "webm") {
35
45
  return {
@@ -47,6 +57,15 @@ export function getEncoderPreset(
47
57
  pixelFormat: "yuva444p10le",
48
58
  };
49
59
  }
60
+ if (hdr) {
61
+ return {
62
+ preset: base.preset === "ultrafast" ? "fast" : base.preset,
63
+ quality: base.quality,
64
+ codec: "h265",
65
+ pixelFormat: "yuv420p10le",
66
+ hdr,
67
+ };
68
+ }
50
69
  return { ...base, pixelFormat: "yuv420p" };
51
70
  }
52
71
 
@@ -109,9 +128,8 @@ export function buildEncoderArgs(
109
128
  if (bitrate) args.push("-b:v", bitrate);
110
129
  else args.push("-crf", String(quality));
111
130
 
112
- // Encoder-specific params: anti-banding + bt709 color space.
131
+ // Encoder-specific params: anti-banding + color space tagging.
113
132
  // aq-mode=3 redistributes bits to dark flat areas (gradients).
114
- // colorprim/transfer/colormatrix embed bt709 in the H.264/H.265 VUI.
115
133
  const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
116
134
  const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
117
135
  if (preset === "ultrafast") {
@@ -120,6 +138,10 @@ export function buildEncoderArgs(
120
138
  args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
121
139
  }
122
140
  }
141
+ // Apple devices require hvc1 tag for HEVC playback (default hev1 won't open in QuickTime)
142
+ if (codec === "h265") {
143
+ args.push("-tag:v", "hvc1");
144
+ }
123
145
  } else if (codec === "vp9") {
124
146
  args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
125
147
  args.push("-deadline", preset === "ultrafast" ? "realtime" : "good");
@@ -134,8 +156,12 @@ export function buildEncoderArgs(
134
156
  return [...args, "-y", outputPath];
135
157
  }
136
158
 
137
- // BT.709 color space metadata — Chrome screenshots are sRGB which maps to bt709.
138
- // Tags the output so players interpret colors correctly across devices.
159
+ // Color space metadata — tags the output so players interpret colors correctly.
160
+ // Chrome screenshots are always sRGB/bt709 pixels regardless of --hdr flag.
161
+ // We tag truthfully as bt709 even for HDR output — the --hdr flag gives
162
+ // H.265 + 10-bit encoding (better quality/compression) without lying about
163
+ // the color space. Tagging as bt2020 when pixels are bt709 causes browsers
164
+ // to apply the wrong color transform, producing visible orange/warm shifts.
139
165
  if (codec === "h264" || codec === "h265") {
140
166
  args.push(
141
167
  "-colorspace:v",
@@ -148,15 +174,15 @@ export function buildEncoderArgs(
148
174
  "tv",
149
175
  );
150
176
 
151
- // Convert full-range RGB input (Chrome screenshots) to limited/TV range for H.264.
152
- // VAAPI already has a -vf chain for hwupload; prepend range conversion to it.
177
+ // Range conversion: Chrome's full-range RGB limited/TV range.
153
178
  if (gpuEncoder === "vaapi") {
154
- // Replace the existing VAAPI -vf with one that includes range conversion
155
179
  const vfIdx = args.indexOf("-vf");
156
180
  if (vfIdx !== -1) {
157
181
  args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
158
182
  }
159
183
  } else if (!shouldUseGpu) {
184
+ // Range conversion: Chrome screenshots are full-range RGB.
185
+ // The scale filter handles both 8-bit and 10-bit correctly.
160
186
  args.push("-vf", "scale=in_range=pc:out_range=tv");
161
187
  }
162
188
 
@@ -1,3 +1,5 @@
1
+ import type { HdrTransfer } from "../utils/hdr.js";
2
+
1
3
  export interface EncoderOptions {
2
4
  fps: number;
3
5
  width: number;
@@ -8,6 +10,7 @@ export interface EncoderOptions {
8
10
  bitrate?: string;
9
11
  pixelFormat?: string;
10
12
  useGpu?: boolean;
13
+ hdr?: { transfer: HdrTransfer };
11
14
  }
12
15
 
13
16
  export interface EncodeResult {