@clipkit/runtime 1.0.0

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 (219) hide show
  1. package/LICENSE +54 -0
  2. package/README.md +98 -0
  3. package/dist/animation/easings.d.ts +9 -0
  4. package/dist/animation/easings.d.ts.map +1 -0
  5. package/dist/animation/easings.js +230 -0
  6. package/dist/animation/easings.js.map +1 -0
  7. package/dist/animation/expr.d.ts +44 -0
  8. package/dist/animation/expr.d.ts.map +1 -0
  9. package/dist/animation/expr.js +236 -0
  10. package/dist/animation/expr.js.map +1 -0
  11. package/dist/animation/keyframes.d.ts +23 -0
  12. package/dist/animation/keyframes.d.ts.map +1 -0
  13. package/dist/animation/keyframes.js +117 -0
  14. package/dist/animation/keyframes.js.map +1 -0
  15. package/dist/animation/motion-path.d.ts +18 -0
  16. package/dist/animation/motion-path.d.ts.map +1 -0
  17. package/dist/animation/motion-path.js +269 -0
  18. package/dist/animation/motion-path.js.map +1 -0
  19. package/dist/animation/noise1d.d.ts +5 -0
  20. package/dist/animation/noise1d.d.ts.map +1 -0
  21. package/dist/animation/noise1d.js +27 -0
  22. package/dist/animation/noise1d.js.map +1 -0
  23. package/dist/animation/presets.d.ts +60 -0
  24. package/dist/animation/presets.d.ts.map +1 -0
  25. package/dist/animation/presets.js +221 -0
  26. package/dist/animation/presets.js.map +1 -0
  27. package/dist/assets/cache.d.ts +18 -0
  28. package/dist/assets/cache.d.ts.map +1 -0
  29. package/dist/assets/cache.js +56 -0
  30. package/dist/assets/cache.js.map +1 -0
  31. package/dist/assets/fonts.d.ts +20 -0
  32. package/dist/assets/fonts.d.ts.map +1 -0
  33. package/dist/assets/fonts.js +127 -0
  34. package/dist/assets/fonts.js.map +1 -0
  35. package/dist/assets/loader.d.ts +18 -0
  36. package/dist/assets/loader.d.ts.map +1 -0
  37. package/dist/assets/loader.js +87 -0
  38. package/dist/assets/loader.js.map +1 -0
  39. package/dist/assets/lut.d.ts +5 -0
  40. package/dist/assets/lut.d.ts.map +1 -0
  41. package/dist/assets/lut.js +77 -0
  42. package/dist/assets/lut.js.map +1 -0
  43. package/dist/assets/media-time.d.ts +31 -0
  44. package/dist/assets/media-time.d.ts.map +1 -0
  45. package/dist/assets/media-time.js +65 -0
  46. package/dist/assets/media-time.js.map +1 -0
  47. package/dist/assets/mp4-frame-source.d.ts +44 -0
  48. package/dist/assets/mp4-frame-source.d.ts.map +1 -0
  49. package/dist/assets/mp4-frame-source.js +387 -0
  50. package/dist/assets/mp4-frame-source.js.map +1 -0
  51. package/dist/audio/encoder.d.ts +31 -0
  52. package/dist/audio/encoder.d.ts.map +1 -0
  53. package/dist/audio/encoder.js +96 -0
  54. package/dist/audio/encoder.js.map +1 -0
  55. package/dist/audio/fades.d.ts +16 -0
  56. package/dist/audio/fades.d.ts.map +1 -0
  57. package/dist/audio/fades.js +43 -0
  58. package/dist/audio/fades.js.map +1 -0
  59. package/dist/audio/limiter.d.ts +8 -0
  60. package/dist/audio/limiter.d.ts.map +1 -0
  61. package/dist/audio/limiter.js +39 -0
  62. package/dist/audio/limiter.js.map +1 -0
  63. package/dist/audio/loader.d.ts +6 -0
  64. package/dist/audio/loader.d.ts.map +1 -0
  65. package/dist/audio/loader.js +42 -0
  66. package/dist/audio/loader.js.map +1 -0
  67. package/dist/audio/mixer.d.ts +17 -0
  68. package/dist/audio/mixer.d.ts.map +1 -0
  69. package/dist/audio/mixer.js +204 -0
  70. package/dist/audio/mixer.js.map +1 -0
  71. package/dist/audio/varispeed.d.ts +24 -0
  72. package/dist/audio/varispeed.d.ts.map +1 -0
  73. package/dist/audio/varispeed.js +114 -0
  74. package/dist/audio/varispeed.js.map +1 -0
  75. package/dist/audio/wav.d.ts +6 -0
  76. package/dist/audio/wav.d.ts.map +1 -0
  77. package/dist/audio/wav.js +62 -0
  78. package/dist/audio/wav.js.map +1 -0
  79. package/dist/backend/backend.d.ts +579 -0
  80. package/dist/backend/backend.d.ts.map +1 -0
  81. package/dist/backend/backend.js +17 -0
  82. package/dist/backend/backend.js.map +1 -0
  83. package/dist/backend/webgl-backend.d.ts +97 -0
  84. package/dist/backend/webgl-backend.d.ts.map +1 -0
  85. package/dist/backend/webgl-backend.js +2142 -0
  86. package/dist/backend/webgl-backend.js.map +1 -0
  87. package/dist/backend/webgpu-backend.d.ts +121 -0
  88. package/dist/backend/webgpu-backend.d.ts.map +1 -0
  89. package/dist/backend/webgpu-backend.js +2481 -0
  90. package/dist/backend/webgpu-backend.js.map +1 -0
  91. package/dist/compositor/bitfont.d.ts +8 -0
  92. package/dist/compositor/bitfont.d.ts.map +1 -0
  93. package/dist/compositor/bitfont.js +52 -0
  94. package/dist/compositor/bitfont.js.map +1 -0
  95. package/dist/compositor/camera.d.ts +5 -0
  96. package/dist/compositor/camera.d.ts.map +1 -0
  97. package/dist/compositor/camera.js +114 -0
  98. package/dist/compositor/camera.js.map +1 -0
  99. package/dist/compositor/color.d.ts +26 -0
  100. package/dist/compositor/color.d.ts.map +1 -0
  101. package/dist/compositor/color.js +189 -0
  102. package/dist/compositor/color.js.map +1 -0
  103. package/dist/compositor/element-renderers/caption.d.ts +4 -0
  104. package/dist/compositor/element-renderers/caption.d.ts.map +1 -0
  105. package/dist/compositor/element-renderers/caption.js +376 -0
  106. package/dist/compositor/element-renderers/caption.js.map +1 -0
  107. package/dist/compositor/element-renderers/group.d.ts +12 -0
  108. package/dist/compositor/element-renderers/group.d.ts.map +1 -0
  109. package/dist/compositor/element-renderers/group.js +259 -0
  110. package/dist/compositor/element-renderers/group.js.map +1 -0
  111. package/dist/compositor/element-renderers/image.d.ts +4 -0
  112. package/dist/compositor/element-renderers/image.d.ts.map +1 -0
  113. package/dist/compositor/element-renderers/image.js +97 -0
  114. package/dist/compositor/element-renderers/image.js.map +1 -0
  115. package/dist/compositor/element-renderers/lit.d.ts +6 -0
  116. package/dist/compositor/element-renderers/lit.d.ts.map +1 -0
  117. package/dist/compositor/element-renderers/lit.js +82 -0
  118. package/dist/compositor/element-renderers/lit.js.map +1 -0
  119. package/dist/compositor/element-renderers/particles.d.ts +4 -0
  120. package/dist/compositor/element-renderers/particles.d.ts.map +1 -0
  121. package/dist/compositor/element-renderers/particles.js +212 -0
  122. package/dist/compositor/element-renderers/particles.js.map +1 -0
  123. package/dist/compositor/element-renderers/shape.d.ts +4 -0
  124. package/dist/compositor/element-renderers/shape.d.ts.map +1 -0
  125. package/dist/compositor/element-renderers/shape.js +171 -0
  126. package/dist/compositor/element-renderers/shape.js.map +1 -0
  127. package/dist/compositor/element-renderers/svg.d.ts +4 -0
  128. package/dist/compositor/element-renderers/svg.d.ts.map +1 -0
  129. package/dist/compositor/element-renderers/svg.js +210 -0
  130. package/dist/compositor/element-renderers/svg.js.map +1 -0
  131. package/dist/compositor/element-renderers/text.d.ts +25 -0
  132. package/dist/compositor/element-renderers/text.d.ts.map +1 -0
  133. package/dist/compositor/element-renderers/text.js +1358 -0
  134. package/dist/compositor/element-renderers/text.js.map +1 -0
  135. package/dist/compositor/element-renderers/video.d.ts +12 -0
  136. package/dist/compositor/element-renderers/video.d.ts.map +1 -0
  137. package/dist/compositor/element-renderers/video.js +109 -0
  138. package/dist/compositor/element-renderers/video.js.map +1 -0
  139. package/dist/compositor/fit.d.ts +18 -0
  140. package/dist/compositor/fit.d.ts.map +1 -0
  141. package/dist/compositor/fit.js +106 -0
  142. package/dist/compositor/fit.js.map +1 -0
  143. package/dist/compositor/lighting.d.ts +63 -0
  144. package/dist/compositor/lighting.d.ts.map +1 -0
  145. package/dist/compositor/lighting.js +141 -0
  146. package/dist/compositor/lighting.js.map +1 -0
  147. package/dist/compositor/mat4.d.ts +88 -0
  148. package/dist/compositor/mat4.d.ts.map +1 -0
  149. package/dist/compositor/mat4.js +245 -0
  150. package/dist/compositor/mat4.js.map +1 -0
  151. package/dist/compositor/project.d.ts +24 -0
  152. package/dist/compositor/project.d.ts.map +1 -0
  153. package/dist/compositor/project.js +105 -0
  154. package/dist/compositor/project.js.map +1 -0
  155. package/dist/compositor/render-context.d.ts +194 -0
  156. package/dist/compositor/render-context.d.ts.map +1 -0
  157. package/dist/compositor/render-context.js +10 -0
  158. package/dist/compositor/render-context.js.map +1 -0
  159. package/dist/compositor/resolve.d.ts +80 -0
  160. package/dist/compositor/resolve.d.ts.map +1 -0
  161. package/dist/compositor/resolve.js +276 -0
  162. package/dist/compositor/resolve.js.map +1 -0
  163. package/dist/compositor/scene.d.ts +10 -0
  164. package/dist/compositor/scene.d.ts.map +1 -0
  165. package/dist/compositor/scene.js +658 -0
  166. package/dist/compositor/scene.js.map +1 -0
  167. package/dist/compositor/transform.d.ts +73 -0
  168. package/dist/compositor/transform.d.ts.map +1 -0
  169. package/dist/compositor/transform.js +229 -0
  170. package/dist/compositor/transform.js.map +1 -0
  171. package/dist/compositor/unit.d.ts +27 -0
  172. package/dist/compositor/unit.d.ts.map +1 -0
  173. package/dist/compositor/unit.js +74 -0
  174. package/dist/compositor/unit.js.map +1 -0
  175. package/dist/encoder/exporter.d.ts +95 -0
  176. package/dist/encoder/exporter.d.ts.map +1 -0
  177. package/dist/encoder/exporter.js +341 -0
  178. package/dist/encoder/exporter.js.map +1 -0
  179. package/dist/encoder/index.d.ts +3 -0
  180. package/dist/encoder/index.d.ts.map +1 -0
  181. package/dist/encoder/index.js +2 -0
  182. package/dist/encoder/index.js.map +1 -0
  183. package/dist/index.d.ts +12 -0
  184. package/dist/index.d.ts.map +1 -0
  185. package/dist/index.js +27 -0
  186. package/dist/index.js.map +1 -0
  187. package/dist/logger.d.ts +13 -0
  188. package/dist/logger.d.ts.map +1 -0
  189. package/dist/logger.js +32 -0
  190. package/dist/logger.js.map +1 -0
  191. package/dist/runtime.d.ts +216 -0
  192. package/dist/runtime.d.ts.map +1 -0
  193. package/dist/runtime.js +1012 -0
  194. package/dist/runtime.js.map +1 -0
  195. package/dist/svg/morph.d.ts +6 -0
  196. package/dist/svg/morph.d.ts.map +1 -0
  197. package/dist/svg/morph.js +62 -0
  198. package/dist/svg/morph.js.map +1 -0
  199. package/dist/svg/svg-renderer.d.ts +18 -0
  200. package/dist/svg/svg-renderer.d.ts.map +1 -0
  201. package/dist/svg/svg-renderer.js +142 -0
  202. package/dist/svg/svg-renderer.js.map +1 -0
  203. package/dist/text/caption-chunk.d.ts +17 -0
  204. package/dist/text/caption-chunk.d.ts.map +1 -0
  205. package/dist/text/caption-chunk.js +76 -0
  206. package/dist/text/caption-chunk.js.map +1 -0
  207. package/dist/text/font-atlas.d.ts +63 -0
  208. package/dist/text/font-atlas.d.ts.map +1 -0
  209. package/dist/text/font-atlas.js +225 -0
  210. package/dist/text/font-atlas.js.map +1 -0
  211. package/dist/text/measure.d.ts +38 -0
  212. package/dist/text/measure.d.ts.map +1 -0
  213. package/dist/text/measure.js +164 -0
  214. package/dist/text/measure.js.map +1 -0
  215. package/dist/text/text-animation.d.ts +52 -0
  216. package/dist/text/text-animation.d.ts.map +1 -0
  217. package/dist/text/text-animation.js +133 -0
  218. package/dist/text/text-animation.js.map +1 -0
  219. package/package.json +47 -0
@@ -0,0 +1,341 @@
1
+ import { Muxer, ArrayBufferTarget } from "mp4-muxer";
2
+ import { encodeAudioBuffer, pickAudioCodec } from "../audio/encoder.js";
3
+ /**
4
+ * Heights for each named resolution tier. Source aspect ratio drives
5
+ * the width.
6
+ */
7
+ const RESOLUTION_HEIGHTS = {
8
+ '480p': 480,
9
+ '720p': 720,
10
+ '1080p': 1080,
11
+ '1440p': 1440,
12
+ '4k': 2160,
13
+ };
14
+ /**
15
+ * Default video bitrates per resolution tier. Tuned for landing-page /
16
+ * UI motion graphics with lots of text — slightly conservative on the
17
+ * low end, generous at 4K where chroma sampling shows blockiness fast.
18
+ */
19
+ const RESOLUTION_BITRATES = {
20
+ '480p': 1_500_000,
21
+ '720p': 4_000_000,
22
+ '1080p': 8_000_000,
23
+ '1440p': 16_000_000,
24
+ '4k': 35_000_000,
25
+ };
26
+ /**
27
+ * H.264 codec strings per resolution tier. The level field caps the
28
+ * pixel area the encoder will accept — Level 4.2 (default elsewhere)
29
+ * tops out at 1080p, so 1440p / 4K must move up to Level 5.1, which
30
+ * covers up to 4K30. High Profile (640) gives noticeably better
31
+ * compression than Baseline (420) at the same bitrate.
32
+ */
33
+ const RESOLUTION_CODECS = {
34
+ '480p': 'avc1.42E01F', // Baseline @ Level 3.1 (covers 854×480)
35
+ '720p': 'avc1.42E01F', // Baseline @ Level 3.1
36
+ '1080p': 'avc1.42002A', // Baseline @ Level 4.2
37
+ '1440p': 'avc1.640033', // High @ Level 5.1
38
+ '4k': 'avc1.640033', // High @ Level 5.1
39
+ };
40
+ /**
41
+ * Encoder pacing + stall guards for the export path.
42
+ *
43
+ * BACKPRESSURE_LIMIT — max frames allowed in the VideoEncoder queue before the
44
+ * render loop pauses for it to drain. Bounds memory and stops frame submission
45
+ * from racing ahead of a slow (software) encoder.
46
+ *
47
+ * FLUSH_TIMEOUT_MS — ceiling on encoder.flush(). With backpressure the queue is
48
+ * tiny by the time we flush, so a flush that exceeds this is a genuine stall;
49
+ * we reject it so the failure is visible instead of an infinite "100%" hang.
50
+ */
51
+ const BACKPRESSURE_LIMIT = 8;
52
+ const FLUSH_TIMEOUT_MS = 120_000;
53
+ /**
54
+ * Resolve a RenderResolution + Source dimensions into the concrete
55
+ * physical canvas size, pixel ratio, and a sensible default bitrate.
56
+ */
57
+ export function resolveRenderResolution(resolution, sourceWidth, sourceHeight) {
58
+ if (resolution === 'source') {
59
+ // Pick a codec based on the source's actual height, not a fixed
60
+ // tier — a 4K source authored natively still needs Level 5.1.
61
+ const codec = sourceHeight > 1080 ? 'avc1.640033' : 'avc1.42002A';
62
+ return {
63
+ physicalWidth: sourceWidth,
64
+ physicalHeight: sourceHeight,
65
+ pixelRatio: 1,
66
+ defaultBitrate: 5_000_000,
67
+ defaultCodec: codec,
68
+ };
69
+ }
70
+ const targetHeight = RESOLUTION_HEIGHTS[resolution];
71
+ const pixelRatio = targetHeight / sourceHeight;
72
+ return {
73
+ physicalWidth: Math.round(sourceWidth * pixelRatio),
74
+ physicalHeight: targetHeight,
75
+ pixelRatio,
76
+ defaultBitrate: RESOLUTION_BITRATES[resolution],
77
+ defaultCodec: RESOLUTION_CODECS[resolution],
78
+ };
79
+ }
80
+ /**
81
+ * H.264 (and ffmpeg's yuv420p) require EVEN frame dimensions. A named
82
+ * resolution tier derives width from the source aspect ratio, which can land
83
+ * odd — e.g. a 16:9 source at 480p gives 1920×(480/1080) = 853. Floor each
84
+ * encode dimension to the nearest even pixel so every tier / aspect ratio
85
+ * encodes cleanly. Export-only: the live preview backend is never resized
86
+ * through this.
87
+ */
88
+ function toEvenDimension(n) {
89
+ const floored = Math.max(2, Math.floor(n));
90
+ return floored % 2 === 0 ? floored : floored - 1;
91
+ }
92
+ export class ClipkitExporter {
93
+ canvas;
94
+ renderer;
95
+ encoder = null;
96
+ muxer = null;
97
+ tempCanvas;
98
+ tempCtx;
99
+ constructor(canvas, renderer) {
100
+ this.canvas = canvas;
101
+ this.renderer = renderer;
102
+ this.tempCanvas = document.createElement("canvas");
103
+ this.tempCtx = this.tempCanvas.getContext("2d");
104
+ }
105
+ async export(source, options = {}) {
106
+ const { codec = "avc1.42002A", // H.264 Baseline Profile Level 4.2 (supports 1080p)
107
+ bitrate = 5_000_000, framerate = source.frame_rate || 30, onProgress, } = options;
108
+ // Apply schema-aware defaults — Source dimensions are optional in the
109
+ // schema but mandatory for encoding.
110
+ const sourceWidth = source.width ?? 1920;
111
+ const sourceHeight = source.height ?? 1080;
112
+ const duration = typeof source.duration === "number" ? source.duration : 10;
113
+ // Output dimensions come from the runtime canvas — the runtime is
114
+ // expected to have resize()'d the backend to the right pixel ratio
115
+ // before calling export(). Falls back to source dims if not. Clamped to
116
+ // even (H.264 requires it) — the backend canvas may be odd at some tiers.
117
+ const width = toEvenDimension(this.canvas.width || sourceWidth);
118
+ const height = toEvenDimension(this.canvas.height || sourceHeight);
119
+ console.log("[clipkit] Starting export:", { codec, bitrate, framerate, sourceWidth, sourceHeight, width, height, duration, hasAudio: !!options.audio });
120
+ this.tempCanvas.width = width;
121
+ this.tempCanvas.height = height;
122
+ const totalFrames = Math.ceil(duration * framerate);
123
+ // Motion blur — exact sub-frame supersampling (PROTOCOL.md §3.x).
124
+ // N renders per output frame across a shutter window centered on the
125
+ // frame time, averaged per 8-bit channel in float (single rounding).
126
+ // samples=1 or no motion_blur block → the plain single-render path.
127
+ const mb = source.motion_blur;
128
+ const mbSamples = mb
129
+ ? Math.max(1, Math.min(32, Math.round(typeof mb.samples === "number" ? mb.samples : 8)))
130
+ : 1;
131
+ const mbShutter = mb && typeof mb.shutter === "number"
132
+ ? Math.min(1, Math.max(0, mb.shutter))
133
+ : 0.5;
134
+ const blurOn = mbSamples > 1 && mbShutter > 0;
135
+ const mbAccum = blurOn ? new Float32Array(width * height * 4) : null;
136
+ console.log("[clipkit] Encoding", totalFrames, "frames at", framerate, "fps", blurOn ? `(motion blur: ${mbSamples} samples, shutter ${mbShutter})` : "");
137
+ const target = new ArrayBufferTarget();
138
+ // Negotiate an audio codec this environment can actually encode. Chromium
139
+ // on Linux (the render container) has NO AAC encoder, so AAC throws
140
+ // "Unsupported codec type" mid-export; pickAudioCodec falls back to Opus
141
+ // there (MP4 muxes both). null → no audio encoder at all: render silently
142
+ // instead of crashing.
143
+ let audioPick = null;
144
+ if (options.audio) {
145
+ audioPick = await pickAudioCodec(options.audio.buffer.sampleRate, options.audio.buffer.numberOfChannels, options.audio.bitrate);
146
+ if (!audioPick) {
147
+ console.warn("[clipkit] No supported audio encoder — rendering without audio.");
148
+ }
149
+ }
150
+ const muxerConfig = {
151
+ target,
152
+ video: {
153
+ codec: "avc",
154
+ width,
155
+ height,
156
+ },
157
+ fastStart: "in-memory",
158
+ };
159
+ // Add the audio track only when we have a working encoder for it.
160
+ if (options.audio && audioPick) {
161
+ muxerConfig.audio = {
162
+ codec: audioPick.muxer,
163
+ sampleRate: options.audio.buffer.sampleRate,
164
+ numberOfChannels: options.audio.buffer.numberOfChannels,
165
+ };
166
+ }
167
+ this.muxer = new Muxer(muxerConfig);
168
+ // Encode audio first (it's fast — single pass over the pre-mixed buffer).
169
+ // Audio chunks reach the muxer before video chunks; mp4-muxer handles
170
+ // interleaving on finalize. Non-fatal: an audio failure must never sink the
171
+ // whole render — drop audio and keep the video.
172
+ if (options.audio && audioPick) {
173
+ console.log("[clipkit] Encoding audio:", {
174
+ codec: audioPick.muxer,
175
+ sampleRate: options.audio.buffer.sampleRate,
176
+ channels: options.audio.buffer.numberOfChannels,
177
+ duration: options.audio.buffer.duration.toFixed(2) + "s",
178
+ });
179
+ try {
180
+ await encodeAudioBuffer(options.audio.buffer, this.muxer, {
181
+ bitrate: options.audio.bitrate,
182
+ codec: audioPick.encoder,
183
+ });
184
+ console.log("[clipkit] Audio encoded");
185
+ }
186
+ catch (e) {
187
+ console.warn("[clipkit] Audio encode failed — continuing without audio:", e instanceof Error ? e.message : String(e));
188
+ }
189
+ }
190
+ // Progress is driven from the encoder OUTPUT (frames actually encoded), not
191
+ // from submission. Submission races ahead of a slow software encoder — which
192
+ // is exactly why the bar used to hit 100% while flush() was still draining.
193
+ // Now 100% means every frame is truly encoded + muxed.
194
+ let encodedChunks = 0;
195
+ // Latch encoder faults. The WebCodecs error callback fires asynchronously and
196
+ // does not reliably reject the pending flush() across browsers, so we capture
197
+ // it and throw at the next checkpoint — turning a silent hang into a visible,
198
+ // catchable failure.
199
+ let encoderError = null;
200
+ this.encoder = new VideoEncoder({
201
+ output: (chunk, metadata) => {
202
+ this.muxer?.addVideoChunk(chunk, metadata);
203
+ encodedChunks++;
204
+ if (onProgress)
205
+ onProgress(Math.min(1, encodedChunks / totalFrames));
206
+ if (encodedChunks % 30 === 0 || encodedChunks === totalFrames) {
207
+ console.log(`[clipkit] Encoded ${encodedChunks}/${totalFrames}`);
208
+ }
209
+ },
210
+ error: (error) => {
211
+ encoderError = error instanceof Error ? error : new Error(String(error));
212
+ console.error("[clipkit] VideoEncoder error:", encoderError.message);
213
+ },
214
+ });
215
+ this.encoder.configure({
216
+ codec,
217
+ width,
218
+ height,
219
+ bitrate,
220
+ });
221
+ // Render and encode each frame
222
+ for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
223
+ const currentTime = frameIndex * (1 / framerate);
224
+ if (blurOn && mbAccum) {
225
+ mbAccum.fill(0);
226
+ for (let k = 0; k < mbSamples; k++) {
227
+ const tk = Math.min(Math.max(currentTime + ((k + 0.5) / mbSamples - 0.5) * (mbShutter / framerate), 0), duration);
228
+ await this.renderer.renderAsync(source, tk);
229
+ await this.renderer.gpuFinish(); // drain GPU before read-back (hardware-GL capture race)
230
+ this.tempCtx.drawImage(this.canvas, 0, 0, width, height);
231
+ const sample = this.tempCtx.getImageData(0, 0, width, height).data;
232
+ for (let i = 0; i < sample.length; i++)
233
+ mbAccum[i] += sample[i];
234
+ }
235
+ const out = this.tempCtx.createImageData(width, height);
236
+ const od = out.data;
237
+ const inv = 1 / mbSamples;
238
+ for (let i = 0; i < od.length; i++)
239
+ od[i] = Math.round(mbAccum[i] * inv);
240
+ this.tempCtx.putImageData(out, 0, 0);
241
+ }
242
+ else {
243
+ await this.renderer.renderAsync(source, currentTime);
244
+ await this.renderer.gpuFinish(); // drain GPU before read-back (hardware-GL capture race)
245
+ this.tempCtx.drawImage(this.canvas, 0, 0, width, height);
246
+ }
247
+ const videoFrame = new VideoFrame(this.tempCanvas, {
248
+ timestamp: (frameIndex * 1_000_000) / framerate, // microseconds
249
+ duration: 1_000_000 / framerate,
250
+ });
251
+ // Backpressure — never let submission race more than a few frames ahead of
252
+ // the (often software) encoder. Without this a heavy comp queues hundreds
253
+ // of VideoFrames faster than they drain: memory climbs and flush() can take
254
+ // (or appear to take) forever. 'dequeue' is the modern drain signal; race a
255
+ // short macrotask timer so we never wedge if it isn't delivered.
256
+ while (this.encoder.encodeQueueSize > BACKPRESSURE_LIMIT) {
257
+ if (encoderError)
258
+ throw encoderError;
259
+ await new Promise((resolve) => {
260
+ const enc = this.encoder;
261
+ let settled = false;
262
+ const finish = () => {
263
+ if (settled)
264
+ return;
265
+ settled = true;
266
+ enc.removeEventListener("dequeue", finish);
267
+ resolve();
268
+ };
269
+ enc.addEventListener("dequeue", finish, { once: true });
270
+ setTimeout(finish, 50);
271
+ });
272
+ }
273
+ if (encoderError)
274
+ throw encoderError;
275
+ // Encode frame
276
+ const keyFrame = frameIndex % 30 === 0; // Keyframe every 30 frames
277
+ this.encoder.encode(videoFrame, { keyFrame });
278
+ // Close frame to free memory. Progress comes from the encoder OUTPUT
279
+ // callback (chunks actually encoded), not from submission here.
280
+ videoFrame.close();
281
+ }
282
+ // Watchdog around flush — a wedged software encoder can leave flush() pending
283
+ // forever, which is what hung the export at "100%" with no error and no
284
+ // download. With backpressure the queue is small here, so a flush that blows
285
+ // past this ceiling is a real stall: reject so the UI surfaces an error
286
+ // instead of an infinite spinner. Ceiling is generous — legit heavy comps are
287
+ // fine; an actual hang is not.
288
+ if (encoderError)
289
+ throw encoderError;
290
+ await Promise.race([
291
+ this.encoder.flush(),
292
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Export stalled: encoder.flush did not resolve within ${FLUSH_TIMEOUT_MS / 1000}s`)), FLUSH_TIMEOUT_MS)),
293
+ ]);
294
+ if (encoderError)
295
+ throw encoderError;
296
+ console.log("[clipkit] Encoding complete");
297
+ this.muxer.finalize();
298
+ const { buffer } = target;
299
+ if (!buffer || buffer.byteLength === 0) {
300
+ throw new Error("Muxer produced no output");
301
+ }
302
+ console.log("[clipkit] Muxer finalized, size:", (buffer.byteLength / 1024 / 1024).toFixed(2), "MB");
303
+ const mp4Blob = new Blob([buffer], { type: "video/mp4" });
304
+ // Cleanup
305
+ this.encoder.close();
306
+ this.encoder = null;
307
+ this.muxer = null;
308
+ console.log("[clipkit] Export complete, size:", (mp4Blob.size / 1024 / 1024).toFixed(2), "MB");
309
+ return mp4Blob;
310
+ }
311
+ /**
312
+ * Render each frame and hand its PNG bytes (base64, with alpha) to `onFrame`.
313
+ * No encoder/muxer — this feeds the transparent export path (ProRes 4444 /
314
+ * VP9-alpha), which assembles the PNGs with a server-side ffmpeg. `alpha`
315
+ * relies on the renderer having been told to clear transparent (the runtime
316
+ * arranges that). Returns the frame count.
317
+ */
318
+ async exportFrames(source, options) {
319
+ const framerate = options.framerate ?? source.frame_rate ?? 30;
320
+ const duration = typeof source.duration === "number" ? source.duration : 10;
321
+ // Even dims for the downstream ffmpeg encode (yuv420p), same as export().
322
+ const width = toEvenDimension(this.canvas.width || (source.width ?? 1920));
323
+ const height = toEvenDimension(this.canvas.height || (source.height ?? 1080));
324
+ this.tempCanvas.width = width;
325
+ this.tempCanvas.height = height;
326
+ const totalFrames = Math.ceil(duration * framerate);
327
+ for (let i = 0; i < totalFrames; i++) {
328
+ await this.renderer.renderAsync(source, i / framerate);
329
+ await this.renderer.gpuFinish(); // drain GPU before read-back (hardware-GL capture race)
330
+ // Transparent output must not composite onto the previous frame.
331
+ if (options.alpha)
332
+ this.tempCtx.clearRect(0, 0, width, height);
333
+ this.tempCtx.drawImage(this.canvas, 0, 0, width, height);
334
+ const dataUrl = this.tempCanvas.toDataURL("image/png");
335
+ await options.onFrame(i, dataUrl.slice(dataUrl.indexOf(",") + 1));
336
+ options.onProgress?.((i + 1) / totalFrames);
337
+ }
338
+ return totalFrames;
339
+ }
340
+ }
341
+ //# sourceMappingURL=exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exporter.js","sourceRoot":"","sources":["../../src/encoder/exporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { ClipkitExporter } from './exporter.js';
2
+ export type { ExportOptions } from './exporter.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/encoder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ClipkitExporter } from './exporter.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/encoder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,12 @@
1
+ export { ClipkitRuntime } from './runtime.js';
2
+ export { setLogger, getLogger, type Logger } from './logger.js';
3
+ export { projectElementQuad, unprojectToPlane, elementDepthZ, type Pt, } from './compositor/project.js';
4
+ export { mapToMediaTime, trimWindow, rateOf, timeRemapOf, trimDurationOf, type MediaTiming, } from './assets/media-time.js';
5
+ export { fadeBreakpoints, fadeGainAt, type FadePoint } from './audio/fades.js';
6
+ export { createMasterLimiter } from './audio/limiter.js';
7
+ export { applyEasing } from './animation/easings.js';
8
+ export { compileExpr, evalExpr, isExpr, type ExprScope } from './animation/expr.js';
9
+ export { ClipkitExporter, resolveRenderResolution, type ExportOptions, type FrameProducer, type RenderResolution, } from './encoder/exporter.js';
10
+ export type { Source, Element } from '@clipkit/protocol';
11
+ export { chunkCaptionWords, activeCaptionChunk, type CaptionChunk } from './text/caption-chunk.js';
12
+ //# 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,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIhE,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,KAAK,EAAE,GACR,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EACL,cAAc,EACd,UAAU,EACV,MAAM,EACN,WAAW,EACX,cAAc,EACd,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAIzD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGpF,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,uBAAuB,CAAC;AAK/B,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAKzD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ // @clipkit/runtime — public surface.
2
+ export { ClipkitRuntime } from './runtime.js';
3
+ export { setLogger, getLogger } from './logger.js';
4
+ // Pure projection helpers for editor camera-view gizmos (§item 6) —
5
+ // render-consistent (reuse the compositor's camera + resolve + matrix).
6
+ export { projectElementQuad, unprojectToPlane, elementDepthZ, } from './compositor/project.js';
7
+ // Composition-time → media-time mapping, shared with the playback
8
+ // engine (video pump sync + audio scheduling for video tracks).
9
+ export { mapToMediaTime, trimWindow, rateOf, timeRemapOf, trimDurationOf, } from './assets/media-time.js';
10
+ // Audio fade envelope, shared with the playback engine's scheduler so
11
+ // preview and export gains are identical.
12
+ export { fadeBreakpoints, fadeGainAt } from './audio/fades.js';
13
+ // Master output limiter, shared with the playback engine's scheduler so the
14
+ // preview master bus and the export master bus apply identical clip protection.
15
+ export { createMasterLimiter } from './audio/limiter.js';
16
+ // The normative easing evaluator — shared with the editors' curve
17
+ // display so the drawn curve IS the curve that renders.
18
+ export { applyEasing } from './animation/easings.js';
19
+ // Tier-A expression evaluator (editors/tools use compileExpr for live validity).
20
+ export { compileExpr, evalExpr, isExpr } from './animation/expr.js';
21
+ // Encoder (still callable directly for advanced use).
22
+ export { ClipkitExporter, resolveRenderResolution, } from './encoder/exporter.js';
23
+ // Caption windowing — split a caption's words into chunks (max_length) and pick
24
+ // the chunk active at time t. Shared with the editor timeline so its chunk
25
+ // blocks match what the renderer shows.
26
+ export { chunkCaptionWords, activeCaptionChunk } from './text/caption-chunk.js';
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAe,MAAM,aAAa,CAAC;AAEhE,oEAAoE;AACpE,wEAAwE;AACxE,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GAEd,MAAM,yBAAyB,CAAC;AAEjC,kEAAkE;AAClE,gEAAgE;AAChE,OAAO,EACL,cAAc,EACd,UAAU,EACV,MAAM,EACN,WAAW,EACX,cAAc,GAEf,MAAM,wBAAwB,CAAC;AAEhC,sEAAsE;AACtE,0CAA0C;AAC1C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAE/E,4EAA4E;AAC5E,gFAAgF;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,kEAAkE;AAClE,wDAAwD;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,iFAAiF;AACjF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAkB,MAAM,qBAAqB,CAAC;AAEpF,sDAAsD;AACtD,OAAO,EACL,eAAe,EACf,uBAAuB,GAIxB,MAAM,uBAAuB,CAAC;AAO/B,gFAAgF;AAChF,2EAA2E;AAC3E,wCAAwC;AACxC,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAqB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface Logger {
2
+ debug(message: string, ...args: unknown[]): void;
3
+ info(message: string, ...args: unknown[]): void;
4
+ warn(message: string, ...args: unknown[]): void;
5
+ error(message: string, ...args: unknown[]): void;
6
+ }
7
+ /**
8
+ * Replace the runtime's logger. Pass `'console'` to log to the browser console
9
+ * (default), `'silent'` to suppress everything, or a custom Logger object.
10
+ */
11
+ export declare function setLogger(logger: Logger | 'console' | 'silent'): void;
12
+ export declare function getLogger(): Logger;
13
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD;AAkBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAIrE;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
package/dist/logger.js ADDED
@@ -0,0 +1,32 @@
1
+ // Pluggable logger. The runtime prefixes its console output with `[clipkit]`
2
+ // (formerly `[v0]` in the upstream prototype). Consumers can swap in their
3
+ // own logger, silence everything, or route through their existing observability.
4
+ const silentLogger = {
5
+ debug: () => { },
6
+ info: () => { },
7
+ warn: () => { },
8
+ error: () => { },
9
+ };
10
+ const consoleLogger = {
11
+ debug: (m, ...a) => console.debug(`[clipkit] ${m}`, ...a),
12
+ info: (m, ...a) => console.info(`[clipkit] ${m}`, ...a),
13
+ warn: (m, ...a) => console.warn(`[clipkit] ${m}`, ...a),
14
+ error: (m, ...a) => console.error(`[clipkit] ${m}`, ...a),
15
+ };
16
+ let activeLogger = consoleLogger;
17
+ /**
18
+ * Replace the runtime's logger. Pass `'console'` to log to the browser console
19
+ * (default), `'silent'` to suppress everything, or a custom Logger object.
20
+ */
21
+ export function setLogger(logger) {
22
+ if (logger === 'console')
23
+ activeLogger = consoleLogger;
24
+ else if (logger === 'silent')
25
+ activeLogger = silentLogger;
26
+ else
27
+ activeLogger = logger;
28
+ }
29
+ export function getLogger() {
30
+ return activeLogger;
31
+ }
32
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,2EAA2E;AAC3E,iFAAiF;AASjF,MAAM,YAAY,GAAW;IAC3B,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAEF,MAAM,aAAa,GAAW;IAC5B,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACzD,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACvD,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACvD,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;CAC1D,CAAC;AAEF,IAAI,YAAY,GAAW,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAqC;IAC7D,IAAI,MAAM,KAAK,SAAS;QAAE,YAAY,GAAG,aAAa,CAAC;SAClD,IAAI,MAAM,KAAK,QAAQ;QAAE,YAAY,GAAG,YAAY,CAAC;;QACrD,YAAY,GAAG,MAAM,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC"}