@smoove/renderer 0.1.1

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/audio-mix.d.ts +14 -0
  4. package/dist/audio-mix.d.ts.map +1 -0
  5. package/dist/audio-mix.js +127 -0
  6. package/dist/audio-mix.js.map +1 -0
  7. package/dist/audio-source-null.d.ts +25 -0
  8. package/dist/audio-source-null.d.ts.map +1 -0
  9. package/dist/audio-source-null.js +26 -0
  10. package/dist/audio-source-null.js.map +1 -0
  11. package/dist/audio-track.d.ts +17 -0
  12. package/dist/audio-track.d.ts.map +1 -0
  13. package/dist/audio-track.js +91 -0
  14. package/dist/audio-track.js.map +1 -0
  15. package/dist/encode.d.ts +45 -0
  16. package/dist/encode.d.ts.map +1 -0
  17. package/dist/encode.js +105 -0
  18. package/dist/encode.js.map +1 -0
  19. package/dist/font-loader.d.ts +11 -0
  20. package/dist/font-loader.d.ts.map +1 -0
  21. package/dist/font-loader.js +52 -0
  22. package/dist/font-loader.js.map +1 -0
  23. package/dist/gl-node.d.ts +10 -0
  24. package/dist/gl-node.d.ts.map +1 -0
  25. package/dist/gl-node.js +61 -0
  26. package/dist/gl-node.js.map +1 -0
  27. package/dist/gl.d.ts +12 -0
  28. package/dist/gl.d.ts.map +1 -0
  29. package/dist/gl.js +28 -0
  30. package/dist/gl.js.map +1 -0
  31. package/dist/image-loader.d.ts +9 -0
  32. package/dist/image-loader.d.ts.map +1 -0
  33. package/dist/image-loader.js +20 -0
  34. package/dist/image-loader.js.map +1 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +16 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/media-input-source.d.ts +8 -0
  40. package/dist/media-input-source.d.ts.map +1 -0
  41. package/dist/media-input-source.js +22 -0
  42. package/dist/media-input-source.js.map +1 -0
  43. package/dist/media-server.d.ts +7 -0
  44. package/dist/media-server.d.ts.map +1 -0
  45. package/dist/media-server.js +14 -0
  46. package/dist/media-server.js.map +1 -0
  47. package/dist/probe.d.ts +5 -0
  48. package/dist/probe.d.ts.map +1 -0
  49. package/dist/probe.js +13 -0
  50. package/dist/probe.js.map +1 -0
  51. package/dist/register.d.ts +2 -0
  52. package/dist/register.d.ts.map +1 -0
  53. package/dist/register.js +6 -0
  54. package/dist/register.js.map +1 -0
  55. package/dist/render.d.ts +18 -0
  56. package/dist/render.d.ts.map +1 -0
  57. package/dist/render.js +190 -0
  58. package/dist/render.js.map +1 -0
  59. package/dist/setup.d.ts +12 -0
  60. package/dist/setup.d.ts.map +1 -0
  61. package/dist/setup.js +46 -0
  62. package/dist/setup.js.map +1 -0
  63. package/dist/skia.d.ts +8 -0
  64. package/dist/skia.d.ts.map +1 -0
  65. package/dist/skia.js +15 -0
  66. package/dist/skia.js.map +1 -0
  67. package/dist/types.d.ts +153 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +8 -0
  70. package/dist/types.js.map +1 -0
  71. package/dist/video-source-mediabunny.d.ts +63 -0
  72. package/dist/video-source-mediabunny.d.ts.map +1 -0
  73. package/dist/video-source-mediabunny.js +239 -0
  74. package/dist/video-source-mediabunny.js.map +1 -0
  75. package/package.json +67 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font-loader.js","sourceRoot":"","sources":["../src/font-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,gEAAgE;AAChE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC;AAE1E,gFAAgF;AAChF,+EAA+E;AAC/E,4DAA4D;AAC5D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAErC,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,kBAAkB,CAAC,GAAW,EAAE,QAAgB;IAC7D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IAC5F,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC;IAC7E,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACjD,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,sBAAsB;IAC1E,OAAO,KAAK,EAAE,MAAc,EAAE,IAAwB,EAAiB,EAAE;QACvE,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;QAClE,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QAChC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpB,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type GlPlatform } from "@smoove/transitions";
2
+ /**
3
+ * A {@link GlPlatform} backed by `headless-gl` (off-DOM WebGL1) and skia-canvas.
4
+ * Scenes are uploaded as raw RGBA via `texImage2D`, the GLSL ES 3.00 fragments
5
+ * are transpiled to 1.00, and the drawn frame is read back with `readPixels`
6
+ * (flipped from GL's bottom-up framebuffer) into a skia `Canvas` that Konva can
7
+ * draw. Returns `null` if `headless-gl` isn't installed or can't get a context.
8
+ */
9
+ export declare function createNodeGlPlatform(): GlPlatform | null;
10
+ //# sourceMappingURL=gl-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gl-node.d.ts","sourceRoot":"","sources":["../src/gl-node.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAqC,MAAM,qBAAqB,CAAC;AAczF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,GAAG,IAAI,CAmDxD"}
@@ -0,0 +1,61 @@
1
+ import { createRequire } from "node:module";
2
+ import { transpileTo100, VERTEX_SHADER_100 } from "@smoove/transitions";
3
+ import { Canvas } from "skia-canvas";
4
+ const require = createRequire(import.meta.url);
5
+ /**
6
+ * A {@link GlPlatform} backed by `headless-gl` (off-DOM WebGL1) and skia-canvas.
7
+ * Scenes are uploaded as raw RGBA via `texImage2D`, the GLSL ES 3.00 fragments
8
+ * are transpiled to 1.00, and the drawn frame is read back with `readPixels`
9
+ * (flipped from GL's bottom-up framebuffer) into a skia `Canvas` that Konva can
10
+ * draw. Returns `null` if `headless-gl` isn't installed or can't get a context.
11
+ */
12
+ export function createNodeGlPlatform() {
13
+ let createGl;
14
+ try {
15
+ createGl = require("gl");
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ const gl = createGl(1, 1, { preserveDrawingBuffer: true, premultipliedAlpha: true });
21
+ if (!gl)
22
+ return null;
23
+ const resizeExt = gl.getExtension("STACKGL_resize_drawingbuffer");
24
+ let bufWidth = 1;
25
+ let bufHeight = 1;
26
+ let pixels = new Uint8Array(4);
27
+ let out = new Canvas(1, 1);
28
+ return {
29
+ gl,
30
+ vertexShader: VERTEX_SHADER_100,
31
+ prepareFragment: transpileTo100,
32
+ resize(width, height) {
33
+ if (width === bufWidth && height === bufHeight)
34
+ return;
35
+ resizeExt?.resize(width, height);
36
+ bufWidth = width;
37
+ bufHeight = height;
38
+ pixels = new Uint8Array(width * height * 4);
39
+ out = new Canvas(width, height);
40
+ },
41
+ uploadScene(source, width, height) {
42
+ // `source` is a skia Canvas at render time; pull straight-alpha RGBA bytes.
43
+ const ctx = source.getContext("2d");
44
+ const data = ctx.getImageData(0, 0, width, height).data;
45
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
46
+ },
47
+ result(width, height) {
48
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
49
+ const ctx = out.getContext("2d");
50
+ const img = ctx.createImageData(width, height);
51
+ const row = width * 4;
52
+ // GL framebuffer is bottom-up; flip rows into a top-down image.
53
+ for (let y = 0; y < height; y++) {
54
+ img.data.set(pixels.subarray((height - 1 - y) * row, (height - y) * row), y * row);
55
+ }
56
+ ctx.putImageData(img, 0, 0);
57
+ return out;
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=gl-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gl-node.js","sourceRoot":"","sources":["../src/gl-node.ts"],"names":[],"mappings":"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"}
package/dist/gl.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Route `@smoove/transitions` Tier B (shader) transitions through a
3
+ * headless-gl + skia compositor so they render in Node instead of falling back
4
+ * to `fade()`. Idempotent. The GL context is created lazily on first use; if the
5
+ * optional `gl` (headless-gl) package isn't installed the compositor resolves to
6
+ * `null`, transitions still fall back to fade, and a one-time hint is logged.
7
+ *
8
+ * Call before building any `TransitionSeries`, or simply
9
+ * `import "@smoove/renderer/gl"` (which calls this on load).
10
+ */
11
+ export declare function enableNodeShaderTransitions(): void;
12
+ //# sourceMappingURL=gl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gl.d.ts","sourceRoot":"","sources":["../src/gl.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAclD"}
package/dist/gl.js ADDED
@@ -0,0 +1,28 @@
1
+ import { GlCompositor, setCompositorFactory } from "@smoove/transitions";
2
+ import { createNodeGlPlatform } from "./gl-node.js";
3
+ let warned = false;
4
+ /**
5
+ * Route `@smoove/transitions` Tier B (shader) transitions through a
6
+ * headless-gl + skia compositor so they render in Node instead of falling back
7
+ * to `fade()`. Idempotent. The GL context is created lazily on first use; if the
8
+ * optional `gl` (headless-gl) package isn't installed the compositor resolves to
9
+ * `null`, transitions still fall back to fade, and a one-time hint is logged.
10
+ *
11
+ * Call before building any `TransitionSeries`, or simply
12
+ * `import "@smoove/renderer/gl"` (which calls this on load).
13
+ */
14
+ export function enableNodeShaderTransitions() {
15
+ setCompositorFactory(() => {
16
+ const platform = createNodeGlPlatform();
17
+ if (!platform) {
18
+ if (!warned) {
19
+ warned = true;
20
+ console.warn("@smoove/renderer: shader transitions need the optional `gl` (headless-gl) package — install it to render them, otherwise they fall back to fade().");
21
+ }
22
+ return null;
23
+ }
24
+ return GlCompositor.fromPlatform(platform);
25
+ });
26
+ }
27
+ enableNodeShaderTransitions();
28
+ //# sourceMappingURL=gl.js.map
package/dist/gl.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gl.js","sourceRoot":"","sources":["../src/gl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B;IACzC,oBAAoB,CAAC,GAAG,EAAE;QACxB,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,IAAI,CAAC;gBACd,OAAO,CAAC,IAAI,CACV,oJAAoJ,CACrJ,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,YAAY,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2BAA2B,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { LoadedImage } from "@smoove/core";
2
+ /**
3
+ * Server image loader backed by skia-canvas. Returns a drawable usable by the
4
+ * konva skia backend. skia-canvas `Image` exposes `width`/`height` but not
5
+ * `naturalWidth`/`naturalHeight`, which `@smoove/core`'s `Image` layout
6
+ * reads — so we define them from the decoded dimensions.
7
+ */
8
+ export declare function loadImageNode(src: string): Promise<LoadedImage>;
9
+ //# sourceMappingURL=image-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../src/image-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAWrE"}
@@ -0,0 +1,20 @@
1
+ import { loadImage } from "skia-canvas";
2
+ /**
3
+ * Server image loader backed by skia-canvas. Returns a drawable usable by the
4
+ * konva skia backend. skia-canvas `Image` exposes `width`/`height` but not
5
+ * `naturalWidth`/`naturalHeight`, which `@smoove/core`'s `Image` layout
6
+ * reads — so we define them from the decoded dimensions.
7
+ */
8
+ export async function loadImageNode(src) {
9
+ const img = await loadImage(src);
10
+ Object.defineProperty(img, "naturalWidth", {
11
+ get: () => img.width,
12
+ configurable: true,
13
+ });
14
+ Object.defineProperty(img, "naturalHeight", {
15
+ get: () => img.height,
16
+ configurable: true,
17
+ });
18
+ return img;
19
+ }
20
+ //# sourceMappingURL=image-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-loader.js","sourceRoot":"","sources":["../src/image-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE;QACzC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK;QACpB,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE;QAC1C,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM;QACrB,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;IACH,OAAO,GAA6B,CAAC;AACvC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export { mixAudio } from "./audio-mix.js";
2
+ export { NullAudioSource, nullAudioSourceFactory } from "./audio-source-null.js";
3
+ export { collectAudioTrack } from "./audio-track.js";
4
+ export { DEFAULT_FONT_CACHE_DIR, makeSkiaFontLoader } from "./font-loader.js";
5
+ export { loadImageNode } from "./image-loader.js";
6
+ export { registerServerMedia } from "./media-server.js";
7
+ export { probeComposition } from "./probe.js";
8
+ export { renderComposition, renderFrames, renderStill, renderToStream, } from "./render.js";
9
+ export { registerFonts, setupServerRendering } from "./setup.js";
10
+ export { installSkiaBackend } from "./skia.js";
11
+ export { type AudioClip, type CompositionInfo, type Fit, type FontsOption, type FrameOptions, type FrameRange, QUALITY_PRESETS, type QualityConfig, type QualityPreset, type RenderedFrame, type RenderOptions, type RenderProgress, type RenderResult, type RenderToStreamResult, type Resolution, type SetupOptions, type StillOptions, type StreamOptions, type VolumeKeyframe, } from "./types.js";
12
+ export { MediabunnyVideoSource, nodeVideoSourceFactory, setVideoDecodeCap, } from "./video-source-mediabunny.js";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EACL,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // High-level render API
2
+ export { mixAudio } from "./audio-mix.js";
3
+ export { NullAudioSource, nullAudioSourceFactory } from "./audio-source-null.js";
4
+ export { collectAudioTrack } from "./audio-track.js";
5
+ export { DEFAULT_FONT_CACHE_DIR, makeSkiaFontLoader } from "./font-loader.js";
6
+ export { loadImageNode } from "./image-loader.js";
7
+ export { registerServerMedia } from "./media-server.js";
8
+ export { probeComposition } from "./probe.js";
9
+ export { renderComposition, renderFrames, renderStill, renderToStream, } from "./render.js";
10
+ // Wiring seams (call before building compositions)
11
+ export { registerFonts, setupServerRendering } from "./setup.js";
12
+ export { installSkiaBackend } from "./skia.js";
13
+ // Types
14
+ export { QUALITY_PRESETS, } from "./types.js";
15
+ export { MediabunnyVideoSource, nodeVideoSourceFactory, setVideoDecodeCap, } from "./video-source-mediabunny.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wBAAwB;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,mDAAmD;AACnD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,QAAQ;AACR,OAAO,EAOL,eAAe,GAahB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type Source } from "mediabunny";
2
+ /**
3
+ * Build a Mediabunny {@link Source} for a media reference. Server renders feed
4
+ * filesystem paths (and `file://` URLs); remote `http(s)` URLs are fetched via
5
+ * {@link UrlSource}. Anything else is treated as a local path.
6
+ */
7
+ export declare function makeInputSource(src: string): Source;
8
+ //# sourceMappingURL=media-input-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-input-source.d.ts","sourceRoot":"","sources":["../src/media-input-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAa,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAInD"}
@@ -0,0 +1,22 @@
1
+ import { FilePathSource, UrlSource } from "mediabunny";
2
+ /**
3
+ * Build a Mediabunny {@link Source} for a media reference. Server renders feed
4
+ * filesystem paths (and `file://` URLs); remote `http(s)` URLs are fetched via
5
+ * {@link UrlSource}. Anything else is treated as a local path.
6
+ */
7
+ export function makeInputSource(src) {
8
+ if (/^https?:\/\//i.test(src))
9
+ return new UrlSource(src);
10
+ if (src.startsWith("file://"))
11
+ return new FilePathSource(fileUrlToPath(src));
12
+ return new FilePathSource(src);
13
+ }
14
+ function fileUrlToPath(url) {
15
+ try {
16
+ return new URL(url).pathname;
17
+ }
18
+ catch {
19
+ return url;
20
+ }
21
+ }
22
+ //# sourceMappingURL=media-input-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-input-source.js","sourceRoot":"","sources":["../src/media-input-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAe,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Register Mediabunny's server backend (node-av → FFmpeg C API) so the same
3
+ * WebCodecs-based `Input`/`Output`/sink/source classes that run in the browser
4
+ * work in Node. Idempotent — `setupServerRendering()` calls it for you.
5
+ */
6
+ export declare function registerServerMedia(): void;
7
+ //# sourceMappingURL=media-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-server.d.ts","sourceRoot":"","sources":["../src/media-server.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C"}
@@ -0,0 +1,14 @@
1
+ import { registerMediabunnyServer } from "@mediabunny/server";
2
+ let registered = false;
3
+ /**
4
+ * Register Mediabunny's server backend (node-av → FFmpeg C API) so the same
5
+ * WebCodecs-based `Input`/`Output`/sink/source classes that run in the browser
6
+ * work in Node. Idempotent — `setupServerRendering()` calls it for you.
7
+ */
8
+ export function registerServerMedia() {
9
+ if (registered)
10
+ return;
11
+ registerMediabunnyServer();
12
+ registered = true;
13
+ }
14
+ //# sourceMappingURL=media-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-server.js","sourceRoot":"","sources":["../src/media-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IACjC,IAAI,UAAU;QAAE,OAAO;IACvB,wBAAwB,EAAE,CAAC;IAC3B,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Composition } from "@smoove/core";
2
+ import type { CompositionInfo } from "./types.js";
3
+ /** Read a composition's metadata without rendering anything. */
4
+ export declare function probeComposition(comp: Composition): CompositionInfo;
5
+ //# sourceMappingURL=probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAUnE"}
package/dist/probe.js ADDED
@@ -0,0 +1,13 @@
1
+ /** Read a composition's metadata without rendering anything. */
2
+ export function probeComposition(comp) {
3
+ const durationInFrames = comp.durationInFrames.get();
4
+ return {
5
+ fps: comp.fps,
6
+ durationInFrames,
7
+ width: comp.width(),
8
+ height: comp.height(),
9
+ durationInSeconds: durationInFrames / comp.fps,
10
+ isRendering: comp.environment.isRendering,
11
+ };
12
+ }
13
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAGA,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,CAAC;IACrD,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,gBAAgB;QAChB,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;QACnB,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACrB,iBAAiB,EAAE,gBAAgB,GAAG,IAAI,CAAC,GAAG;QAC9C,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;KAC1C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ // Side-effecting sugar: `import "@smoove/renderer/register"` ===
2
+ // calling setupServerRendering() at import time. Import this before constructing
3
+ // any Composition.
4
+ import { setupServerRendering } from "./setup.js";
5
+ setupServerRendering();
6
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,iFAAiF;AACjF,mBAAmB;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type Composition } from "@smoove/core";
2
+ import { type FrameOptions, type RenderedFrame, type RenderOptions, type RenderResult, type RenderToStreamResult, type StillOptions, type StreamOptions } from "./types.js";
3
+ /**
4
+ * The render primitive: yields raw RGBA frames at the composition's native size.
5
+ * Build custom encoders / GIFs / image sequences on top of this.
6
+ */
7
+ export declare function renderFrames(comp: Composition, opts?: FrameOptions): AsyncGenerator<RenderedFrame>;
8
+ /** Render frames + audio to a muxed video file. */
9
+ export declare function renderComposition(comp: Composition, opts: RenderOptions): Promise<RenderResult>;
10
+ /**
11
+ * Render to a fragmented mp4/webm exposed as a `Readable`, plus a `done` promise
12
+ * that resolves with the {@link RenderResult} (or rejects) so errors aren't lost.
13
+ * Mediabunny writes the fragmented container straight into the stream — no temp file.
14
+ */
15
+ export declare function renderToStream(comp: Composition, opts: StreamOptions): RenderToStreamResult;
16
+ /** Render a single frame to a PNG/JPEG buffer (optionally written to `output`). */
17
+ export declare function renderStill(comp: Composition, opts: StillOptions): Promise<Buffer>;
18
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAA4B,MAAM,cAAc,CAAC;AAY1E,OAAO,EAEL,KAAK,YAAY,EAIjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAEzB,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AA8CpB;;;GAGG;AACH,wBAAuB,YAAY,CACjC,IAAI,EAAE,WAAW,EACjB,IAAI,GAAE,YAAiB,GACtB,cAAc,CAAC,aAAa,CAAC,CAQ/B;AAwGD,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAE/F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,oBAAoB,CAK3F;AAeD,mFAAmF;AACnF,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAYxF"}
package/dist/render.js ADDED
@@ -0,0 +1,190 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { PassThrough } from "node:stream";
3
+ import { isAudioNode, isVideoNode } from "@smoove/core";
4
+ import Konva from "konva";
5
+ import { Canvas } from "skia-canvas";
6
+ import { mixAudio } from "./audio-mix.js";
7
+ import { collectAudioTrack } from "./audio-track.js";
8
+ import { AUDIO_CHANNELS, AUDIO_SAMPLE_RATE, MediabunnyEncoder, } from "./encode.js";
9
+ import { registerFonts } from "./setup.js";
10
+ import { QUALITY_PRESETS, } from "./types.js";
11
+ function assertRendering(comp) {
12
+ if (!comp.environment.isRendering) {
13
+ throw new Error('[smoove] Composition is not in rendering mode. Call setupServerRendering() (or import "@smoove/renderer/register") BEFORE constructing the Composition, or pass { mode: "rendering" }.');
14
+ }
15
+ }
16
+ function resolveQuality(q) {
17
+ if (!q)
18
+ return QUALITY_PRESETS.medium;
19
+ return typeof q === "string" ? QUALITY_PRESETS[q] : q;
20
+ }
21
+ /** Capture the current frame as a skia Canvas (already composited by core). */
22
+ function capture(comp) {
23
+ return comp.captureCanvas();
24
+ }
25
+ /** Does the composition contain any audio-bearing node? Decides the audio track up front. */
26
+ function hasAudioNodes(comp) {
27
+ for (const layer of comp.getChildren()) {
28
+ if (!(layer instanceof Konva.Layer))
29
+ continue;
30
+ const found = layer.find((n) => isAudioNode(n) || isVideoNode(n));
31
+ if (found.length > 0)
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+ async function* framesBetween(comp, from, to, signal) {
37
+ let index = 0;
38
+ for (let f = from; f <= to; f++) {
39
+ if (signal?.aborted)
40
+ throw new Error("[smoove] render aborted");
41
+ await comp.renderFrame(f);
42
+ const canvas = capture(comp);
43
+ const data = canvas.toBufferSync("raw", { colorType: "rgba" });
44
+ yield { index: index++, frame: f, data, width: canvas.width, height: canvas.height };
45
+ }
46
+ }
47
+ /**
48
+ * The render primitive: yields raw RGBA frames at the composition's native size.
49
+ * Build custom encoders / GIFs / image sequences on top of this.
50
+ */
51
+ export async function* renderFrames(comp, opts = {}) {
52
+ assertRendering(comp);
53
+ registerFonts(opts.fonts);
54
+ const total = comp.durationInFrames.get();
55
+ const from = opts.range?.from ?? 0;
56
+ const to = opts.range?.to ?? total - 1;
57
+ comp.clearAudioAssets();
58
+ yield* framesBetween(comp, from, to, opts.signal);
59
+ }
60
+ /**
61
+ * Core render: rasterize each frame, feed it to a single Mediabunny
62
+ * {@link MediabunnyEncoder}, then mix + add audio and finalize — one pass, no
63
+ * temp files, no child processes. `target` is either an output file or a stream.
64
+ */
65
+ async function runRender(comp, opts, target) {
66
+ assertRendering(comp);
67
+ registerFonts(opts.fonts);
68
+ const fps = opts.fps ?? comp.fps;
69
+ const fit = opts.fit ?? "contain";
70
+ const native = { width: comp.width(), height: comp.height() };
71
+ const size = opts.resolution ?? native;
72
+ const resize = size.width !== native.width || size.height !== native.height;
73
+ const quality = resolveQuality(opts.quality);
74
+ const format = opts.format ?? "mp4";
75
+ const total = comp.durationInFrames.get();
76
+ const from = opts.range?.from ?? 0;
77
+ const to = opts.range?.to ?? total - 1;
78
+ const frameCount = to - from + 1;
79
+ const durationInSeconds = frameCount / fps;
80
+ comp.clearAudioAssets();
81
+ const wantAudio = !opts.mute && hasAudioNodes(comp);
82
+ const encoder = new MediabunnyEncoder({
83
+ width: size.width,
84
+ height: size.height,
85
+ fps,
86
+ format,
87
+ quality,
88
+ hasAudio: wantAudio,
89
+ target,
90
+ });
91
+ await encoder.start();
92
+ const startMs = Date.now();
93
+ try {
94
+ let i = 0;
95
+ for (let f = from; f <= to; f++) {
96
+ if (opts.signal?.aborted)
97
+ throw new Error("[smoove] render aborted");
98
+ await comp.renderFrame(f);
99
+ let canvas = capture(comp);
100
+ if (resize)
101
+ canvas = fitCanvas(canvas, size, fit);
102
+ const data = canvas.toBufferSync("raw", { colorType: "rgba" });
103
+ await encoder.addFrame(data, i / fps, 1 / fps);
104
+ i++;
105
+ if (opts.onProgress) {
106
+ const elapsed = (Date.now() - startMs) / 1000;
107
+ const encFps = elapsed > 0 ? i / elapsed : 0;
108
+ opts.onProgress({
109
+ frame: i,
110
+ total: frameCount,
111
+ fps: encFps,
112
+ etaSeconds: encFps > 0 ? (frameCount - i) / encFps : undefined,
113
+ });
114
+ }
115
+ }
116
+ let hasAudio = false;
117
+ if (wantAudio) {
118
+ const clips = collectAudioTrack(comp, fps);
119
+ const mixed = await mixAudio(clips, fps, durationInSeconds, from);
120
+ hasAudio = mixed !== null;
121
+ await feedAudio(encoder, mixed, durationInSeconds);
122
+ }
123
+ await encoder.finalize();
124
+ return {
125
+ output: target.kind === "file" ? target.path : "",
126
+ width: size.width,
127
+ height: size.height,
128
+ frames: frameCount,
129
+ durationInSeconds,
130
+ hasAudio,
131
+ };
132
+ }
133
+ catch (err) {
134
+ await encoder.cancel().catch(() => { });
135
+ throw err;
136
+ }
137
+ }
138
+ /** Feed mixed PCM (or full-duration silence when the track is empty) in 1s chunks. */
139
+ async function feedAudio(encoder, mixed, durationInSeconds) {
140
+ const totalFrames = Math.ceil(durationInSeconds * AUDIO_SAMPLE_RATE);
141
+ const buf = mixed ?? new Float32Array(totalFrames * AUDIO_CHANNELS);
142
+ const chunkFrames = AUDIO_SAMPLE_RATE; // 1 second per AudioSample
143
+ for (let frame = 0; frame < totalFrames; frame += chunkFrames) {
144
+ const n = Math.min(chunkFrames, totalFrames - frame);
145
+ const chunk = buf.slice(frame * AUDIO_CHANNELS, (frame + n) * AUDIO_CHANNELS);
146
+ await encoder.addAudioChunk(chunk, frame / AUDIO_SAMPLE_RATE);
147
+ }
148
+ }
149
+ /** Render frames + audio to a muxed video file. */
150
+ export function renderComposition(comp, opts) {
151
+ return runRender(comp, opts, { kind: "file", path: opts.output });
152
+ }
153
+ /**
154
+ * Render to a fragmented mp4/webm exposed as a `Readable`, plus a `done` promise
155
+ * that resolves with the {@link RenderResult} (or rejects) so errors aren't lost.
156
+ * Mediabunny writes the fragmented container straight into the stream — no temp file.
157
+ */
158
+ export function renderToStream(comp, opts) {
159
+ const stream = new PassThrough();
160
+ const done = runRender(comp, { ...opts, output: "" }, { kind: "stream", writable: stream });
161
+ done.catch((err) => stream.destroy(err instanceof Error ? err : new Error(String(err))));
162
+ return { stream, done };
163
+ }
164
+ function fitCanvas(src, target, fit) {
165
+ const out = new Canvas(target.width, target.height);
166
+ const ctx = out.getContext("2d");
167
+ const scale = fit === "cover"
168
+ ? Math.max(target.width / src.width, target.height / src.height)
169
+ : Math.min(target.width / src.width, target.height / src.height);
170
+ const dw = src.width * scale;
171
+ const dh = src.height * scale;
172
+ ctx.drawImage(src, (target.width - dw) / 2, (target.height - dh) / 2, dw, dh);
173
+ return out;
174
+ }
175
+ /** Render a single frame to a PNG/JPEG buffer (optionally written to `output`). */
176
+ export async function renderStill(comp, opts) {
177
+ assertRendering(comp);
178
+ registerFonts(opts.fonts);
179
+ await comp.renderFrame(opts.frame);
180
+ let canvas = capture(comp);
181
+ if (opts.resolution)
182
+ canvas = fitCanvas(canvas, opts.resolution, opts.fit ?? "contain");
183
+ const buf = opts.type === "jpeg"
184
+ ? canvas.toBufferSync("jpg", { quality: opts.quality ?? 0.92 })
185
+ : canvas.toBufferSync("png");
186
+ if (opts.output)
187
+ await writeFile(opts.output, buf);
188
+ return buf;
189
+ }
190
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { FontsOption, SetupOptions } from "./types.js";
2
+ /** Register fonts with skia-canvas so `Text` nodes can use them (no DOM `@font-face`). */
3
+ export declare function registerFonts(fonts?: FontsOption): void;
4
+ /**
5
+ * Install everything needed to render a `Composition` headlessly in Node:
6
+ * the konva skia backend, the global rendering flag, and Node-safe default
7
+ * video/audio source factories + image loader (so media nodes construct without
8
+ * a DOM). Idempotent — call it (or import `@smoove/renderer/register`)
9
+ * **before** constructing any composition.
10
+ */
11
+ export declare function setupServerRendering(opts?: SetupOptions): void;
12
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM5D,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAIvD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,YAAiB,GAAG,IAAI,CAiBlE"}