@motionscript/browser 0.1.0-alpha.0 → 0.1.0-alpha.10

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 (181) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/NOTICE +49 -0
  3. package/README.md +96 -24
  4. package/dist/asset-bytes.d.ts +14 -5
  5. package/dist/asset-bytes.d.ts.map +1 -1
  6. package/dist/asset-bytes.js +23 -5
  7. package/dist/asset-bytes.js.map +1 -1
  8. package/dist/audio/bus-graph.d.ts +12 -3
  9. package/dist/audio/bus-graph.d.ts.map +1 -1
  10. package/dist/audio/bus-graph.js +35 -31
  11. package/dist/audio/bus-graph.js.map +1 -1
  12. package/dist/audio/effect-graph.d.ts +62 -0
  13. package/dist/audio/effect-graph.d.ts.map +1 -0
  14. package/dist/audio/effect-graph.js +234 -0
  15. package/dist/audio/effect-graph.js.map +1 -0
  16. package/dist/audio/index.d.ts.map +1 -1
  17. package/dist/audio/index.js +2 -3
  18. package/dist/audio/index.js.map +1 -1
  19. package/dist/audio/loop-region.d.ts +31 -0
  20. package/dist/audio/loop-region.d.ts.map +1 -0
  21. package/dist/audio/loop-region.js +43 -0
  22. package/dist/audio/loop-region.js.map +1 -0
  23. package/dist/audio/mixer.d.ts +4 -4
  24. package/dist/audio/mixer.d.ts.map +1 -1
  25. package/dist/audio/mixer.js +43 -44
  26. package/dist/audio/mixer.js.map +1 -1
  27. package/dist/audio/player.d.ts +15 -9
  28. package/dist/audio/player.d.ts.map +1 -1
  29. package/dist/audio/player.js +60 -69
  30. package/dist/audio/player.js.map +1 -1
  31. package/dist/browser/THIRD_PARTY_LICENSES.txt +784 -0
  32. package/dist/browser/audio.js +1 -1
  33. package/dist/browser/chunks/chunk-F3AZKPHU.js +2 -0
  34. package/dist/browser/chunks/chunk-F3AZKPHU.js.map +7 -0
  35. package/dist/browser/chunks/chunk-KLXMRKTL.js +2 -0
  36. package/dist/browser/chunks/chunk-L5UFQACH.js +163 -0
  37. package/dist/browser/chunks/chunk-L5UFQACH.js.map +7 -0
  38. package/dist/browser/chunks/mediabunny-mp3-encoder-ZCIHVVTJ.js +14 -0
  39. package/dist/browser/chunks/mediabunny-mp3-encoder-ZCIHVVTJ.js.map +7 -0
  40. package/dist/browser/index.js +52 -1490
  41. package/dist/browser/index.js.map +4 -4
  42. package/dist/browser/manifest.json +21 -4
  43. package/dist/browser/ms_renderer_bg.wasm +0 -0
  44. package/dist/concat.d.ts +33 -7
  45. package/dist/concat.d.ts.map +1 -1
  46. package/dist/concat.js +117 -72
  47. package/dist/concat.js.map +1 -1
  48. package/dist/deliver.d.ts +10 -0
  49. package/dist/deliver.d.ts.map +1 -0
  50. package/dist/deliver.js +22 -0
  51. package/dist/deliver.js.map +1 -0
  52. package/dist/dynamic-nodes.d.ts.map +1 -1
  53. package/dist/dynamic-nodes.js +9 -12
  54. package/dist/dynamic-nodes.js.map +1 -1
  55. package/dist/engine.d.ts +43 -15
  56. package/dist/engine.d.ts.map +1 -1
  57. package/dist/engine.js +58 -36
  58. package/dist/engine.js.map +1 -1
  59. package/dist/export-audio.d.ts +36 -0
  60. package/dist/export-audio.d.ts.map +1 -0
  61. package/dist/export-audio.js +56 -0
  62. package/dist/export-audio.js.map +1 -0
  63. package/dist/exporter.d.ts +134 -15
  64. package/dist/exporter.d.ts.map +1 -1
  65. package/dist/exporter.js +135 -90
  66. package/dist/exporter.js.map +1 -1
  67. package/dist/formats.d.ts +36 -0
  68. package/dist/formats.d.ts.map +1 -0
  69. package/dist/formats.js +83 -0
  70. package/dist/formats.js.map +1 -0
  71. package/dist/frame-loop.d.ts +190 -0
  72. package/dist/frame-loop.d.ts.map +1 -0
  73. package/dist/frame-loop.js +304 -0
  74. package/dist/frame-loop.js.map +1 -0
  75. package/dist/index.d.ts +12 -10
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +18 -47
  78. package/dist/index.js.map +1 -1
  79. package/dist/mix-timeline-audio.d.ts +7 -7
  80. package/dist/mix-timeline-audio.d.ts.map +1 -1
  81. package/dist/mix-timeline-audio.js +11 -14
  82. package/dist/mix-timeline-audio.js.map +1 -1
  83. package/dist/parallel/export-parallel.d.ts +32 -0
  84. package/dist/parallel/export-parallel.d.ts.map +1 -0
  85. package/dist/parallel/export-parallel.js +114 -0
  86. package/dist/parallel/export-parallel.js.map +1 -0
  87. package/dist/parallel/governor.d.ts +57 -0
  88. package/dist/parallel/governor.d.ts.map +1 -0
  89. package/dist/parallel/governor.js +79 -0
  90. package/dist/parallel/governor.js.map +1 -0
  91. package/dist/parallel/lane.d.ts +45 -0
  92. package/dist/parallel/lane.d.ts.map +1 -0
  93. package/dist/parallel/lane.js +2 -0
  94. package/dist/parallel/lane.js.map +1 -0
  95. package/dist/parallel/lanes.d.ts +20 -0
  96. package/dist/parallel/lanes.d.ts.map +1 -0
  97. package/dist/parallel/lanes.js +152 -0
  98. package/dist/parallel/lanes.js.map +1 -0
  99. package/dist/parallel/plan.d.ts +23 -0
  100. package/dist/parallel/plan.d.ts.map +1 -0
  101. package/dist/parallel/plan.js +30 -0
  102. package/dist/parallel/plan.js.map +1 -0
  103. package/dist/parallel/run-lanes.d.ts +38 -0
  104. package/dist/parallel/run-lanes.d.ts.map +1 -0
  105. package/dist/parallel/run-lanes.js +164 -0
  106. package/dist/parallel/run-lanes.js.map +1 -0
  107. package/dist/parallel/session.d.ts +9 -0
  108. package/dist/parallel/session.d.ts.map +1 -0
  109. package/dist/parallel/session.js +79 -0
  110. package/dist/parallel/session.js.map +1 -0
  111. package/dist/renderer.d.ts +22 -0
  112. package/dist/renderer.d.ts.map +1 -0
  113. package/dist/renderer.js +23 -0
  114. package/dist/renderer.js.map +1 -0
  115. package/dist/screenshot.d.ts +6 -6
  116. package/dist/screenshot.d.ts.map +1 -1
  117. package/dist/screenshot.js +4 -5
  118. package/dist/screenshot.js.map +1 -1
  119. package/dist/still.d.ts +32 -24
  120. package/dist/still.d.ts.map +1 -1
  121. package/dist/still.js +54 -54
  122. package/dist/still.js.map +1 -1
  123. package/dist/ticker.d.ts +41 -9
  124. package/dist/ticker.d.ts.map +1 -1
  125. package/dist/ticker.js +113 -41
  126. package/dist/ticker.js.map +1 -1
  127. package/package.json +11 -10
  128. package/dist/audio/filter-graph.d.ts +0 -58
  129. package/dist/audio/filter-graph.d.ts.map +0 -1
  130. package/dist/audio/filter-graph.js +0 -235
  131. package/dist/audio/filter-graph.js.map +0 -1
  132. package/dist/browser/chunks/GLTFLoader-PGXD4V5B.js +0 -2
  133. package/dist/browser/chunks/GLTFLoader-PGXD4V5B.js.map +0 -7
  134. package/dist/browser/chunks/SkeletonUtils-XYDMTGTM.js +0 -2
  135. package/dist/browser/chunks/chunk-CTLBXZSU.js +0 -4119
  136. package/dist/browser/chunks/chunk-CTLBXZSU.js.map +0 -7
  137. package/dist/browser/chunks/chunk-FQ32PDIU.js +0 -2
  138. package/dist/browser/chunks/chunk-FQ32PDIU.js.map +0 -7
  139. package/dist/browser/chunks/chunk-FY3BTKY4.js +0 -2
  140. package/dist/browser/chunks/chunk-FY3BTKY4.js.map +0 -7
  141. package/dist/browser/chunks/chunk-NSFLRI4P.js +0 -2
  142. package/dist/browser/chunks/chunk-NSFLRI4P.js.map +0 -7
  143. package/dist/browser/chunks/three.module-Z4EOVUWF.js +0 -2
  144. package/dist/browser/chunks/three.module-Z4EOVUWF.js.map +0 -7
  145. package/dist/decode/image-levels.d.ts +0 -137
  146. package/dist/decode/image-levels.d.ts.map +0 -1
  147. package/dist/decode/image-levels.js +0 -477
  148. package/dist/decode/image-levels.js.map +0 -1
  149. package/dist/decode/levels.d.ts +0 -37
  150. package/dist/decode/levels.d.ts.map +0 -1
  151. package/dist/decode/levels.js +0 -68
  152. package/dist/decode/levels.js.map +0 -1
  153. package/dist/decode/video-levels.d.ts +0 -37
  154. package/dist/decode/video-levels.d.ts.map +0 -1
  155. package/dist/decode/video-levels.js +0 -95
  156. package/dist/decode/video-levels.js.map +0 -1
  157. package/dist/getter.d.ts +0 -10
  158. package/dist/getter.d.ts.map +0 -1
  159. package/dist/getter.js +0 -38
  160. package/dist/getter.js.map +0 -1
  161. package/dist/measure-context.d.ts +0 -12
  162. package/dist/measure-context.d.ts.map +0 -1
  163. package/dist/measure-context.js +0 -12
  164. package/dist/measure-context.js.map +0 -1
  165. package/dist/output.d.ts +0 -7
  166. package/dist/output.d.ts.map +0 -1
  167. package/dist/output.js +0 -6
  168. package/dist/output.js.map +0 -1
  169. package/dist/render-context.d.ts +0 -30
  170. package/dist/render-context.d.ts.map +0 -1
  171. package/dist/render-context.js +0 -55
  172. package/dist/render-context.js.map +0 -1
  173. package/dist/storage-adapter.d.ts +0 -367
  174. package/dist/storage-adapter.d.ts.map +0 -1
  175. package/dist/storage-adapter.js +0 -1278
  176. package/dist/storage-adapter.js.map +0 -1
  177. package/dist/three/renderer.d.ts +0 -48
  178. package/dist/three/renderer.d.ts.map +0 -1
  179. package/dist/three/renderer.js +0 -177
  180. package/dist/three/renderer.js.map +0 -1
  181. /package/dist/browser/chunks/{SkeletonUtils-XYDMTGTM.js.map → chunk-KLXMRKTL.js.map} +0 -0
@@ -0,0 +1,304 @@
1
+ import { AssetManager, AudioSink, ClipStore, AssetTimeline, isFatalRenderIssue, RenderBackendError, renderIssueKey, renderSettled, SETTLE_PASSES, setTheme, toRenderIssue, spansCovering, StateEvaluator, yieldToScheduler, } from "@motionscript/core";
2
+ /**
3
+ * Audio is mixed offline at the end of a run (or irrelevant, for a still), so the
4
+ * `AssetManager` just needs an `AudioSink`-shaped sink that does nothing during
5
+ * the per-frame pass.
6
+ */
7
+ export class NoopAudioSink extends AudioSink {
8
+ /** Nothing here records a request, so `AssetManager` can skip the whole sync. */
9
+ get schedulesAudio() { return false; }
10
+ has(_src) { return true; }
11
+ async append(_src, _data) { }
12
+ retain(_keep) { }
13
+ schedule(_requests) { }
14
+ syncTo(_clipTime) { }
15
+ play(_time, _speed, _reverse) { }
16
+ stop() { }
17
+ }
18
+ /**
19
+ * Hand the context's issues since the last call to `onIssue`, once per key in
20
+ * `seen`, and throw on one that means the frames drawn are not to be trusted.
21
+ */
22
+ function drainIssues(renderContext, seen, onIssue) {
23
+ for (const raw of renderContext.takeRenderIssues()) {
24
+ const issue = toRenderIssue(raw);
25
+ if (isFatalRenderIssue(issue))
26
+ throw new RenderBackendError(issue);
27
+ const key = renderIssueKey(issue);
28
+ if (seen.has(key))
29
+ continue;
30
+ seen.add(key);
31
+ onIssue?.(issue);
32
+ }
33
+ }
34
+ /** Draw one frame, settling and re-rendering until it is accurate. See `renderSettled`. */
35
+ async function renderWarmFrame({ renderContext, storageAdapter }, draw, frame) {
36
+ const outcome = await renderSettled(() => renderContext.execute(draw), storageAdapter, () => true);
37
+ if (outcome === "exhausted") {
38
+ console.warn(`[export] frame ${frame} still had pending decodes after ${SETTLE_PASSES} renders; it may not be exact.`);
39
+ }
40
+ }
41
+ /**
42
+ * Switch the adapter to decoding exactly what each frame draws, so a frame is
43
+ * the same pixels whatever was rendered on this adapter before it. Returns the
44
+ * restore: an adapter lent by a live preview must not come back pinned to it.
45
+ */
46
+ function exactDecodes(storageAdapter) {
47
+ const previous = storageAdapter.getDecodePolicy();
48
+ storageAdapter.setDecodePolicy("exact");
49
+ return () => storageAdapter.setDecodePolicy(previous);
50
+ }
51
+ function outputOf(project) {
52
+ const { fps, viewport } = project;
53
+ if (fps === undefined || viewport === undefined) {
54
+ throw new Error("Rendering takes a project whose fps and viewport are resolved — see Session.");
55
+ }
56
+ return { fps, viewport };
57
+ }
58
+ /** `frames`, or the whole timeline; a range that is empty or leaves the timeline is refused rather than clamped. */
59
+ function checkedRange(frames, totalFrames) {
60
+ if (!frames)
61
+ return { start: 0, end: totalFrames };
62
+ const { start, end } = frames;
63
+ if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end <= start || end > totalFrames) {
64
+ throw new RangeError(`Frames ${start} to ${end} are not a range inside this timeline's ${totalFrames} frames.`);
65
+ }
66
+ return { start, end };
67
+ }
68
+ /** Build the discovery + evaluator pair both entry points drive, over one store so each track builds once. */
69
+ function prepare(params) {
70
+ const { project, registry, assetCatalog, renderContext } = params;
71
+ const { fps, viewport } = outputOf(project);
72
+ // The theme is process-global, so anything else drawing in this page may
73
+ // have replaced it; applied before the first build, which resolves tokens.
74
+ const applyTheme = () => setTheme(project.theme ?? params.theme);
75
+ applyTheme();
76
+ // The render context *is* the measurer — `RenderContext2D` extends
77
+ // `MeasureContext2D` so a node measures and draws through one object. Named
78
+ // rather than cast so the compiler checks it; this package has no test suite
79
+ // to catch it otherwise.
80
+ const measurer = renderContext;
81
+ const scope = { registry, viewport, fps, assets: assetCatalog, measurer };
82
+ const loads = {};
83
+ const store = new ClipStore(project, scope, { isReady: (key) => loads.manager?.isReady(key) ?? false });
84
+ const precomper = new AssetTimeline(project, scope, { store });
85
+ const stateEvaluator = new StateEvaluator(project, scope, store);
86
+ return { store, precomper, stateEvaluator, measurer, fps, viewport, loads, applyTheme };
87
+ }
88
+ /**
89
+ * Load the layout gates of `trackIds`, then rebuild any of them whose compile
90
+ * measured a face before it was resident — every build here runs before a
91
+ * single asset has loaded.
92
+ */
93
+ async function settleLayoutGates(store, manager, trackIds) {
94
+ const gates = trackIds.flatMap((id) => store.layoutGates(id).filter((record) => !manager.isReady(record.src)));
95
+ if (gates.length > 0)
96
+ await manager.loadRecords(gates);
97
+ store.settleGates(trackIds);
98
+ }
99
+ /**
100
+ * Collect every audio request with the absolute offset it plays at. See
101
+ * {@link ScheduledAudioRequest.globalOffset}.
102
+ *
103
+ * Exported because a **split** export has to mix its audio somewhere other than
104
+ * alongside its frames: the video is rendered in pieces by separate workers, and
105
+ * concatenating separately-encoded AAC would put the encoder's priming delay at
106
+ * every join. The coordinator mixes once over the whole timeline instead — and
107
+ * must schedule it exactly as this does. A second implementation that drifted by
108
+ * a track offset would produce a file that plays perfectly and is out of sync.
109
+ */
110
+ export function collectAudio(precomp) {
111
+ const out = [];
112
+ for (const track of precomp.tracks) {
113
+ const globalOffset = track.startFrame / precomp.fps;
114
+ for (const request of track.audioRequests)
115
+ out.push({ request, globalOffset });
116
+ }
117
+ return out;
118
+ }
119
+ /**
120
+ * Drive every frame of `project` (or of `frames`) into `sink`, then mix audio and finalize.
121
+ *
122
+ * The caller owns the render context and its surface — this neither creates nor
123
+ * disposes them, because how a surface comes to exist is exactly the thing that
124
+ * differs between backends.
125
+ */
126
+ export async function renderTimeline(params) {
127
+ const { scale = 1, sink, mixer, onProgress, signal, includeAudio = true } = params;
128
+ const harness = params;
129
+ const { renderContext, storageAdapter } = harness;
130
+ if (params.project.tracks.length === 0)
131
+ return;
132
+ signal?.throwIfAborted();
133
+ renderContext.pixelRatio = scale;
134
+ const { store, precomper, stateEvaluator, measurer, fps, viewport, loads, applyTheme } = prepare(params);
135
+ const restorePolicy = exactDecodes(storageAdapter);
136
+ const seen = new Set();
137
+ try {
138
+ // Not this render's: a device lost before it started was recovered from before it draws.
139
+ renderContext.takeRenderIssues();
140
+ const { start, end } = checkedRange(params.frames, precomper.result().totalFrames);
141
+ const trackIds = params.frames
142
+ ? store.spans.filter((span) => span.startFrame < end && span.endFrame >= start).map((span) => span.id)
143
+ : params.project.tracks.map((track) => track.id);
144
+ if (params.frames)
145
+ for (const id of trackIds)
146
+ store.discover(id);
147
+ const precomp = params.frames ? precomper.result() : precomper.run();
148
+ const { totalDuration } = precomp;
149
+ const frameCount = end - start;
150
+ // Before the sink starts, so a refused export encodes nothing.
151
+ assertDrawable(store, precomp, start, end - 1);
152
+ const assetManager = loads.manager = new AssetManager(precomp, storageAdapter, new NoopAudioSink());
153
+ await settleLayoutGates(store, assetManager, trackIds);
154
+ const scheduled = collectAudio(precomp);
155
+ const wantsAudio = includeAudio && !params.frames && scheduled.length > 0 && mixer !== undefined;
156
+ await sink.start({
157
+ width: viewport.width * scale,
158
+ height: viewport.height * scale,
159
+ fps,
160
+ totalFrames: frameCount,
161
+ hasAudio: wantsAudio,
162
+ });
163
+ const frameDuration = 1 / fps;
164
+ let globalTime = 0;
165
+ // An export is the most perfectly monotonic forward walk of a timeline there
166
+ // is — exactly the access pattern an adapter's look-ahead decode window is
167
+ // built for. Left at the default (`false`, i.e. "paused") every video frame
168
+ // instead takes the random-access branch: a seek + decode the render has to
169
+ // wait on, followed by a *second* full render of the frame once it lands (see
170
+ // `renderWarmFrame`). Telling the adapter we are playing turns that into one
171
+ // sequential decode pass per clip.
172
+ //
173
+ // Restored in the `finally` so a cancelled export doesn't leave an adapter the
174
+ // caller still owns believing a playhead is running.
175
+ storageAdapter.setPlaying(true);
176
+ try {
177
+ for (let f = start; f < end; f++) {
178
+ signal?.throwIfAborted();
179
+ await assetManager.loadAt(f);
180
+ applyTheme();
181
+ stateEvaluator.stateAt(f);
182
+ stateEvaluator.layout(measurer);
183
+ await renderWarmFrame(harness, () => stateEvaluator.render(renderContext), f);
184
+ drainIssues(renderContext, seen, params.onIssue);
185
+ await sink.addFrame(globalTime, frameDuration);
186
+ globalTime += frameDuration;
187
+ const done = f - start + 1;
188
+ // Video occupies most of the range; the audio mix and finalize are the tail.
189
+ onProgress?.((done / frameCount) * (wantsAudio ? 0.85 : 1));
190
+ // Hand the host back its event loop periodically so a long export stays
191
+ // interruptible. Core's yield is feature-detected and prefers a real
192
+ // macrotask over a clamped setTimeout, in the browser and in Node alike.
193
+ if ((f + 1) % 4 === 0)
194
+ await yieldToScheduler();
195
+ }
196
+ }
197
+ finally {
198
+ storageAdapter.setPlaying(false);
199
+ }
200
+ if (wantsAudio) {
201
+ const mixed = await mixer.mix(scheduled, totalDuration);
202
+ if (mixed !== null && sink.addAudio)
203
+ await sink.addAudio(mixed);
204
+ onProgress?.(0.97);
205
+ }
206
+ // The last frame's device may have been lost after it was drawn.
207
+ drainIssues(renderContext, seen, params.onIssue);
208
+ const bytes = await sink.finalize();
209
+ onProgress?.(1);
210
+ return bytes;
211
+ }
212
+ finally {
213
+ restorePolicy();
214
+ stateEvaluator.dispose();
215
+ store.dispose();
216
+ }
217
+ }
218
+ /**
219
+ * Every track covering {@link frame} failed to build. Drawn anyway, it would be
220
+ * a blank frame — the consequence, with the cause hidden — so a still and an
221
+ * export both refuse it.
222
+ */
223
+ export class NothingToDrawError extends Error {
224
+ frame;
225
+ failures;
226
+ name = "NothingToDrawError";
227
+ constructor(frame,
228
+ /** One per track covering the frame. */
229
+ failures) {
230
+ const causes = failures.map((failure) => ` ${failure.trackId}: ${failure.message}`).join("\n");
231
+ super(`Frame ${frame} has nothing to draw — every track on it failed to build:\n${causes}`);
232
+ this.frame = frame;
233
+ this.failures = failures;
234
+ }
235
+ }
236
+ /**
237
+ * Draw one frame of `project` into the caller's render context and leave it on
238
+ * the surface.
239
+ *
240
+ * Same discovery/evaluator/warm-retry path as {@link renderTimeline}, so a still
241
+ * is the same render the video would produce at that frame — and only the tracks
242
+ * on that frame are discovered, since a track's span is known without it.
243
+ *
244
+ * The drawing is shared while what happens to the frame varies: a still encodes
245
+ * it, a live preview leaves it on a visible canvas. The context is the
246
+ * caller's throughout — this neither creates, resizes nor disposes it — so a host
247
+ * holding one surface can call this repeatedly and keep both the surface and the
248
+ * storage adapter's decode cache warm.
249
+ */
250
+ export async function drawFrameAt(params) {
251
+ const { scale = 1, frame, project } = params;
252
+ const harness = params;
253
+ const { renderContext, storageAdapter } = harness;
254
+ if (project.tracks.length === 0)
255
+ throw new Error("The project has no tracks to render.");
256
+ renderContext.pixelRatio = scale;
257
+ const { store, precomper, stateEvaluator, measurer, loads } = prepare(params);
258
+ const restorePolicy = exactDecodes(storageAdapter);
259
+ try {
260
+ const { totalFrames } = precomper.result();
261
+ const requested = frame.kind === "first" ? 0
262
+ : frame.kind === "last" ? totalFrames - 1
263
+ : frame.frame;
264
+ const targetFrame = Math.max(0, Math.min(totalFrames - 1, Math.floor(requested)));
265
+ const precomp = precomper.ensureCovering(targetFrame);
266
+ assertDrawable(store, precomp, targetFrame);
267
+ const assetManager = loads.manager = new AssetManager(precomp, storageAdapter, new NoopAudioSink());
268
+ await settleLayoutGates(store, assetManager, spansCovering(store.spans, targetFrame).map((span) => span.id));
269
+ await assetManager.loadAt(targetFrame);
270
+ stateEvaluator.stateAt(targetFrame);
271
+ stateEvaluator.layout(measurer);
272
+ renderContext.takeRenderIssues();
273
+ await renderWarmFrame(harness, () => stateEvaluator.render(renderContext), targetFrame);
274
+ drainIssues(renderContext, new Set(), params.onIssue);
275
+ return { frame: targetFrame, totalFrames };
276
+ }
277
+ finally {
278
+ restorePolicy();
279
+ // The pass flushed the surface before returning, so the pixels survive the
280
+ // tree being torn down here. In `finally` so a throw mid-draw doesn't leak
281
+ // a live scene graph.
282
+ stateEvaluator.dispose();
283
+ store.dispose();
284
+ }
285
+ }
286
+ /**
287
+ * Throw a {@link NothingToDrawError} for the first frame in `first..last` whose
288
+ * every track failed to build. Only a build that threw counts: `buildErrors` also
289
+ * carries a node's failed declaration, and a track with one still draws.
290
+ */
291
+ function assertDrawable(store, precomp, first, last = first) {
292
+ const failed = new Map(precomp.buildErrors
293
+ .filter((error) => store.failed(error.trackId))
294
+ .map((error) => [error.trackId, error]));
295
+ if (failed.size === 0)
296
+ return;
297
+ for (let frame = first; frame <= last; frame++) {
298
+ const covering = spansCovering(store.spans, frame);
299
+ if (covering.length > 0 && covering.every((span) => failed.has(span.id))) {
300
+ throw new NothingToDrawError(frame, covering.map((span) => failed.get(span.id)));
301
+ }
302
+ }
303
+ }
304
+ //# sourceMappingURL=frame-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-loop.js","sourceRoot":"","sources":["../src/frame-loop.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AssetCatalog, AssetManager, AudioSink, type AudioRequest, type BuildError, type CanvasRenderContext2D, ClipStore, MeasureContext2D,\n AssetTimeline, type AssetTimelineResult, type FrameRange, type ProjectDocument, type Registry,\n isFatalRenderIssue, RenderBackendError, type RenderIssue, renderIssueKey, renderSettled, SETTLE_PASSES, setTheme, toRenderIssue,\n type FrameSpec, type Size2D, spansCovering, StateEvaluator, type StorageAdapter, type Theme, yieldToScheduler,\n} from \"@motionscript/core\";\n\n/**\n * Rendering a timeline is portable; *encoding* it is not.\n *\n * Everything about driving the frames — discovery, state evaluation, asset\n * windows, the bounded warm-and-re-render retry that makes a frame accurate,\n * audio scheduling, progress, cancellation — is the same work whatever the\n * output format. Only the codec differs, so it is injected as a `VideoFrameSink`\n * (exporter.ts: mediabunny over WebCodecs).\n */\n\n/** Image formats a still can be encoded to. */\nexport type ScreenshotFormat = \"png\" | \"jpeg\";\n\nexport type { FrameSpec };\n\n/** One scheduled audio request, with the absolute time its track starts at. */\nexport interface ScheduledAudioRequest {\n request: AudioRequest;\n /**\n * Seconds to add to the request's own times, which are clip-relative: its\n * track's start. Getting it wrong produces an export that looks right and\n * drifts out of sync, which no pixel comparison catches.\n */\n globalOffset: number;\n}\n\n/**\n * Where finished frames go.\n *\n * `TAudio` ties the sink to its {@link AudioMixer}: what a mixer produces is\n * platform-specific (a Web Audio `AudioBuffer`, interleaved PCM, a temp file) and\n * only its own sink consumes it, so this module relays it without naming it.\n */\nexport interface VideoFrameSink<TAudio = unknown> {\n /**\n * Called once before the first frame, with the resolved output geometry.\n *\n * `hasAudio` is here rather than left for the sink to infer because a muxer\n * generally has to declare its tracks before writing any, and whether the\n * timeline schedules any audio is only known after discovery — which happens\n * inside the orchestrator, after the sink was constructed.\n */\n start(info: {\n width: number;\n height: number;\n fps: number;\n totalFrames: number;\n hasAudio: boolean;\n }): Promise<void>;\n\n /**\n * One frame has been drawn and the surface flushed. Times are in seconds.\n *\n * **Deliberately does not receive pixels.** A browser sink wraps the canvas\n * the renderer already drew into and lets WebCodecs pull from it directly, with no\n * readback; handing a buffer here instead would force a full GPU→CPU\n * `readPixels` on every frame of every export. A sink that *does* need bytes\n * (ffmpeg over stdin) reads them itself, because it holds the render target it\n * was constructed with. Do not \"simplify\" this signature.\n */\n addFrame(timestamp: number, duration: number): Promise<void>;\n\n /** Mixed audio for the whole timeline, if any was produced. */\n addAudio?(mixed: TAudio): Promise<void>;\n\n /** Finish the container. Returns encoded bytes, or nothing if the sink delivered them itself. */\n finalize(): Promise<Uint8Array | void>;\n}\n\n/** Mixes every scheduled request into one timeline-length buffer. */\nexport interface AudioMixer<TAudio = unknown> {\n mix(requests: readonly ScheduledAudioRequest[], totalDuration: number): Promise<TAudio | null>;\n}\n\n/**\n * Audio is mixed offline at the end of a run (or irrelevant, for a still), so the\n * `AssetManager` just needs an `AudioSink`-shaped sink that does nothing during\n * the per-frame pass.\n */\nexport class NoopAudioSink extends AudioSink {\n /** Nothing here records a request, so `AssetManager` can skip the whole sync. */\n override get schedulesAudio(): boolean { return false; }\n\n has(_src: string): boolean { return true; }\n async append(_src: string, _data: ArrayBuffer): Promise<void> { }\n retain(_keep: ReadonlySet<string>): void { }\n schedule(_requests: readonly AudioRequest[]): void { }\n syncTo(_clipTime: number): void { }\n play(_time: number, _speed: number, _reverse: boolean): void { }\n stop(): void { }\n}\n\n/** The platform pieces both entry points need. */\n/**\n * What the frame loop draws with. Any backend's context and adapter: the loop\n * uses only core's base members, so another renderer exports through it too.\n */\ninterface RenderHarness {\n renderContext: CanvasRenderContext2D & { pixelRatio: number };\n /** The same adapter the context was built with. */\n storageAdapter: StorageAdapter;\n assetCatalog: AssetCatalog;\n}\n\n/** Fields shared by the video and still entry points. */\ninterface CommonParams extends RenderHarness {\n /** Its fps and viewport resolved — see `Session`. */\n project: ProjectDocument;\n /** The vocabulary its clips compile against. */\n registry: Registry;\n /** Theme for a project that carries none of its own. */\n theme?: Theme;\n /**\n * What the backend drew but not as the document says (a feature it lacks,\n * a shader that does not compile, a GPU error), once per kind, feature and\n * node. A lost device or a crashed backend is not reported here: the render\n * throws a {@link RenderBackendError} instead.\n */\n onIssue?: (issue: RenderIssue) => void;\n}\n\n/**\n * Hand the context's issues since the last call to `onIssue`, once per key in\n * `seen`, and throw on one that means the frames drawn are not to be trusted.\n */\nfunction drainIssues(renderContext: CanvasRenderContext2D, seen: Set<string>, onIssue?: (issue: RenderIssue) => void): void {\n for (const raw of renderContext.takeRenderIssues()) {\n const issue = toRenderIssue(raw);\n if (isFatalRenderIssue(issue)) throw new RenderBackendError(issue);\n const key = renderIssueKey(issue);\n if (seen.has(key)) continue;\n seen.add(key);\n onIssue?.(issue);\n }\n}\n\n/** Draw one frame, settling and re-rendering until it is accurate. See `renderSettled`. */\nasync function renderWarmFrame(\n { renderContext, storageAdapter }: RenderHarness,\n draw: () => void,\n frame: number,\n): Promise<void> {\n const outcome = await renderSettled(() => renderContext.execute(draw), storageAdapter, () => true);\n if (outcome === \"exhausted\") {\n console.warn(`[export] frame ${frame} still had pending decodes after ${SETTLE_PASSES} renders; it may not be exact.`);\n }\n}\n\n/**\n * Switch the adapter to decoding exactly what each frame draws, so a frame is\n * the same pixels whatever was rendered on this adapter before it. Returns the\n * restore: an adapter lent by a live preview must not come back pinned to it.\n */\nfunction exactDecodes(storageAdapter: StorageAdapter): () => void {\n const previous = storageAdapter.getDecodePolicy();\n storageAdapter.setDecodePolicy(\"exact\");\n return () => storageAdapter.setDecodePolicy(previous);\n}\n\nfunction outputOf(project: ProjectDocument): { fps: number; viewport: Size2D } {\n const { fps, viewport } = project;\n if (fps === undefined || viewport === undefined) {\n throw new Error(\"Rendering takes a project whose fps and viewport are resolved — see Session.\");\n }\n return { fps, viewport };\n}\n\n/** `frames`, or the whole timeline; a range that is empty or leaves the timeline is refused rather than clamped. */\nfunction checkedRange(frames: FrameRange | undefined, totalFrames: number): FrameRange {\n if (!frames) return { start: 0, end: totalFrames };\n const { start, end } = frames;\n if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end <= start || end > totalFrames) {\n throw new RangeError(`Frames ${start} to ${end} are not a range inside this timeline's ${totalFrames} frames.`);\n }\n return { start, end };\n}\n\n/** Build the discovery + evaluator pair both entry points drive, over one store so each track builds once. */\nfunction prepare(params: CommonParams) {\n const { project, registry, assetCatalog, renderContext } = params;\n const { fps, viewport } = outputOf(project);\n // The theme is process-global, so anything else drawing in this page may\n // have replaced it; applied before the first build, which resolves tokens.\n const applyTheme = () => setTheme(project.theme ?? params.theme);\n applyTheme();\n // The render context *is* the measurer — `RenderContext2D` extends\n // `MeasureContext2D` so a node measures and draws through one object. Named\n // rather than cast so the compiler checks it; this package has no test suite\n // to catch it otherwise.\n const measurer: MeasureContext2D = renderContext;\n const scope = { registry, viewport, fps, assets: assetCatalog, measurer };\n const loads: { manager?: AssetManager } = {};\n const store = new ClipStore(project, scope, { isReady: (key) => loads.manager?.isReady(key) ?? false });\n const precomper = new AssetTimeline(project, scope, { store });\n const stateEvaluator = new StateEvaluator(project, scope, store);\n return { store, precomper, stateEvaluator, measurer, fps, viewport, loads, applyTheme };\n}\n\n/**\n * Load the layout gates of `trackIds`, then rebuild any of them whose compile\n * measured a face before it was resident — every build here runs before a\n * single asset has loaded.\n */\nasync function settleLayoutGates(store: ClipStore, manager: AssetManager, trackIds: readonly string[]): Promise<void> {\n const gates = trackIds.flatMap((id) => store.layoutGates(id).filter((record) => !manager.isReady(record.src)));\n if (gates.length > 0) await manager.loadRecords(gates);\n store.settleGates(trackIds);\n}\n\n/**\n * Collect every audio request with the absolute offset it plays at. See\n * {@link ScheduledAudioRequest.globalOffset}.\n *\n * Exported because a **split** export has to mix its audio somewhere other than\n * alongside its frames: the video is rendered in pieces by separate workers, and\n * concatenating separately-encoded AAC would put the encoder's priming delay at\n * every join. The coordinator mixes once over the whole timeline instead — and\n * must schedule it exactly as this does. A second implementation that drifted by\n * a track offset would produce a file that plays perfectly and is out of sync.\n */\nexport function collectAudio(precomp: AssetTimelineResult): ScheduledAudioRequest[] {\n const out: ScheduledAudioRequest[] = [];\n for (const track of precomp.tracks) {\n const globalOffset = track.startFrame / precomp.fps;\n for (const request of track.audioRequests) out.push({ request, globalOffset });\n }\n return out;\n}\n\nexport interface RenderTimelineParams<TAudio> extends CommonParams {\n scale?: number;\n sink: VideoFrameSink<TAudio>;\n mixer?: AudioMixer<TAudio>;\n /**\n * Set false to render a silent file: no mix, and no audio track declared on\n * the container at all.\n *\n * Distinct from muting. The audio is still discovered — the pass that finds\n * it is the one that finds the frames' assets — it simply is not encoded.\n */\n includeAudio?: boolean;\n /**\n * Render only these frames, timestamped from 0, with no audio: one segment of a\n * split export. Only the tracks they cover are discovered.\n */\n frames?: FrameRange;\n onProgress?: (progress: number) => void;\n signal?: AbortSignal;\n}\n\n/**\n * Drive every frame of `project` (or of `frames`) into `sink`, then mix audio and finalize.\n *\n * The caller owns the render context and its surface — this neither creates nor\n * disposes them, because how a surface comes to exist is exactly the thing that\n * differs between backends.\n */\nexport async function renderTimeline<TAudio>(\n params: RenderTimelineParams<TAudio>,\n): Promise<Uint8Array | void> {\n const { scale = 1, sink, mixer, onProgress, signal, includeAudio = true } = params;\n const harness: RenderHarness = params;\n const { renderContext, storageAdapter } = harness;\n\n if (params.project.tracks.length === 0) return;\n signal?.throwIfAborted();\n\n renderContext.pixelRatio = scale;\n const { store, precomper, stateEvaluator, measurer, fps, viewport, loads, applyTheme } = prepare(params);\n const restorePolicy = exactDecodes(storageAdapter);\n const seen = new Set<string>();\n try {\n // Not this render's: a device lost before it started was recovered from before it draws.\n renderContext.takeRenderIssues();\n const { start, end } = checkedRange(params.frames, precomper.result().totalFrames);\n const trackIds = params.frames\n ? store.spans.filter((span) => span.startFrame < end && span.endFrame >= start).map((span) => span.id)\n : params.project.tracks.map((track) => track.id);\n if (params.frames) for (const id of trackIds) store.discover(id);\n const precomp = params.frames ? precomper.result() : precomper.run();\n const { totalDuration } = precomp;\n const frameCount = end - start;\n // Before the sink starts, so a refused export encodes nothing.\n assertDrawable(store, precomp, start, end - 1);\n const assetManager = loads.manager = new AssetManager(precomp, storageAdapter, new NoopAudioSink());\n await settleLayoutGates(store, assetManager, trackIds);\n const scheduled = collectAudio(precomp);\n const wantsAudio = includeAudio && !params.frames && scheduled.length > 0 && mixer !== undefined;\n\n await sink.start({\n width: viewport.width * scale,\n height: viewport.height * scale,\n fps,\n totalFrames: frameCount,\n hasAudio: wantsAudio,\n });\n\n const frameDuration = 1 / fps;\n let globalTime = 0;\n // An export is the most perfectly monotonic forward walk of a timeline there\n // is — exactly the access pattern an adapter's look-ahead decode window is\n // built for. Left at the default (`false`, i.e. \"paused\") every video frame\n // instead takes the random-access branch: a seek + decode the render has to\n // wait on, followed by a *second* full render of the frame once it lands (see\n // `renderWarmFrame`). Telling the adapter we are playing turns that into one\n // sequential decode pass per clip.\n //\n // Restored in the `finally` so a cancelled export doesn't leave an adapter the\n // caller still owns believing a playhead is running.\n storageAdapter.setPlaying(true);\n try {\n for (let f = start; f < end; f++) {\n signal?.throwIfAborted();\n\n await assetManager.loadAt(f);\n applyTheme();\n stateEvaluator.stateAt(f);\n stateEvaluator.layout(measurer);\n await renderWarmFrame(harness, () => stateEvaluator.render(renderContext), f);\n drainIssues(renderContext, seen, params.onIssue);\n\n await sink.addFrame(globalTime, frameDuration);\n globalTime += frameDuration;\n const done = f - start + 1;\n // Video occupies most of the range; the audio mix and finalize are the tail.\n onProgress?.((done / frameCount) * (wantsAudio ? 0.85 : 1));\n\n // Hand the host back its event loop periodically so a long export stays\n // interruptible. Core's yield is feature-detected and prefers a real\n // macrotask over a clamped setTimeout, in the browser and in Node alike.\n if ((f + 1) % 4 === 0) await yieldToScheduler();\n }\n } finally {\n storageAdapter.setPlaying(false);\n }\n\n if (wantsAudio) {\n const mixed = await mixer!.mix(scheduled, totalDuration);\n if (mixed !== null && sink.addAudio) await sink.addAudio(mixed);\n onProgress?.(0.97);\n }\n\n // The last frame's device may have been lost after it was drawn.\n drainIssues(renderContext, seen, params.onIssue);\n const bytes = await sink.finalize();\n onProgress?.(1);\n return bytes;\n } finally {\n restorePolicy();\n stateEvaluator.dispose();\n store.dispose();\n }\n}\n\nexport interface DrawFrameParams extends CommonParams {\n scale?: number;\n frame: FrameSpec;\n}\n\nexport interface DrawnFrame {\n /** The frame actually drawn, after clamping the request into range. */\n frame: number;\n totalFrames: number;\n}\n\n/**\n * Every track covering {@link frame} failed to build. Drawn anyway, it would be\n * a blank frame — the consequence, with the cause hidden — so a still and an\n * export both refuse it.\n */\nexport class NothingToDrawError extends Error {\n override readonly name = \"NothingToDrawError\";\n\n constructor(\n readonly frame: number,\n /** One per track covering the frame. */\n readonly failures: readonly BuildError[],\n ) {\n const causes = failures.map((failure) => ` ${failure.trackId}: ${failure.message}`).join(\"\\n\");\n super(`Frame ${frame} has nothing to draw — every track on it failed to build:\\n${causes}`);\n }\n}\n\n/**\n * Draw one frame of `project` into the caller's render context and leave it on\n * the surface.\n *\n * Same discovery/evaluator/warm-retry path as {@link renderTimeline}, so a still\n * is the same render the video would produce at that frame — and only the tracks\n * on that frame are discovered, since a track's span is known without it.\n *\n * The drawing is shared while what happens to the frame varies: a still encodes\n * it, a live preview leaves it on a visible canvas. The context is the\n * caller's throughout — this neither creates, resizes nor disposes it — so a host\n * holding one surface can call this repeatedly and keep both the surface and the\n * storage adapter's decode cache warm.\n */\nexport async function drawFrameAt(params: DrawFrameParams): Promise<DrawnFrame> {\n const { scale = 1, frame, project } = params;\n const harness: RenderHarness = params;\n const { renderContext, storageAdapter } = harness;\n\n if (project.tracks.length === 0) throw new Error(\"The project has no tracks to render.\");\n\n renderContext.pixelRatio = scale;\n const { store, precomper, stateEvaluator, measurer, loads } = prepare(params);\n const restorePolicy = exactDecodes(storageAdapter);\n try {\n const { totalFrames } = precomper.result();\n const requested =\n frame.kind === \"first\" ? 0\n : frame.kind === \"last\" ? totalFrames - 1\n : frame.frame;\n const targetFrame = Math.max(0, Math.min(totalFrames - 1, Math.floor(requested)));\n const precomp = precomper.ensureCovering(targetFrame);\n assertDrawable(store, precomp, targetFrame);\n\n const assetManager = loads.manager = new AssetManager(precomp, storageAdapter, new NoopAudioSink());\n await settleLayoutGates(store, assetManager, spansCovering(store.spans, targetFrame).map((span) => span.id));\n await assetManager.loadAt(targetFrame);\n stateEvaluator.stateAt(targetFrame);\n stateEvaluator.layout(measurer);\n renderContext.takeRenderIssues();\n await renderWarmFrame(harness, () => stateEvaluator.render(renderContext), targetFrame);\n drainIssues(renderContext, new Set(), params.onIssue);\n\n return { frame: targetFrame, totalFrames };\n } finally {\n restorePolicy();\n // The pass flushed the surface before returning, so the pixels survive the\n // tree being torn down here. In `finally` so a throw mid-draw doesn't leak\n // a live scene graph.\n stateEvaluator.dispose();\n store.dispose();\n }\n}\n\n/**\n * Throw a {@link NothingToDrawError} for the first frame in `first..last` whose\n * every track failed to build. Only a build that threw counts: `buildErrors` also\n * carries a node's failed declaration, and a track with one still draws.\n */\nfunction assertDrawable(store: ClipStore, precomp: AssetTimelineResult, first: number, last = first): void {\n const failed = new Map(precomp.buildErrors\n .filter((error) => store.failed(error.trackId))\n .map((error) => [error.trackId, error]));\n if (failed.size === 0) return;\n for (let frame = first; frame <= last; frame++) {\n const covering = spansCovering(store.spans, frame);\n if (covering.length > 0 && covering.every((span) => failed.has(span.id))) {\n throw new NothingToDrawError(frame, covering.map((span) => failed.get(span.id)!));\n }\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,22 +1,24 @@
1
- export { WebRenderContext } from "./render-context";
2
- export { WebStorageAdapter } from "./storage-adapter";
3
- export { setAssetByteSource, type AssetByteSource } from "./asset-bytes";
4
- export { getCanvasKit } from "./getter";
5
- export { EffectRegistry } from "@motionscript/skia-render";
6
- export type { EffectHandler, EffectGeometry, EffectResources, EffectTarget, RenderEffect } from "@motionscript/skia-render";
1
+ export { loadAssetBytes, setAssetByteSource, type AssetByteSource } from "./asset-bytes";
7
2
  export { exportVideo, type ExportParams, type ExportProgressCallback, type VideoEncoderOptions, type AudioEncoderOptions, } from "./exporter";
3
+ export { exportAudio, NoAudioError, type ExportAudioParams } from "./export-audio";
4
+ export { EXPORT_FORMATS, type ExportFormat, type ExportFormatInfo, type VideoFormat, type AudioFormat, } from "./formats";
8
5
  export { QUALITY_VERY_LOW, QUALITY_LOW, QUALITY_MEDIUM, QUALITY_HIGH, QUALITY_VERY_HIGH, type Quality, } from "mediabunny";
9
6
  export { exportScreenshot, type ScreenshotParams, type ScreenshotResult, type ScreenshotFormat, type FrameSpec } from "./screenshot";
10
- export { concatVideoSegments, ConcatMismatchError, type ConcatOptions } from "./concat";
7
+ export { NothingToDrawError } from "./frame-loop";
8
+ export { concatVideoSegments, createSegmentJoiner, ConcatMismatchError, type ConcatOptions, type SegmentJoiner } from "./concat";
9
+ export { exportVideoParallel, type ParallelExportParams } from "./parallel/export-parallel";
10
+ export { localLane, messagePortLane, serveExportLane, type MessagePortLaneOptions } from "./parallel/lanes";
11
+ export type { ExportLane, LaneEnvironment, LaneReport, LaneSession, SegmentSpec } from "./parallel/lane";
12
+ export type { ExportChunk } from "./parallel/plan";
11
13
  export { mixTimelineAudio, type MixTimelineAudioParams } from "./mix-timeline-audio";
12
14
  export { StillRenderer, createStillRenderer } from "./still";
13
15
  export type { StillRendererOptions, RenderStillOptions, EncodeStillOptions, StillSource, FrameRef, } from "./still";
14
16
  export { WebAudioSink as WebAudioPlayer } from "./audio/player";
15
17
  export { mixAudio, encodeWav, type MixAudioOptions } from "./audio/mixer";
16
- export { WebMeasureContext } from "./measure-context";
17
18
  export { WebTicker } from "./ticker";
18
19
  export { createBrowserEngine } from "./engine";
19
20
  export type { BrowserEngineOptions, BrowserEncodeOptions } from "./engine";
20
- export { Canvas3DBackend, canvas3DBackend, loadCanvas3D, disposeCanvas3DBackend } from "@motionscript/skia-render";
21
- export type { Canvas3DAssets, RenderedCanvas3D } from "@motionscript/skia-render";
21
+ export { WebGpuUnavailableError, type RendererWasmOption } from "./renderer";
22
+ export { isWgpuSupported, onWgpuEvent, ShaderPreview, validateShader, WGSL_PRELUDE } from "@motionscript/renderer";
23
+ export type { WgpuEvent, ShaderDiagnostic, ShaderField, ShaderPreviewInput, ShaderReport, ShaderScope, ShaderStatus, } from "@motionscript/renderer";
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC5H,OAAO,EACH,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GAC3B,MAAM,YAAY,CAAC;AAIpB,OAAO,EACH,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,KAAK,OAAO,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAKrI,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAIxF,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAIrF,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EACR,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,QAAQ,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIhE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAMrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAQ3E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnH,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EACH,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GAC3B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EACH,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,WAAW,GACnB,MAAM,WAAW,CAAC;AAInB,OAAO,EACH,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,KAAK,OAAO,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAErI,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKlD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAGjI,OAAO,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC5G,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzG,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAInD,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAIrF,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EACR,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,QAAQ,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACnH,YAAY,EACR,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,GACxG,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -1,25 +1,28 @@
1
- // @motionscript/browser — CanvasKit/Skia-based renderer for @motionscript/core,
2
- // running in the browser (canvas mount, video export, audio playback/clock).
3
- export { WebRenderContext } from "./render-context";
4
- export { WebStorageAdapter } from "./storage-adapter";
1
+ // @motionscript/browser — @motionscript/core in the browser: WebGPU surfaces
2
+ // (through @motionscript/renderer), video export, audio playback and clock.
5
3
  // The seam letting a host answer for an asset's bytes before the adapter goes
6
4
  // to the network — see `asset-bytes.ts` for why it is module-level.
7
- export { setAssetByteSource } from "./asset-bytes";
8
- export { getCanvasKit } from "./getter";
9
- // Re-exported from @motionscript/skia-render, whose canonical home these are —
10
- // kept on this barrel so the specifier consumers already use keeps resolving.
11
- export { EffectRegistry } from "@motionscript/skia-render";
5
+ export { loadAssetBytes, setAssetByteSource } from "./asset-bytes";
12
6
  export { exportVideo, } from "./exporter";
7
+ // Mixes the timeline's audio and encodes it alone, without rendering a frame.
8
+ export { exportAudio, NoAudioError } from "./export-audio";
9
+ export { EXPORT_FORMATS, } from "./formats";
13
10
  // mediabunny's resolution-aware quality levels, re-exported so a host can pick
14
11
  // one for `ExportParams.video.bitrate` without taking a direct dependency on the
15
12
  // encoder library this package happens to use.
16
13
  export { QUALITY_VERY_LOW, QUALITY_LOW, QUALITY_MEDIUM, QUALITY_HIGH, QUALITY_VERY_HIGH, } from "mediabunny";
17
14
  export { exportScreenshot } from "./screenshot";
18
- // Joins separately-encoded MP4s end to end without re-encoding. What makes it
15
+ // Thrown by a still and an export alike for a frame whose every track failed to build.
16
+ export { NothingToDrawError } from "./frame-loop";
17
+ // Joins separately-encoded MP4s (or WebMs) end to end without re-encoding. What makes it
19
18
  // worth rendering an export in parallel at all: the parts still have to become
20
19
  // one file, and re-encoding to join them gives back everything the parallelism
21
20
  // won. See `concat.ts` for the two things a caller has to guarantee.
22
- export { concatVideoSegments, ConcatMismatchError } from "./concat";
21
+ export { concatVideoSegments, createSegmentJoiner, ConcatMismatchError } from "./concat";
22
+ // One export across several renderers: chunks of frames on lanes the run admits
23
+ // while they add throughput, joined as they land. See `parallel/export-parallel.ts`.
24
+ export { exportVideoParallel } from "./parallel/export-parallel";
25
+ export { localLane, messagePortLane, serveExportLane } from "./parallel/lanes";
23
26
  // Mixes a timeline's audio without rendering a frame — the other half of a split
24
27
  // export, whose video arrives in separately-encoded pieces that AAC cannot be
25
28
  // concatenated across. See `mix-timeline-audio.ts`.
@@ -30,43 +33,11 @@ export { mixTimelineAudio } from "./mix-timeline-audio";
30
33
  export { StillRenderer, createStillRenderer } from "./still";
31
34
  export { WebAudioSink as WebAudioPlayer } from "./audio/player";
32
35
  // Mixdown. Also on the `@motionscript/browser/audio` subpath, which is the
33
- // import to prefer — it reaches it without loading CanvasKit, since none of it
34
- // draws.
36
+ // import to prefer — it reaches it without loading the renderer.
35
37
  export { mixAudio, encodeWav } from "./audio/mixer";
36
- export { WebMeasureContext } from "./measure-context";
37
38
  export { WebTicker } from "./ticker";
38
- // Exported from the barrel, never from a subpath of its own: the 3D
39
- // registrations at the bottom of this file are what make `Canvas3D` draw
40
- // anything, and a consumer reaching `createBrowserEngine` without evaluating
41
- // this module would get an engine whose 3D silently renders empty.
42
39
  export { createBrowserEngine } from "./engine";
43
- // ─── 3D ──────────────────────────────────────────────────────────────────────
44
- // The reconciler, handlers and lazy `three` boundary all live in
45
- // @motionscript/skia-render now; this package supplies only the WebGL renderer
46
- // that rasterizes what they build. Re-exported here so the specifiers consumers
47
- // already use keep resolving.
48
- export { Canvas3DBackend, canvas3DBackend, loadCanvas3D, disposeCanvas3DBackend } from "@motionscript/skia-render";
49
- import { registerCanvas3DBackend, registerCanvas3DRendererHost } from "@motionscript/skia-render";
50
- import { webCanvas3DRendererHost } from "./three/renderer";
51
- // Hand core the three-loading hook so the loader `Canvas3D.declareAssets()` declares
52
- // can preload the runtime before any frame draws. Done at module scope rather
53
- // than lazily because core has no way to reach into this package on its own — the
54
- // registration is the seam.
55
- //
56
- // Note this package declares `sideEffects: false`, which is still accurate: the
57
- // call has no observable effect for a 2D-only project (it just stores a function
58
- // reference), and because it lives in the barrel every consumer imports, a
59
- // bundler can't drop it while keeping anything else here.
60
- registerCanvas3DBackend();
61
- // Hand skia-render the WebGL renderer, for the same reason and with the same
62
- // constraint: it MUST be registered from this barrel.
63
- //
64
- // `./three/renderer` is no longer statically imported by anything else in this
65
- // package — the reconciler that used to import it moved out. Combined with
66
- // `sideEffects: false`, a module-scope registration inside that file could be
67
- // tree-shaken away, and the failure is silent: `canvas3DBackend()` returns null
68
- // forever, the warm-and-retry loop exhausts its three passes, and every frame
69
- // ships its 2D parts with no 3D and no error. Registering here keeps the module
70
- // reachable and the behaviour explicit.
71
- registerCanvas3DRendererHost(webCanvas3DRendererHost);
40
+ export { WebGpuUnavailableError } from "./renderer";
41
+ // The renderer's host-facing surface, so a host needs no second import.
42
+ export { isWgpuSupported, onWgpuEvent, ShaderPreview, validateShader, WGSL_PRELUDE } from "@motionscript/renderer";
72
43
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,8EAA8E;AAC9E,oEAAoE;AACpE,OAAO,EAAE,kBAAkB,EAAwB,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,+EAA+E;AAC/E,8EAA8E;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,EACH,WAAW,GAKd,MAAM,YAAY,CAAC;AACpB,+EAA+E;AAC/E,iFAAiF;AACjF,+CAA+C;AAC/C,OAAO,EACH,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,iBAAiB,GAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAuF,MAAM,cAAc,CAAC;AACrI,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAsB,MAAM,UAAU,CAAC;AACxF,iFAAiF;AACjF,8EAA8E;AAC9E,oDAAoD;AACpD,OAAO,EAAE,gBAAgB,EAA+B,MAAM,sBAAsB,CAAC;AACrF,mFAAmF;AACnF,qFAAqF;AACrF,yCAAyC;AACzC,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQ7D,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,2EAA2E;AAC3E,+EAA+E;AAC/E,SAAS;AACT,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAwB,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,oEAAoE;AACpE,yEAAyE;AACzE,6EAA6E;AAC7E,mEAAmE;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG/C,gFAAgF;AAEhF,iEAAiE;AACjE,+EAA+E;AAC/E,gFAAgF;AAChF,8BAA8B;AAC9B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAGnH,OAAO,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,qFAAqF;AACrF,8EAA8E;AAC9E,kFAAkF;AAClF,4BAA4B;AAC5B,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,2EAA2E;AAC3E,0DAA0D;AAC1D,uBAAuB,EAAE,CAAC;AAE1B,6EAA6E;AAC7E,sDAAsD;AACtD,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,wCAAwC;AACxC,4BAA4B,CAAC,uBAAuB,CAAC,CAAC","sourcesContent":["// @motionscript/browser — CanvasKit/Skia-based renderer for @motionscript/core,\n// running in the browser (canvas mount, video export, audio playback/clock).\n\nexport { WebRenderContext } from \"./render-context\";\nexport { WebStorageAdapter } from \"./storage-adapter\";\n// The seam letting a host answer for an asset's bytes before the adapter goes\n// to the network — see `asset-bytes.ts` for why it is module-level.\nexport { setAssetByteSource, type AssetByteSource } from \"./asset-bytes\";\nexport { getCanvasKit } from \"./getter\";\n// Re-exported from @motionscript/skia-render, whose canonical home these are —\n// kept on this barrel so the specifier consumers already use keeps resolving.\nexport { EffectRegistry } from \"@motionscript/skia-render\";\nexport type { EffectHandler, EffectGeometry, EffectResources, EffectTarget, RenderEffect } from \"@motionscript/skia-render\";\nexport {\n exportVideo,\n type ExportParams,\n type ExportProgressCallback,\n type VideoEncoderOptions,\n type AudioEncoderOptions,\n} from \"./exporter\";\n// mediabunny's resolution-aware quality levels, re-exported so a host can pick\n// one for `ExportParams.video.bitrate` without taking a direct dependency on the\n// encoder library this package happens to use.\nexport {\n QUALITY_VERY_LOW,\n QUALITY_LOW,\n QUALITY_MEDIUM,\n QUALITY_HIGH,\n QUALITY_VERY_HIGH,\n type Quality,\n} from \"mediabunny\";\nexport { exportScreenshot, type ScreenshotParams, type ScreenshotResult, type ScreenshotFormat, type FrameSpec } from \"./screenshot\";\n// Joins separately-encoded MP4s end to end without re-encoding. What makes it\n// worth rendering an export in parallel at all: the parts still have to become\n// one file, and re-encoding to join them gives back everything the parallelism\n// won. See `concat.ts` for the two things a caller has to guarantee.\nexport { concatVideoSegments, ConcatMismatchError, type ConcatOptions } from \"./concat\";\n// Mixes a timeline's audio without rendering a frame — the other half of a split\n// export, whose video arrives in separately-encoded pieces that AAC cannot be\n// concatenated across. See `mix-timeline-audio.ts`.\nexport { mixTimelineAudio, type MixTimelineAudioParams } from \"./mix-timeline-audio\";\n// Single frames as a first-class capability: one long-lived renderer that repaints\n// into a canvas the host owns, for thumbnails and still previews. `exportScreenshot`\n// above is the one-shot wrapper over it.\nexport { StillRenderer, createStillRenderer } from \"./still\";\nexport type {\n StillRendererOptions,\n RenderStillOptions,\n EncodeStillOptions,\n StillSource,\n FrameRef,\n} from \"./still\";\nexport { WebAudioSink as WebAudioPlayer } from \"./audio/player\";\n// Mixdown. Also on the `@motionscript/browser/audio` subpath, which is the\n// import to prefer — it reaches it without loading CanvasKit, since none of it\n// draws.\nexport { mixAudio, encodeWav, type MixAudioOptions } from \"./audio/mixer\";\nexport { WebMeasureContext } from \"./measure-context\";\nexport { WebTicker } from \"./ticker\";\n\n// Exported from the barrel, never from a subpath of its own: the 3D\n// registrations at the bottom of this file are what make `Canvas3D` draw\n// anything, and a consumer reaching `createBrowserEngine` without evaluating\n// this module would get an engine whose 3D silently renders empty.\nexport { createBrowserEngine } from \"./engine\";\nexport type { BrowserEngineOptions, BrowserEncodeOptions } from \"./engine\";\n\n// ─── 3D ──────────────────────────────────────────────────────────────────────\n\n// The reconciler, handlers and lazy `three` boundary all live in\n// @motionscript/skia-render now; this package supplies only the WebGL renderer\n// that rasterizes what they build. Re-exported here so the specifiers consumers\n// already use keep resolving.\nexport { Canvas3DBackend, canvas3DBackend, loadCanvas3D, disposeCanvas3DBackend } from \"@motionscript/skia-render\";\nexport type { Canvas3DAssets, RenderedCanvas3D } from \"@motionscript/skia-render\";\n\nimport { registerCanvas3DBackend, registerCanvas3DRendererHost } from \"@motionscript/skia-render\";\nimport { webCanvas3DRendererHost } from \"./three/renderer\";\n\n// Hand core the three-loading hook so the loader `Canvas3D.declareAssets()` declares\n// can preload the runtime before any frame draws. Done at module scope rather\n// than lazily because core has no way to reach into this package on its own — the\n// registration is the seam.\n//\n// Note this package declares `sideEffects: false`, which is still accurate: the\n// call has no observable effect for a 2D-only project (it just stores a function\n// reference), and because it lives in the barrel every consumer imports, a\n// bundler can't drop it while keeping anything else here.\nregisterCanvas3DBackend();\n\n// Hand skia-render the WebGL renderer, for the same reason and with the same\n// constraint: it MUST be registered from this barrel.\n//\n// `./three/renderer` is no longer statically imported by anything else in this\n// package — the reconciler that used to import it moved out. Combined with\n// `sideEffects: false`, a module-scope registration inside that file could be\n// tree-shaken away, and the failure is silent: `canvas3DBackend()` returns null\n// forever, the warm-and-retry loop exhausts its three passes, and every frame\n// ships its 2D parts with no 3D and no error. Registering here keeps the module\n// reachable and the behaviour explicit.\nregisterCanvas3DRendererHost(webCanvas3DRendererHost);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAE5E,8EAA8E;AAC9E,oEAAoE;AACpE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAwB,MAAM,eAAe,CAAC;AACzF,OAAO,EACH,WAAW,GAKd,MAAM,YAAY,CAAC;AACpB,8EAA8E;AAC9E,OAAO,EAAE,WAAW,EAAE,YAAY,EAA0B,MAAM,gBAAgB,CAAC;AACnF,OAAO,EACH,cAAc,GAKjB,MAAM,WAAW,CAAC;AACnB,+EAA+E;AAC/E,iFAAiF;AACjF,+CAA+C;AAC/C,OAAO,EACH,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,iBAAiB,GAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAuF,MAAM,cAAc,CAAC;AACrI,uFAAuF;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,yFAAyF;AACzF,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAA0C,MAAM,UAAU,CAAC;AACjI,gFAAgF;AAChF,qFAAqF;AACrF,OAAO,EAAE,mBAAmB,EAA6B,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAC;AAG5G,iFAAiF;AACjF,8EAA8E;AAC9E,oDAAoD;AACpD,OAAO,EAAE,gBAAgB,EAA+B,MAAM,sBAAsB,CAAC;AACrF,mFAAmF;AACnF,qFAAqF;AACrF,yCAAyC;AACzC,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQ7D,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,2EAA2E;AAC3E,iEAAiE;AACjE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAwB,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,sBAAsB,EAA2B,MAAM,YAAY,CAAC;AAE7E,wEAAwE;AACxE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC","sourcesContent":["// @motionscript/browser — @motionscript/core in the browser: WebGPU surfaces\n// (through @motionscript/renderer), video export, audio playback and clock.\n\n// The seam letting a host answer for an asset's bytes before the adapter goes\n// to the network — see `asset-bytes.ts` for why it is module-level.\nexport { loadAssetBytes, setAssetByteSource, type AssetByteSource } from \"./asset-bytes\";\nexport {\n exportVideo,\n type ExportParams,\n type ExportProgressCallback,\n type VideoEncoderOptions,\n type AudioEncoderOptions,\n} from \"./exporter\";\n// Mixes the timeline's audio and encodes it alone, without rendering a frame.\nexport { exportAudio, NoAudioError, type ExportAudioParams } from \"./export-audio\";\nexport {\n EXPORT_FORMATS,\n type ExportFormat,\n type ExportFormatInfo,\n type VideoFormat,\n type AudioFormat,\n} from \"./formats\";\n// mediabunny's resolution-aware quality levels, re-exported so a host can pick\n// one for `ExportParams.video.bitrate` without taking a direct dependency on the\n// encoder library this package happens to use.\nexport {\n QUALITY_VERY_LOW,\n QUALITY_LOW,\n QUALITY_MEDIUM,\n QUALITY_HIGH,\n QUALITY_VERY_HIGH,\n type Quality,\n} from \"mediabunny\";\nexport { exportScreenshot, type ScreenshotParams, type ScreenshotResult, type ScreenshotFormat, type FrameSpec } from \"./screenshot\";\n// Thrown by a still and an export alike for a frame whose every track failed to build.\nexport { NothingToDrawError } from \"./frame-loop\";\n// Joins separately-encoded MP4s (or WebMs) end to end without re-encoding. What makes it\n// worth rendering an export in parallel at all: the parts still have to become\n// one file, and re-encoding to join them gives back everything the parallelism\n// won. See `concat.ts` for the two things a caller has to guarantee.\nexport { concatVideoSegments, createSegmentJoiner, ConcatMismatchError, type ConcatOptions, type SegmentJoiner } from \"./concat\";\n// One export across several renderers: chunks of frames on lanes the run admits\n// while they add throughput, joined as they land. See `parallel/export-parallel.ts`.\nexport { exportVideoParallel, type ParallelExportParams } from \"./parallel/export-parallel\";\nexport { localLane, messagePortLane, serveExportLane, type MessagePortLaneOptions } from \"./parallel/lanes\";\nexport type { ExportLane, LaneEnvironment, LaneReport, LaneSession, SegmentSpec } from \"./parallel/lane\";\nexport type { ExportChunk } from \"./parallel/plan\";\n// Mixes a timeline's audio without rendering a frame — the other half of a split\n// export, whose video arrives in separately-encoded pieces that AAC cannot be\n// concatenated across. See `mix-timeline-audio.ts`.\nexport { mixTimelineAudio, type MixTimelineAudioParams } from \"./mix-timeline-audio\";\n// Single frames as a first-class capability: one long-lived renderer that repaints\n// into a canvas the host owns, for thumbnails and still previews. `exportScreenshot`\n// above is the one-shot wrapper over it.\nexport { StillRenderer, createStillRenderer } from \"./still\";\nexport type {\n StillRendererOptions,\n RenderStillOptions,\n EncodeStillOptions,\n StillSource,\n FrameRef,\n} from \"./still\";\nexport { WebAudioSink as WebAudioPlayer } from \"./audio/player\";\n// Mixdown. Also on the `@motionscript/browser/audio` subpath, which is the\n// import to prefer — it reaches it without loading the renderer.\nexport { mixAudio, encodeWav, type MixAudioOptions } from \"./audio/mixer\";\nexport { WebTicker } from \"./ticker\";\n\nexport { createBrowserEngine } from \"./engine\";\nexport type { BrowserEngineOptions, BrowserEncodeOptions } from \"./engine\";\nexport { WebGpuUnavailableError, type RendererWasmOption } from \"./renderer\";\n\n// The renderer's host-facing surface, so a host needs no second import.\nexport { isWgpuSupported, onWgpuEvent, ShaderPreview, validateShader, WGSL_PRELUDE } from \"@motionscript/renderer\";\nexport type {\n WgpuEvent, ShaderDiagnostic, ShaderField, ShaderPreviewInput, ShaderReport, ShaderScope, ShaderStatus,\n} from \"@motionscript/renderer\";\n"]}
@@ -1,7 +1,8 @@
1
- import { Registry, type AssetManifest, type ProjectDocument, type SceneDocument, type Size2D } from "@motionscript/core";
2
- export interface MixTimelineAudioParams {
1
+ import { Registry, type AssetManifest, type ProjectDocument, type VisualDocument, type Size2D } from "@motionscript/core";
2
+ import { type RendererWasmOption } from "./renderer";
3
+ export interface MixTimelineAudioParams extends RendererWasmOption {
3
4
  /** A project, or a lone still or composition as the one track of one. */
4
- document: ProjectDocument | SceneDocument;
5
+ document: ProjectDocument | VisualDocument;
5
6
  /** The vocabulary its clips compile against. Defaults to what the library ships. */
6
7
  registry?: Registry;
7
8
  /** Win over the document's own; 1920×1080 at 60 fps when neither says. */
@@ -10,7 +11,6 @@ export interface MixTimelineAudioParams {
10
11
  manifest?: AssetManifest;
11
12
  /** Mixdown rate. Defaults to the mixer's own (44100). */
12
13
  sampleRate?: number;
13
- wasmUrl?: string;
14
14
  }
15
15
  /**
16
16
  * Mix a timeline's audio without rendering a single frame.
@@ -34,9 +34,9 @@ export interface MixTimelineAudioParams {
34
34
  * that puts its nodes in the tree, so there is no way to know what plays when
35
35
  * without it.
36
36
  *
37
- * Nothing is drawn, so this takes a {@link WebMeasureContext} rather than a render
38
- * context: discovery lays each clip out, which needs text measured, not
39
- * rasterized. No canvas, no surface, no GPU.
37
+ * Nothing is drawn, so this takes the renderer's measure context rather than a
38
+ * render context: discovery lays each clip out, which needs text measured, not
39
+ * rasterized. No canvas, no surface, no GPU, so WebGPU is not required.
40
40
  */
41
41
  export declare function mixTimelineAudio(params: MixTimelineAudioParams): Promise<AudioBuffer | null>;
42
42
  //# sourceMappingURL=mix-timeline-audio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mix-timeline-audio.d.ts","sourceRoot":"","sources":["../src/mix-timeline-audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,QAAQ,EAER,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,MAAM,EACd,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,sBAAsB;IACnC,yEAAyE;IACzE,QAAQ,EAAE,eAAe,GAAG,aAAa,CAAC;IAC1C,oFAAoF;IACpF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CAClC,MAAM,EAAE,sBAAsB,GAC/B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA4B7B"}
1
+ {"version":3,"file":"mix-timeline-audio.d.ts","sourceRoot":"","sources":["../src/mix-timeline-audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,QAAQ,EAER,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,MAAM,EACd,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAC9D,yEAAyE;IACzE,QAAQ,EAAE,eAAe,GAAG,cAAc,CAAC;IAC3C,oFAAoF;IACpF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CAClC,MAAM,EAAE,sBAAsB,GAC/B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA4B7B"}