@motionscript/browser 0.1.0-alpha.3 → 0.1.0-alpha.5
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.
- package/CHANGELOG.md +95 -0
- package/LICENSE +201 -201
- package/NOTICE +48 -0
- package/README.md +118 -69
- package/dist/asset-bytes.js.map +1 -1
- package/dist/audio/bus-graph.d.ts +2 -2
- package/dist/audio/bus-graph.d.ts.map +1 -1
- package/dist/audio/bus-graph.js +6 -6
- package/dist/audio/bus-graph.js.map +1 -1
- package/dist/audio/filter-graph.d.ts +5 -5
- package/dist/audio/filter-graph.d.ts.map +1 -1
- package/dist/audio/filter-graph.js +15 -15
- package/dist/audio/filter-graph.js.map +1 -1
- package/dist/audio/index.js.map +1 -1
- package/dist/audio/loop-region.d.ts +31 -0
- package/dist/audio/loop-region.d.ts.map +1 -0
- package/dist/audio/loop-region.js +43 -0
- package/dist/audio/loop-region.js.map +1 -0
- package/dist/audio/mixer.d.ts.map +1 -1
- package/dist/audio/mixer.js +26 -10
- package/dist/audio/mixer.js.map +1 -1
- package/dist/audio/player.d.ts +4 -4
- package/dist/audio/player.d.ts.map +1 -1
- package/dist/audio/player.js +15 -14
- package/dist/audio/player.js.map +1 -1
- package/dist/browser/THIRD_PARTY_LICENSES.txt +432 -0
- package/dist/browser/audio.js +1 -1
- package/dist/browser/chunks/chunk-2MAFJWBZ.js +2 -0
- package/dist/browser/chunks/chunk-2MAFJWBZ.js.map +7 -0
- package/dist/browser/index.js +130 -276
- package/dist/browser/index.js.map +4 -4
- package/dist/browser/manifest.json +19 -2
- package/dist/concat.js.map +1 -1
- package/dist/decode/image-levels.js.map +1 -1
- package/dist/decode/levels.js.map +1 -1
- package/dist/decode/video-levels.js.map +1 -1
- package/dist/dynamic-nodes.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/exporter.d.ts +8 -2
- package/dist/exporter.d.ts.map +1 -1
- package/dist/exporter.js +8 -7
- package/dist/exporter.js.map +1 -1
- package/dist/getter.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/measure-context.js.map +1 -1
- package/dist/mix-timeline-audio.d.ts +2 -2
- package/dist/mix-timeline-audio.d.ts.map +1 -1
- package/dist/mix-timeline-audio.js +1 -2
- package/dist/mix-timeline-audio.js.map +1 -1
- package/dist/render-context.js.map +1 -1
- package/dist/screenshot.d.ts +3 -3
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +1 -2
- package/dist/screenshot.js.map +1 -1
- package/dist/still.d.ts +6 -4
- package/dist/still.d.ts.map +1 -1
- package/dist/still.js +12 -9
- package/dist/still.js.map +1 -1
- package/dist/storage-adapter.d.ts.map +1 -1
- package/dist/storage-adapter.js +5 -4
- package/dist/storage-adapter.js.map +1 -1
- package/dist/three/renderer.js.map +1 -1
- package/dist/ticker.d.ts +22 -4
- package/dist/ticker.d.ts.map +1 -1
- package/dist/ticker.js +65 -28
- package/dist/ticker.js.map +1 -1
- package/package.json +7 -6
- package/dist/browser/chunks/chunk-M7XD73ED.js +0 -2
- package/dist/browser/chunks/chunk-M7XD73ED.js.map +0 -7
- package/dist/output.d.ts +0 -7
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -6
- package/dist/output.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motionscript/browser",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.5",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": "dist/browser/index.js",
|
|
6
6
|
"./audio": "dist/browser/audio.js"
|
|
@@ -11,5 +11,22 @@
|
|
|
11
11
|
],
|
|
12
12
|
"assets": {
|
|
13
13
|
"esbuild.wasm": "dist/browser/esbuild.wasm"
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"thirdParty": [
|
|
16
|
+
{
|
|
17
|
+
"name": "esbuild-wasm",
|
|
18
|
+
"version": "0.28.2",
|
|
19
|
+
"license": "MIT"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "mediabunny",
|
|
23
|
+
"version": "1.49.0",
|
|
24
|
+
"license": "MPL-2.0"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "three",
|
|
28
|
+
"version": "0.185.1",
|
|
29
|
+
"license": "MIT"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
15
32
|
}
|
package/dist/concat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concat.js","sourceRoot":"","sources":["../src/concat.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AudioBufferSource,\n BufferSource,\n BufferTarget,\n EncodedPacketSink,\n EncodedVideoPacketSource,\n Input,\n MP4,\n Mp4OutputFormat,\n Output,\n StreamTarget,\n type InputVideoTrack,\n type VideoCodec,\n} from \"mediabunny\";\n\n/**\n * Join encoded MP4 segments end to end, without re-encoding.\n *\n * The point of this is what it *doesn't* do: no decode, no encode, no pixels.\n * Each segment is demuxed to its encoded packets, the packets are re-stamped onto\n * one continuous timeline, and a single muxer writes them into one container. The\n * bytes of every frame survive untouched, so the result is bit-identical to the\n * segments and the whole thing runs at I/O speed rather than encode speed.\n *\n * It exists so an export can be **split across workers**. Rendering a timeline is\n * embarrassingly parallel per scene — each scene's generator is independent — but\n * the output has to be one file, and re-encoding the parts to join them would give\n * back everything the parallelism won (and lose a generation of quality). Joining\n * at the packet level is what makes splitting worth doing.\n *\n * ## What the caller has to guarantee\n *\n * **Every segment must be encoded with an identical configuration** — same codec,\n * same resolution, same bitrate settings. A container carries exactly one decoder\n * configuration for a track, so a segment whose parameter sets differ from the\n * first would be described by a configuration that isn't its own, and decode as\n * garbage from that point on. That is not a theoretical concern: hardware encoders\n * are free to pick different parameter sets for the same nominal settings. This is\n * checked rather than assumed — see {@link ConcatMismatchError} — because the\n * failure is silent corruption rather than an exception.\n *\n * **Every segment must begin with a key frame.** Otherwise its first packets refer\n * back to frames that lived in the previous segment's GOP and no longer precede\n * them. Encoders start with a key frame by default; a caller choosing an explicit\n * `keyFrameInterval` needs it to divide the segment length.\n *\n * Audio is *muxed* here but never *joined*: the segments' own audio is ignored\n * entirely, and a single already-mixed buffer is encoded once across the whole\n * result (see {@link ConcatOptions.audio}). Concatenating separately-encoded AAC\n * would put the encoder's priming delay at every join — a click at each boundary\n * and a track that drifts further out of sync with every one — so the timeline's\n * audio is mixed in one pass by whoever owns the timeline, and arrives here as\n * one buffer.\n */\n\n/** Thrown when segments can't be joined losslessly. Carries which segment disagreed. */\nexport class ConcatMismatchError extends Error {\n constructor(\n /** Index of the offending segment in the input array. */\n readonly segment: number,\n message: string,\n ) {\n super(message);\n this.name = \"ConcatMismatchError\";\n }\n}\n\n/** One segment's demuxed video track, plus the config that describes it. */\ninterface Segment {\n track: InputVideoTrack;\n config: VideoDecoderConfig;\n duration: number;\n}\n\n/**\n * Two decoder configurations describe the same bitstream.\n *\n * `description` holds the codec's parameter sets (the `avcC` box for H.264) and is\n * the field that actually decides decodability — a differing one is exactly the\n * silent-corruption case. Compared bytewise; the rest are compared by value.\n */\nfunction sameDecoderConfig(a: VideoDecoderConfig, b: VideoDecoderConfig): boolean {\n if (a.codec !== b.codec) return false;\n if (a.codedWidth !== b.codedWidth) return false;\n if (a.codedHeight !== b.codedHeight) return false;\n\n const da = toBytes(a.description);\n const db = toBytes(b.description);\n if (da === null || db === null) return da === db;\n if (da.length !== db.length) return false;\n for (let i = 0; i < da.length; i++) {\n if (da[i] !== db[i]) return false;\n }\n return true;\n}\n\nfunction toBytes(description: VideoDecoderConfig[\"description\"]): Uint8Array | null {\n if (!description) return null;\n // `description` is a BufferSource: an ArrayBuffer, a SharedArrayBuffer, or any\n // view over one. Views carry an offset into a larger buffer, so they must be\n // narrowed rather than wrapped whole.\n if (ArrayBuffer.isView(description)) {\n return new Uint8Array(description.buffer, description.byteOffset, description.byteLength);\n }\n return new Uint8Array(description);\n}\n\nexport interface ConcatOptions {\n /**\n * One continuous audio track for the whole joined timeline.\n *\n * Mixed once by the caller (see `mixTimelineAudio`) rather than per segment,\n * and that is not a convenience: separately-encoded AAC carries an encoder\n * priming delay at the head of each piece, so joining them puts a click at\n * every scene boundary and drifts the track further out of sync with each\n * one. One buffer in, one encode, no seams.\n */\n audio?: AudioBuffer | null;\n /** AAC bitrate for that track. Defaults to 192 kbps. */\n audioBitrate?: number;\n /**\n * Write the result out as it is muxed instead of returning it.\n *\n * With this the joined file never exists in memory as one buffer, which is\n * the difference between an export the process can hold and one it can't:\n * a 4× 60fps render is hundreds of megabytes, and the default path costs\n * roughly twice that at the tail once the `Blob` copy is counted.\n *\n * **Chunks carry a byte `position` and do not arrive in file order** — a\n * muxer seeks back to patch atom sizes. A sink that appends will produce a\n * file of exactly the right length whose size fields belong to a different\n * one: unplayable, and indistinguishable from success until it is opened.\n * Honour `position`.\n *\n * `concatVideoSegments` resolves to an empty array when this is used; the\n * bytes went to the sink.\n */\n stream?: WritableStream<{ type: \"write\"; data: Uint8Array; position: number }>;\n}\n\nconst DEFAULT_AUDIO_BITRATE = 192_000;\n\n/**\n * Concatenate `segments` (each a complete MP4) into one MP4.\n *\n * Segments are joined in array order. Returns the muxed bytes.\n *\n * @throws {ConcatMismatchError} if a segment carries no video track, or one whose\n * decoder configuration differs from the first segment's. Callers that can\n * fall back to a single-threaded export should catch this and do so — the\n * alternative is shipping a file that decodes into garbage halfway through.\n */\nexport async function concatVideoSegments(\n segments: Uint8Array[],\n options: ConcatOptions = {},\n): Promise<Uint8Array> {\n if (segments.length === 0) throw new Error(\"concatVideoSegments: no segments\");\n // A lone segment is already the finished file — unless a track has to be added\n // to it, or it has to go to a sink, either of which means muxing after all.\n if (segments.length === 1 && !options.audio && !options.stream) return segments[0];\n\n const opened: Segment[] = [];\n for (let i = 0; i < segments.length; i++) {\n const input = new Input({ source: new BufferSource(segments[i]), formats: [MP4] });\n const track = await input.getPrimaryVideoTrack();\n if (!track) throw new ConcatMismatchError(i, `Segment ${i} has no video track.`);\n const config = await track.getDecoderConfig();\n if (!config) throw new ConcatMismatchError(i, `Segment ${i} has no decoder configuration.`);\n if (opened.length > 0 && !sameDecoderConfig(opened[0].config, config)) {\n throw new ConcatMismatchError(\n i,\n `Segment ${i} was encoded differently from segment 0 (${opened[0].config.codec} `\n + `${opened[0].config.codedWidth}x${opened[0].config.codedHeight} vs ${config.codec} `\n + `${config.codedWidth}x${config.codedHeight}); joining them would decode as garbage.`,\n );\n }\n opened.push({ track, config, duration: await track.computeDuration() });\n }\n\n const target = options.stream\n ? new StreamTarget(options.stream as ConstructorParameters<typeof StreamTarget>[0])\n : new BufferTarget();\n const output = new Output({ format: new Mp4OutputFormat(), target });\n // `codec` comes off the track rather than the decoder config: the latter's\n // `codec` is the full codec *string* (`avc1.64001f`), while the source wants\n // the family.\n const source = new EncodedVideoPacketSource(opened[0].track.codec as VideoCodec);\n output.addVideoTrack(source);\n // Declared before `start()` — a muxer fixes its track list up front, so an\n // audio track added later would have nowhere to go.\n const audioSource = options.audio\n ? new AudioBufferSource({\n codec: \"aac\",\n bitrate: options.audioBitrate ?? DEFAULT_AUDIO_BITRATE,\n })\n : null;\n if (audioSource) output.addAudioTrack(audioSource);\n await output.start();\n\n let offset = 0;\n for (const segment of opened) {\n // The decoder config is handed over with the first packet of each segment.\n // Only the first one can take effect — a container holds one per track —\n // but they have all been checked equal above, so restating it is harmless\n // and keeps the muxer from having to infer anything.\n let first = true;\n for await (const packet of new EncodedPacketSink(segment.track).packets()) {\n await source.add(\n packet.clone({ timestamp: packet.timestamp + offset }),\n first ? { decoderConfig: segment.config } : undefined,\n );\n first = false;\n }\n // Advance by the segment's own duration rather than by the last packet's\n // end: a packet's `duration` is what the container recorded, and summing\n // those would accumulate rounding into a drift across many segments.\n offset += segment.duration;\n }\n\n // After the video packets, so the encode overlaps nothing it has to wait on.\n if (audioSource && options.audio) await audioSource.add(options.audio);\n\n source.close();\n audioSource?.close();\n await output.finalize();\n\n // A streamed result has already gone to the sink; there is nothing to hand\n // back, and materialising it here would undo the point of streaming.\n if (options.stream) return new Uint8Array(0);\n\n const buffer = (target as BufferTarget).buffer;\n if (!buffer) throw new Error(\"concatVideoSegments produced no data\");\n return new Uint8Array(buffer);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"concat.js","sourceRoot":"","sources":["../src/concat.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,EACL,GAAG,EACH,eAAe,EACf,MAAM,EACN,YAAY,GAGf,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,wFAAwF;AACxF,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAG7B;IAFb;IACI,yDAAyD;IAChD,OAAe,EACxB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,YAAO,GAAP,OAAO,CAAQ;QAIxB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACtC,CAAC;CACJ;AASD;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,CAAqB,EAAE,CAAqB;IACnE,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAElD,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAClC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACjD,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAC,WAA8C;IAC3D,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,+EAA+E;IAC/E,6EAA6E;IAC7E,sCAAsC;IACtC,IAAI,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;AACvC,CAAC;AAmCD,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACrC,QAAsB,EACtB,UAAyB,EAAE;IAE3B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC/E,+EAA+E;IAC/E,4EAA4E;IAC5E,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEnF,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,oBAAoB,EAAE,CAAC;QACjD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,mBAAmB,CAAC,CAAC,EAAE,WAAW,CAAC,sBAAsB,CAAC,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,mBAAmB,CAAC,CAAC,EAAE,WAAW,CAAC,gCAAgC,CAAC,CAAC;QAC5F,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,mBAAmB,CACzB,CAAC,EACD,WAAW,CAAC,4CAA4C,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG;kBAC/E,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,OAAO,MAAM,CAAC,KAAK,GAAG;kBACpF,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,0CAA0C,CACzF,CAAC;QACN,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;QACzB,CAAC,CAAC,IAAI,YAAY,CAAC,OAAO,CAAC,MAAuD,CAAC;QACnF,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,2EAA2E;IAC3E,6EAA6E;IAC7E,cAAc;IACd,MAAM,MAAM,GAAG,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAmB,CAAC,CAAC;IACjF,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK;QAC7B,CAAC,CAAC,IAAI,iBAAiB,CAAC;YACpB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,OAAO,CAAC,YAAY,IAAI,qBAAqB;SACzD,CAAC;QACF,CAAC,CAAC,IAAI,CAAC;IACX,IAAI,WAAW;QAAE,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC;QAC3B,2EAA2E;QAC3E,yEAAyE;QACzE,0EAA0E;QAC1E,qDAAqD;QACrD,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,IAAI,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YACxE,MAAM,MAAM,CAAC,GAAG,CACZ,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,GAAG,MAAM,EAAE,CAAC,EACtD,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CACxD,CAAC;YACF,KAAK,GAAG,KAAK,CAAC;QAClB,CAAC;QACD,yEAAyE;QACzE,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,6EAA6E;IAC7E,IAAI,WAAW,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEvE,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,WAAW,EAAE,KAAK,EAAE,CAAC;IACrB,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IAExB,2EAA2E;IAC3E,qEAAqE;IACrE,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAI,MAAuB,CAAC,MAAM,CAAC;IAC/C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACrE,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC","sourcesContent":["import {\r\n AudioBufferSource,\r\n BufferSource,\r\n BufferTarget,\r\n EncodedPacketSink,\r\n EncodedVideoPacketSource,\r\n Input,\r\n MP4,\r\n Mp4OutputFormat,\r\n Output,\r\n StreamTarget,\r\n type InputVideoTrack,\r\n type VideoCodec,\r\n} from \"mediabunny\";\r\n\r\n/**\r\n * Join encoded MP4 segments end to end, without re-encoding.\r\n *\r\n * The point of this is what it *doesn't* do: no decode, no encode, no pixels.\r\n * Each segment is demuxed to its encoded packets, the packets are re-stamped onto\r\n * one continuous timeline, and a single muxer writes them into one container. The\r\n * bytes of every frame survive untouched, so the result is bit-identical to the\r\n * segments and the whole thing runs at I/O speed rather than encode speed.\r\n *\r\n * It exists so an export can be **split across workers**. Rendering a timeline is\r\n * embarrassingly parallel per scene — each scene's generator is independent — but\r\n * the output has to be one file, and re-encoding the parts to join them would give\r\n * back everything the parallelism won (and lose a generation of quality). Joining\r\n * at the packet level is what makes splitting worth doing.\r\n *\r\n * ## What the caller has to guarantee\r\n *\r\n * **Every segment must be encoded with an identical configuration** — same codec,\r\n * same resolution, same bitrate settings. A container carries exactly one decoder\r\n * configuration for a track, so a segment whose parameter sets differ from the\r\n * first would be described by a configuration that isn't its own, and decode as\r\n * garbage from that point on. That is not a theoretical concern: hardware encoders\r\n * are free to pick different parameter sets for the same nominal settings. This is\r\n * checked rather than assumed — see {@link ConcatMismatchError} — because the\r\n * failure is silent corruption rather than an exception.\r\n *\r\n * **Every segment must begin with a key frame.** Otherwise its first packets refer\r\n * back to frames that lived in the previous segment's GOP and no longer precede\r\n * them. Encoders start with a key frame by default; a caller choosing an explicit\r\n * `keyFrameInterval` needs it to divide the segment length.\r\n *\r\n * Audio is *muxed* here but never *joined*: the segments' own audio is ignored\r\n * entirely, and a single already-mixed buffer is encoded once across the whole\r\n * result (see {@link ConcatOptions.audio}). Concatenating separately-encoded AAC\r\n * would put the encoder's priming delay at every join — a click at each boundary\r\n * and a track that drifts further out of sync with every one — so the timeline's\r\n * audio is mixed in one pass by whoever owns the timeline, and arrives here as\r\n * one buffer.\r\n */\r\n\r\n/** Thrown when segments can't be joined losslessly. Carries which segment disagreed. */\r\nexport class ConcatMismatchError extends Error {\r\n constructor(\r\n /** Index of the offending segment in the input array. */\r\n readonly segment: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = \"ConcatMismatchError\";\r\n }\r\n}\r\n\r\n/** One segment's demuxed video track, plus the config that describes it. */\r\ninterface Segment {\r\n track: InputVideoTrack;\r\n config: VideoDecoderConfig;\r\n duration: number;\r\n}\r\n\r\n/**\r\n * Two decoder configurations describe the same bitstream.\r\n *\r\n * `description` holds the codec's parameter sets (the `avcC` box for H.264) and is\r\n * the field that actually decides decodability — a differing one is exactly the\r\n * silent-corruption case. Compared bytewise; the rest are compared by value.\r\n */\r\nfunction sameDecoderConfig(a: VideoDecoderConfig, b: VideoDecoderConfig): boolean {\r\n if (a.codec !== b.codec) return false;\r\n if (a.codedWidth !== b.codedWidth) return false;\r\n if (a.codedHeight !== b.codedHeight) return false;\r\n\r\n const da = toBytes(a.description);\r\n const db = toBytes(b.description);\r\n if (da === null || db === null) return da === db;\r\n if (da.length !== db.length) return false;\r\n for (let i = 0; i < da.length; i++) {\r\n if (da[i] !== db[i]) return false;\r\n }\r\n return true;\r\n}\r\n\r\nfunction toBytes(description: VideoDecoderConfig[\"description\"]): Uint8Array | null {\r\n if (!description) return null;\r\n // `description` is a BufferSource: an ArrayBuffer, a SharedArrayBuffer, or any\r\n // view over one. Views carry an offset into a larger buffer, so they must be\r\n // narrowed rather than wrapped whole.\r\n if (ArrayBuffer.isView(description)) {\r\n return new Uint8Array(description.buffer, description.byteOffset, description.byteLength);\r\n }\r\n return new Uint8Array(description);\r\n}\r\n\r\nexport interface ConcatOptions {\r\n /**\r\n * One continuous audio track for the whole joined timeline.\r\n *\r\n * Mixed once by the caller (see `mixTimelineAudio`) rather than per segment,\r\n * and that is not a convenience: separately-encoded AAC carries an encoder\r\n * priming delay at the head of each piece, so joining them puts a click at\r\n * every scene boundary and drifts the track further out of sync with each\r\n * one. One buffer in, one encode, no seams.\r\n */\r\n audio?: AudioBuffer | null;\r\n /** AAC bitrate for that track. Defaults to 192 kbps. */\r\n audioBitrate?: number;\r\n /**\r\n * Write the result out as it is muxed instead of returning it.\r\n *\r\n * With this the joined file never exists in memory as one buffer, which is\r\n * the difference between an export the process can hold and one it can't:\r\n * a 4× 60fps render is hundreds of megabytes, and the default path costs\r\n * roughly twice that at the tail once the `Blob` copy is counted.\r\n *\r\n * **Chunks carry a byte `position` and do not arrive in file order** — a\r\n * muxer seeks back to patch atom sizes. A sink that appends will produce a\r\n * file of exactly the right length whose size fields belong to a different\r\n * one: unplayable, and indistinguishable from success until it is opened.\r\n * Honour `position`.\r\n *\r\n * `concatVideoSegments` resolves to an empty array when this is used; the\r\n * bytes went to the sink.\r\n */\r\n stream?: WritableStream<{ type: \"write\"; data: Uint8Array; position: number }>;\r\n}\r\n\r\nconst DEFAULT_AUDIO_BITRATE = 192_000;\r\n\r\n/**\r\n * Concatenate `segments` (each a complete MP4) into one MP4.\r\n *\r\n * Segments are joined in array order. Returns the muxed bytes.\r\n *\r\n * @throws {ConcatMismatchError} if a segment carries no video track, or one whose\r\n * decoder configuration differs from the first segment's. Callers that can\r\n * fall back to a single-threaded export should catch this and do so — the\r\n * alternative is shipping a file that decodes into garbage halfway through.\r\n */\r\nexport async function concatVideoSegments(\r\n segments: Uint8Array[],\r\n options: ConcatOptions = {},\r\n): Promise<Uint8Array> {\r\n if (segments.length === 0) throw new Error(\"concatVideoSegments: no segments\");\r\n // A lone segment is already the finished file — unless a track has to be added\r\n // to it, or it has to go to a sink, either of which means muxing after all.\r\n if (segments.length === 1 && !options.audio && !options.stream) return segments[0];\r\n\r\n const opened: Segment[] = [];\r\n for (let i = 0; i < segments.length; i++) {\r\n const input = new Input({ source: new BufferSource(segments[i]), formats: [MP4] });\r\n const track = await input.getPrimaryVideoTrack();\r\n if (!track) throw new ConcatMismatchError(i, `Segment ${i} has no video track.`);\r\n const config = await track.getDecoderConfig();\r\n if (!config) throw new ConcatMismatchError(i, `Segment ${i} has no decoder configuration.`);\r\n if (opened.length > 0 && !sameDecoderConfig(opened[0].config, config)) {\r\n throw new ConcatMismatchError(\r\n i,\r\n `Segment ${i} was encoded differently from segment 0 (${opened[0].config.codec} `\r\n + `${opened[0].config.codedWidth}x${opened[0].config.codedHeight} vs ${config.codec} `\r\n + `${config.codedWidth}x${config.codedHeight}); joining them would decode as garbage.`,\r\n );\r\n }\r\n opened.push({ track, config, duration: await track.computeDuration() });\r\n }\r\n\r\n const target = options.stream\r\n ? new StreamTarget(options.stream as ConstructorParameters<typeof StreamTarget>[0])\r\n : new BufferTarget();\r\n const output = new Output({ format: new Mp4OutputFormat(), target });\r\n // `codec` comes off the track rather than the decoder config: the latter's\r\n // `codec` is the full codec *string* (`avc1.64001f`), while the source wants\r\n // the family.\r\n const source = new EncodedVideoPacketSource(opened[0].track.codec as VideoCodec);\r\n output.addVideoTrack(source);\r\n // Declared before `start()` — a muxer fixes its track list up front, so an\r\n // audio track added later would have nowhere to go.\r\n const audioSource = options.audio\r\n ? new AudioBufferSource({\r\n codec: \"aac\",\r\n bitrate: options.audioBitrate ?? DEFAULT_AUDIO_BITRATE,\r\n })\r\n : null;\r\n if (audioSource) output.addAudioTrack(audioSource);\r\n await output.start();\r\n\r\n let offset = 0;\r\n for (const segment of opened) {\r\n // The decoder config is handed over with the first packet of each segment.\r\n // Only the first one can take effect — a container holds one per track —\r\n // but they have all been checked equal above, so restating it is harmless\r\n // and keeps the muxer from having to infer anything.\r\n let first = true;\r\n for await (const packet of new EncodedPacketSink(segment.track).packets()) {\r\n await source.add(\r\n packet.clone({ timestamp: packet.timestamp + offset }),\r\n first ? { decoderConfig: segment.config } : undefined,\r\n );\r\n first = false;\r\n }\r\n // Advance by the segment's own duration rather than by the last packet's\r\n // end: a packet's `duration` is what the container recorded, and summing\r\n // those would accumulate rounding into a drift across many segments.\r\n offset += segment.duration;\r\n }\r\n\r\n // After the video packets, so the encode overlaps nothing it has to wait on.\r\n if (audioSource && options.audio) await audioSource.add(options.audio);\r\n\r\n source.close();\r\n audioSource?.close();\r\n await output.finalize();\r\n\r\n // A streamed result has already gone to the sink; there is nothing to hand\r\n // back, and materialising it here would undo the point of streaming.\r\n if (options.stream) return new Uint8Array(0);\r\n\r\n const buffer = (target as BufferTarget).buffer;\r\n if (!buffer) throw new Error(\"concatVideoSegments produced no data\");\r\n return new Uint8Array(buffer);\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-levels.js","sourceRoot":"","sources":["../../src/decode/image-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import { floorLevel, levelCap, levelFor, type PixelSize } from \"./levels\";\n\n/**\n * Which level a draw is served, and when levels are decoded and released.\n *\n * Browser-free: decoding, uploading and fetching are the host's, so every\n * decision here runs under a plain test runner with a fake clock.\n */\n\n/** Mirrors core's `DecodePolicy`, which its barrel does not export yet. */\nexport type DecodePolicy = \"exact\" | \"interactive\";\n\nexport const IMAGE_DECODE_BUDGET_BYTES = 512 * 1024 * 1024;\n/** Unselected levels older than this go, whatever the budget — bar a {@link RETAINED_LEVEL_BYTES} largest level. */\nexport const LEVEL_AGE_OUT_MS = 2_000;\n/** A preview drawing a level at twice what it needs for this long decodes the smaller one. */\nexport const DOWNGRADE_AFTER_MS = 5_000;\nexport const IDLE_TO_FLOOR_MS = 30_000;\nexport const IDLE_DROP_SOURCE_MS = 60_000;\nexport const MAX_CONCURRENT_DECODES = 3;\n/** An interactive decode covers this multiple of the demand, so a growing draw re-decodes per octave, not per rung. */\nexport const UPGRADE_HEADROOM = 2;\n/**\n * A largest level no bigger than this is neither aged out nor downgraded until\n * the budget needs the room: decoding it again the next time a draw grows costs\n * more, in main-thread copies and a blurry frame, than holding it.\n */\nexport const RETAINED_LEVEL_BYTES = 16 * 1024 * 1024;\n\nexport interface ImageLevel<G = unknown> {\n /** The ladder rung this level was decoded for. */\n readonly edge: number;\n readonly width: number;\n readonly height: number;\n readonly pixels: Uint8Array;\n /** The host's GPU copy, counted against the budget alongside the pixels. */\n gpu: G | null;\n lastSelected: number;\n /** The pass that last selected it — \"unselected\" is judged by pass, since several can share a clock tick. */\n selectedPass: number;\n}\n\nexport interface DecodedImage {\n pixels: Uint8Array;\n width: number;\n height: number;\n intrinsic: PixelSize;\n vector: boolean;\n}\n\nexport interface ImageLevelHost<G> {\n now(): number;\n /**\n * Decode `src` at the rung `chooseEdge` picks once the source's intrinsic\n * size is known — the host may have to fetch or even decode the source to\n * learn it, which is why the choice is a callback rather than an argument.\n */\n decode(src: string, chooseEdge: (intrinsic: PixelSize, vector: boolean) => number): Promise<DecodedImage & { edge: number }>;\n /** A level left the cache. Free what was built from it. */\n released(src: string, level: ImageLevel<G>): void;\n /** Kept compressed bytes for `src`, so an upgrade needs no refetch; 0 when none. */\n sourceBytes(src: string): number;\n dropSource(src: string): void;\n /** Long edge of the surface in device px, for sizing a first interactive load. */\n surfaceEdge(): number;\n onError?(src: string, error: unknown): void;\n}\n\ninterface Entry<G> {\n readonly src: string;\n intrinsic: PixelSize | null;\n vector: boolean;\n cap: number;\n /** Ascending by edge. */\n levels: ImageLevel<G>[];\n lastDemand: number | undefined;\n lastUsed: number;\n passNeed: number;\n passDraws: number;\n passAllOver: boolean;\n overSince: number | null;\n /** Exact rungs this pass drew without. */\n wanted: Set<number>;\n queue: number[];\n job: Promise<void> | null;\n failed: Set<number>;\n loading: Promise<void> | null;\n /** The pass that asked for this source with nothing resident. */\n missedPass: number;\n /** The last pass to draw it was served below demand, so an upgrade landing changes the picture. */\n short: boolean;\n}\n\nexport class ImageLevelCache<G = unknown> {\n private _policy: DecodePolicy = \"interactive\";\n private readonly _entries = new Map<string, Entry<G>>();\n private _pass = 0;\n private _inflight = 0;\n\n constructor(\n private readonly _host: ImageLevelHost<G>,\n private readonly _budget = IMAGE_DECODE_BUDGET_BYTES,\n ) { }\n\n get policy(): DecodePolicy {\n return this._policy;\n }\n\n setPolicy(policy: DecodePolicy): void {\n this._policy = policy;\n }\n\n has(src: string): boolean {\n return (this._entries.get(src)?.levels.length ?? 0) > 0;\n }\n\n intrinsicSize(src: string): PixelSize | null {\n return this._entries.get(src)?.intrinsic ?? null;\n }\n\n /** Make sure `src` has a level to draw with. First loads pick their rung by policy. */\n load(src: string): Promise<void> {\n const entry = this._entry(src);\n if (entry.levels.length > 0) return Promise.resolve();\n entry.loading ??= this._host.decode(src, (intrinsic, vector) => {\n this._learn(entry, intrinsic, vector);\n if (this._policy === \"exact\") return floorLevel(entry.cap);\n if (entry.lastDemand !== undefined) return this._interactiveEdge(entry, entry.lastDemand);\n return levelFor(Math.min(Math.max(intrinsic.width, intrinsic.height), this._host.surfaceEdge()), entry.cap);\n }).then((decoded) => {\n this._learn(entry, decoded.intrinsic, decoded.vector);\n this._add(entry, decoded);\n this._enforceBudget();\n }).finally(() => {\n entry.loading = null;\n this._pump();\n });\n return entry.loading;\n }\n\n beginPass(): void {\n this._pass++;\n }\n\n /**\n * The level one draw paints with. `footprint` is the long edge in device px\n * the draw needs; absent means the source's own resolution.\n */\n select(src: string, footprint?: number): ImageLevel<G> | null {\n const entry = this._entry(src);\n const now = this._host.now();\n const demand = footprint !== undefined && Number.isFinite(footprint) && footprint > 0 ? footprint : Infinity;\n if (entry.levels.length === 0) {\n // Remembered so the load this miss triggers starts at the right rung.\n if (Number.isFinite(demand)) entry.lastDemand = Math.max(entry.lastDemand ?? 0, demand);\n entry.missedPass = this._pass;\n return null;\n }\n\n const need = Math.min(demand, entry.cap);\n entry.passNeed = Math.max(entry.passNeed, need);\n entry.passDraws++;\n entry.lastUsed = now;\n\n let level: ImageLevel<G>;\n if (this._policy === \"exact\") {\n const target = levelFor(need, entry.cap);\n const exact = entry.levels.find((l) => l.edge === target);\n if (!exact && !entry.failed.has(target)) entry.wanted.add(target);\n level = exact ?? nearest(entry.levels, target);\n } else {\n level = entry.levels.find((l) => l.edge >= need) ?? entry.levels[entry.levels.length - 1];\n if (level.edge < 2 * need) entry.passAllOver = false;\n }\n level.lastSelected = now;\n level.selectedPass = this._pass;\n return level;\n }\n\n /** Decide this pass's level changes, now that every draw has reported. */\n endPass(): void {\n const now = this._host.now();\n for (const entry of this._entries.values()) {\n const top = entry.levels[entry.levels.length - 1];\n if (entry.passDraws > 0 && top) {\n entry.lastDemand = entry.passNeed;\n if (this._policy === \"exact\") {\n for (const edge of entry.wanted) this._request(entry, edge, false);\n entry.wanted.clear();\n } else {\n entry.short = top.edge < entry.passNeed;\n if (entry.short) {\n this._request(entry, this._interactiveEdge(entry, entry.passNeed), true);\n } else {\n // Demand fell back under what is resident: a queued upgrade is no longer worth starting.\n entry.queue = entry.queue.filter((edge) => edge <= top.edge);\n }\n if (entry.passAllOver && !retained(top)) {\n entry.overSince ??= now;\n if (now - entry.overSince >= DOWNGRADE_AFTER_MS) {\n this._request(entry, this._interactiveEdge(entry, entry.passNeed), true);\n }\n } else {\n entry.overSince = null;\n }\n }\n } else {\n entry.short = false;\n }\n entry.passNeed = 0;\n entry.passDraws = 0;\n entry.passAllOver = true;\n this._ageOut(entry, now);\n }\n this._enforceBudget();\n this._pump();\n }\n\n /**\n * What a settling caller must wait for before the frame is right: under\n * `exact`, every decode queued or running; under `interactive`, only a source\n * the last pass asked for with nothing resident. An upgrade is {@link hasUpgrades}.\n */\n hasPending(): boolean {\n for (const entry of this._entries.values()) {\n if (this._blocks(entry)) return true;\n }\n return false;\n }\n\n /** Await what {@link hasPending} reports, and report whether there was any. */\n async warm(): Promise<boolean> {\n if (this._policy === \"interactive\") {\n let did = false;\n for (let guard = 0; guard < 64; guard++) {\n const loading = [...this._entries.values()]\n .filter((e) => this._blocks(e))\n .map((e) => e.loading!);\n if (loading.length === 0) break;\n did = true;\n await Promise.all(loading.map((job) => job.catch(() => undefined)));\n }\n return did;\n }\n\n let did = false;\n for (const entry of this._entries.values()) {\n for (const edge of entry.wanted) this._request(entry, edge, false);\n entry.wanted.clear();\n }\n this._pump();\n for (let guard = 0; guard < 64; guard++) {\n const running = [...this._entries.values()]\n .map((e) => e.job ?? e.loading)\n .filter((job): job is Promise<void> => job !== null);\n if (running.length === 0) break;\n did = true;\n await Promise.all(running.map((job) => job.catch(() => undefined)));\n this._pump();\n }\n return did;\n }\n\n /** Whether, under `interactive`, a source the last pass drew below demand has a better level on its way. */\n hasUpgrades(): boolean {\n if (this._policy !== \"interactive\") return false;\n for (const entry of this._entries.values()) {\n if (this._upgrading(entry)) return true;\n }\n return false;\n }\n\n /** Await the upgrades {@link hasUpgrades} reports; whether one landed. */\n async warmUpgrades(): Promise<boolean> {\n let did = false;\n for (let guard = 0; guard < 64 && this._policy === \"interactive\"; guard++) {\n this._pump();\n const waiting = [...this._entries.values()].filter((e) => this._upgrading(e));\n if (waiting.length === 0) break;\n const own = waiting.flatMap((e) => (e.job ? [e.job] : []));\n // Queued behind other sources' decodes: those free the slot it needs.\n const running = own.length > 0 ? own : [...this._entries.values()].flatMap((e) => (e.job ? [e.job] : []));\n if (running.length === 0) break;\n await Promise.all(running.map((job) => job.catch(() => undefined)));\n if (own.length > 0) did = true;\n }\n return did;\n }\n\n stats(): { sources: number; levels: number; levelBytes: number; sourceBytes: number; inflight: number } {\n let levels = 0;\n let levelBytes = 0;\n let sourceBytes = 0;\n for (const entry of this._entries.values()) {\n levels += entry.levels.length;\n for (const level of entry.levels) levelBytes += bytesOf(level);\n sourceBytes += this._host.sourceBytes(entry.src);\n }\n return { sources: this._entries.size, levels, levelBytes, sourceBytes, inflight: this._inflight };\n }\n\n /** Every level held for `src`, ascending — for tests and stats. */\n levelsOf(src: string): readonly ImageLevel<G>[] {\n return this._entries.get(src)?.levels ?? [];\n }\n\n dispose(): void {\n for (const entry of this._entries.values()) {\n for (const level of entry.levels) this._host.released(entry.src, level);\n entry.levels = [];\n entry.queue = [];\n }\n this._entries.clear();\n }\n\n private _entry(src: string): Entry<G> {\n let entry = this._entries.get(src);\n if (!entry) {\n entry = {\n src, intrinsic: null, vector: false, cap: 1, levels: [],\n lastDemand: undefined, lastUsed: this._host.now(),\n passNeed: 0, passDraws: 0, passAllOver: true, overSince: null,\n wanted: new Set(), queue: [], job: null, failed: new Set(), loading: null,\n missedPass: -1, short: false,\n };\n this._entries.set(src, entry);\n }\n return entry;\n }\n\n private _blocks(entry: Entry<G>): boolean {\n if (this._policy === \"exact\") {\n return entry.job !== null || entry.queue.length > 0 || entry.wanted.size > 0 || entry.loading !== null;\n }\n return entry.levels.length === 0 && entry.missedPass === this._pass && entry.loading !== null;\n }\n\n private _upgrading(entry: Entry<G>): boolean {\n return entry.short && entry.levels.length > 0 && (entry.job !== null || entry.queue.length > 0);\n }\n\n /** The rung an interactive decode for `demand` asks for: with headroom, capped at the source. */\n private _interactiveEdge(entry: Entry<G>, demand: number): number {\n return levelFor(Math.min(demand * UPGRADE_HEADROOM, entry.cap), entry.cap);\n }\n\n private _learn(entry: Entry<G>, intrinsic: PixelSize, vector: boolean): void {\n entry.intrinsic = { width: intrinsic.width, height: intrinsic.height };\n entry.vector = vector;\n entry.cap = levelCap(intrinsic, vector);\n }\n\n private _add(entry: Entry<G>, decoded: { pixels: Uint8Array; width: number; height: number; edge: number }): void {\n const existing = entry.levels.findIndex((l) => l.edge === decoded.edge);\n if (existing >= 0) {\n this._host.released(entry.src, entry.levels[existing]);\n entry.levels.splice(existing, 1);\n }\n const level: ImageLevel<G> = {\n edge: decoded.edge,\n width: decoded.width,\n height: decoded.height,\n pixels: decoded.pixels,\n gpu: null,\n // Counts as selected now, so a level a settle pass decoded is not\n // evicted before the render that asked for it has run.\n lastSelected: this._host.now(),\n selectedPass: this._pass,\n };\n entry.levels.push(level);\n entry.levels.sort((a, b) => a.edge - b.edge);\n }\n\n /**\n * Queue a decode of `edge`. An interactive request replaces what is queued,\n * since only the latest demand matters; exact requests accumulate, since a\n * frame may need several rungs of one source.\n */\n private _request(entry: Entry<G>, edge: number, replace: boolean): void {\n if (entry.failed.has(edge) || !entry.intrinsic) return;\n if (entry.levels.some((l) => l.edge === edge)) return;\n if (replace) {\n entry.queue = [edge];\n } else if (!entry.queue.includes(edge)) {\n entry.queue.push(edge);\n }\n }\n\n private _pump(): void {\n for (const entry of this._entries.values()) {\n if (this._inflight >= MAX_CONCURRENT_DECODES) return;\n if (entry.job || entry.loading) continue;\n const edge = this._nextEdge(entry);\n if (edge === undefined) continue;\n const target = edge;\n this._inflight++;\n entry.job = this._host.decode(entry.src, () => target)\n .then((decoded) => {\n this._learn(entry, decoded.intrinsic, decoded.vector);\n if (!this._entries.has(entry.src)) {\n this._host.released(entry.src, { ...decoded, gpu: null, lastSelected: 0, selectedPass: 0 });\n return;\n }\n this._add(entry, decoded);\n this._enforceBudget();\n }, (error) => {\n entry.failed.add(target);\n this._host.onError?.(entry.src, error);\n })\n .finally(() => {\n this._inflight--;\n entry.job = null;\n this._pump();\n });\n }\n }\n\n /**\n * The next queued rung worth decoding. Under `interactive` never one below the\n * latest demand, and no upgrade once what landed meanwhile already covers it.\n */\n private _nextEdge(entry: Entry<G>): number | undefined {\n for (let edge = entry.queue.shift(); edge !== undefined; edge = entry.queue.shift()) {\n if (this._policy === \"interactive\" && entry.lastDemand !== undefined) {\n const demand = Math.min(entry.lastDemand, entry.cap);\n const top = entry.levels[entry.levels.length - 1];\n if (top && edge > top.edge && top.edge >= demand) continue;\n if (edge < demand) edge = this._interactiveEdge(entry, demand);\n }\n if (!entry.failed.has(edge) && !entry.levels.some((l) => l.edge === edge)) return edge;\n }\n return undefined;\n }\n\n private _ageOut(entry: Entry<G>, now: number): void {\n if (entry.levels.length <= 1) return;\n const top = entry.levels[entry.levels.length - 1];\n const keep = entry.levels.filter((l) => now - l.lastSelected < LEVEL_AGE_OUT_MS || (l === top && retained(l)));\n if (keep.length === entry.levels.length) return;\n // Never the last one: fall back to the most recently used.\n if (keep.length === 0) keep.push(entry.levels.reduce((a, b) => (b.lastSelected > a.lastSelected ? b : a)));\n for (const level of entry.levels) if (!keep.includes(level)) this._host.released(entry.src, level);\n entry.levels = keep;\n }\n\n private _total(): number {\n let total = 0;\n for (const entry of this._entries.values()) {\n for (const level of entry.levels) total += bytesOf(level);\n total += this._host.sourceBytes(entry.src);\n }\n return total;\n }\n\n /**\n * Cheapest loss first: levels nothing drew this pass, then levels a preview\n * holds bigger than it needs, then sources nobody has looked at in a while,\n * then their compressed bytes. A source's last level is never evicted — a\n * draw finding nothing would throw rather than degrade.\n */\n private _enforceBudget(): void {\n let total = this._total();\n if (total <= this._budget) return;\n const now = this._host.now();\n\n const unselected: { entry: Entry<G>; level: ImageLevel<G> }[] = [];\n for (const entry of this._entries.values()) {\n const drawn = entry.levels.some((l) => l.selectedPass >= this._pass);\n entry.levels.forEach((level, index) => {\n if (level.selectedPass >= this._pass) return;\n // A source nothing drew keeps its smallest level as the one that\n // survives, not whichever happened to be used last.\n if (!drawn && index === 0) return;\n unselected.push({ entry, level });\n });\n }\n unselected.sort((a, b) => a.level.lastSelected - b.level.lastSelected || bytesOf(b.level) - bytesOf(a.level));\n for (const { entry, level } of unselected) {\n if (total <= this._budget) return;\n if (entry.levels.length <= 1) continue;\n entry.levels = entry.levels.filter((l) => l !== level);\n this._host.released(entry.src, level);\n total -= bytesOf(level);\n }\n\n if (this._policy === \"interactive\") {\n for (const entry of this._entries.values()) {\n if (entry.lastDemand === undefined) continue;\n const target = levelFor(entry.lastDemand, entry.cap);\n const top = entry.levels[entry.levels.length - 1];\n if (top && top.edge > target) this._request(entry, target, true);\n }\n }\n\n for (const entry of this._entries.values()) {\n if (total <= this._budget) break;\n if (now - entry.lastUsed < IDLE_TO_FLOOR_MS || entry.levels.length === 0) continue;\n const smallest = entry.levels[0];\n for (const level of entry.levels.slice(1)) {\n this._host.released(entry.src, level);\n total -= bytesOf(level);\n }\n entry.levels = [smallest];\n const floor = floorLevel(entry.cap);\n if (smallest.edge > floor) this._request(entry, floor, true);\n }\n\n for (const entry of this._entries.values()) {\n if (total <= this._budget) break;\n if (now - entry.lastUsed < IDLE_DROP_SOURCE_MS) continue;\n const bytes = this._host.sourceBytes(entry.src);\n if (bytes === 0) continue;\n this._host.dropSource(entry.src);\n total -= bytes;\n }\n }\n}\n\nfunction bytesOf(level: ImageLevel<unknown>): number {\n return level.width * level.height * 4 * (level.gpu ? 2 : 1);\n}\n\nfunction retained(level: ImageLevel<unknown>): boolean {\n return level.width * level.height * 4 <= RETAINED_LEVEL_BYTES;\n}\n\n/** The resident level closest to `edge`, preferring the larger on a tie. */\nfunction nearest<G>(levels: ImageLevel<G>[], edge: number): ImageLevel<G> {\n let best = levels[0];\n for (const level of levels) {\n const d = Math.abs(Math.log(level.edge / edge));\n const b = Math.abs(Math.log(best.edge / edge));\n if (d < b || (d === b && level.edge > best.edge)) best = level;\n }\n return best;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"image-levels.js","sourceRoot":"","sources":["../../src/decode/image-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import { floorLevel, levelCap, levelFor, type PixelSize } from \"./levels\";\r\n\r\n/**\r\n * Which level a draw is served, and when levels are decoded and released.\r\n *\r\n * Browser-free: decoding, uploading and fetching are the host's, so every\r\n * decision here runs under a plain test runner with a fake clock.\r\n */\r\n\r\n/** Mirrors core's `DecodePolicy`, which its barrel does not export yet. */\r\nexport type DecodePolicy = \"exact\" | \"interactive\";\r\n\r\nexport const IMAGE_DECODE_BUDGET_BYTES = 512 * 1024 * 1024;\r\n/** Unselected levels older than this go, whatever the budget — bar a {@link RETAINED_LEVEL_BYTES} largest level. */\r\nexport const LEVEL_AGE_OUT_MS = 2_000;\r\n/** A preview drawing a level at twice what it needs for this long decodes the smaller one. */\r\nexport const DOWNGRADE_AFTER_MS = 5_000;\r\nexport const IDLE_TO_FLOOR_MS = 30_000;\r\nexport const IDLE_DROP_SOURCE_MS = 60_000;\r\nexport const MAX_CONCURRENT_DECODES = 3;\r\n/** An interactive decode covers this multiple of the demand, so a growing draw re-decodes per octave, not per rung. */\r\nexport const UPGRADE_HEADROOM = 2;\r\n/**\r\n * A largest level no bigger than this is neither aged out nor downgraded until\r\n * the budget needs the room: decoding it again the next time a draw grows costs\r\n * more, in main-thread copies and a blurry frame, than holding it.\r\n */\r\nexport const RETAINED_LEVEL_BYTES = 16 * 1024 * 1024;\r\n\r\nexport interface ImageLevel<G = unknown> {\r\n /** The ladder rung this level was decoded for. */\r\n readonly edge: number;\r\n readonly width: number;\r\n readonly height: number;\r\n readonly pixels: Uint8Array;\r\n /** The host's GPU copy, counted against the budget alongside the pixels. */\r\n gpu: G | null;\r\n lastSelected: number;\r\n /** The pass that last selected it — \"unselected\" is judged by pass, since several can share a clock tick. */\r\n selectedPass: number;\r\n}\r\n\r\nexport interface DecodedImage {\r\n pixels: Uint8Array;\r\n width: number;\r\n height: number;\r\n intrinsic: PixelSize;\r\n vector: boolean;\r\n}\r\n\r\nexport interface ImageLevelHost<G> {\r\n now(): number;\r\n /**\r\n * Decode `src` at the rung `chooseEdge` picks once the source's intrinsic\r\n * size is known — the host may have to fetch or even decode the source to\r\n * learn it, which is why the choice is a callback rather than an argument.\r\n */\r\n decode(src: string, chooseEdge: (intrinsic: PixelSize, vector: boolean) => number): Promise<DecodedImage & { edge: number }>;\r\n /** A level left the cache. Free what was built from it. */\r\n released(src: string, level: ImageLevel<G>): void;\r\n /** Kept compressed bytes for `src`, so an upgrade needs no refetch; 0 when none. */\r\n sourceBytes(src: string): number;\r\n dropSource(src: string): void;\r\n /** Long edge of the surface in device px, for sizing a first interactive load. */\r\n surfaceEdge(): number;\r\n onError?(src: string, error: unknown): void;\r\n}\r\n\r\ninterface Entry<G> {\r\n readonly src: string;\r\n intrinsic: PixelSize | null;\r\n vector: boolean;\r\n cap: number;\r\n /** Ascending by edge. */\r\n levels: ImageLevel<G>[];\r\n lastDemand: number | undefined;\r\n lastUsed: number;\r\n passNeed: number;\r\n passDraws: number;\r\n passAllOver: boolean;\r\n overSince: number | null;\r\n /** Exact rungs this pass drew without. */\r\n wanted: Set<number>;\r\n queue: number[];\r\n job: Promise<void> | null;\r\n failed: Set<number>;\r\n loading: Promise<void> | null;\r\n /** The pass that asked for this source with nothing resident. */\r\n missedPass: number;\r\n /** The last pass to draw it was served below demand, so an upgrade landing changes the picture. */\r\n short: boolean;\r\n}\r\n\r\nexport class ImageLevelCache<G = unknown> {\r\n private _policy: DecodePolicy = \"interactive\";\r\n private readonly _entries = new Map<string, Entry<G>>();\r\n private _pass = 0;\r\n private _inflight = 0;\r\n\r\n constructor(\r\n private readonly _host: ImageLevelHost<G>,\r\n private readonly _budget = IMAGE_DECODE_BUDGET_BYTES,\r\n ) { }\r\n\r\n get policy(): DecodePolicy {\r\n return this._policy;\r\n }\r\n\r\n setPolicy(policy: DecodePolicy): void {\r\n this._policy = policy;\r\n }\r\n\r\n has(src: string): boolean {\r\n return (this._entries.get(src)?.levels.length ?? 0) > 0;\r\n }\r\n\r\n intrinsicSize(src: string): PixelSize | null {\r\n return this._entries.get(src)?.intrinsic ?? null;\r\n }\r\n\r\n /** Make sure `src` has a level to draw with. First loads pick their rung by policy. */\r\n load(src: string): Promise<void> {\r\n const entry = this._entry(src);\r\n if (entry.levels.length > 0) return Promise.resolve();\r\n entry.loading ??= this._host.decode(src, (intrinsic, vector) => {\r\n this._learn(entry, intrinsic, vector);\r\n if (this._policy === \"exact\") return floorLevel(entry.cap);\r\n if (entry.lastDemand !== undefined) return this._interactiveEdge(entry, entry.lastDemand);\r\n return levelFor(Math.min(Math.max(intrinsic.width, intrinsic.height), this._host.surfaceEdge()), entry.cap);\r\n }).then((decoded) => {\r\n this._learn(entry, decoded.intrinsic, decoded.vector);\r\n this._add(entry, decoded);\r\n this._enforceBudget();\r\n }).finally(() => {\r\n entry.loading = null;\r\n this._pump();\r\n });\r\n return entry.loading;\r\n }\r\n\r\n beginPass(): void {\r\n this._pass++;\r\n }\r\n\r\n /**\r\n * The level one draw paints with. `footprint` is the long edge in device px\r\n * the draw needs; absent means the source's own resolution.\r\n */\r\n select(src: string, footprint?: number): ImageLevel<G> | null {\r\n const entry = this._entry(src);\r\n const now = this._host.now();\r\n const demand = footprint !== undefined && Number.isFinite(footprint) && footprint > 0 ? footprint : Infinity;\r\n if (entry.levels.length === 0) {\r\n // Remembered so the load this miss triggers starts at the right rung.\r\n if (Number.isFinite(demand)) entry.lastDemand = Math.max(entry.lastDemand ?? 0, demand);\r\n entry.missedPass = this._pass;\r\n return null;\r\n }\r\n\r\n const need = Math.min(demand, entry.cap);\r\n entry.passNeed = Math.max(entry.passNeed, need);\r\n entry.passDraws++;\r\n entry.lastUsed = now;\r\n\r\n let level: ImageLevel<G>;\r\n if (this._policy === \"exact\") {\r\n const target = levelFor(need, entry.cap);\r\n const exact = entry.levels.find((l) => l.edge === target);\r\n if (!exact && !entry.failed.has(target)) entry.wanted.add(target);\r\n level = exact ?? nearest(entry.levels, target);\r\n } else {\r\n level = entry.levels.find((l) => l.edge >= need) ?? entry.levels[entry.levels.length - 1];\r\n if (level.edge < 2 * need) entry.passAllOver = false;\r\n }\r\n level.lastSelected = now;\r\n level.selectedPass = this._pass;\r\n return level;\r\n }\r\n\r\n /** Decide this pass's level changes, now that every draw has reported. */\r\n endPass(): void {\r\n const now = this._host.now();\r\n for (const entry of this._entries.values()) {\r\n const top = entry.levels[entry.levels.length - 1];\r\n if (entry.passDraws > 0 && top) {\r\n entry.lastDemand = entry.passNeed;\r\n if (this._policy === \"exact\") {\r\n for (const edge of entry.wanted) this._request(entry, edge, false);\r\n entry.wanted.clear();\r\n } else {\r\n entry.short = top.edge < entry.passNeed;\r\n if (entry.short) {\r\n this._request(entry, this._interactiveEdge(entry, entry.passNeed), true);\r\n } else {\r\n // Demand fell back under what is resident: a queued upgrade is no longer worth starting.\r\n entry.queue = entry.queue.filter((edge) => edge <= top.edge);\r\n }\r\n if (entry.passAllOver && !retained(top)) {\r\n entry.overSince ??= now;\r\n if (now - entry.overSince >= DOWNGRADE_AFTER_MS) {\r\n this._request(entry, this._interactiveEdge(entry, entry.passNeed), true);\r\n }\r\n } else {\r\n entry.overSince = null;\r\n }\r\n }\r\n } else {\r\n entry.short = false;\r\n }\r\n entry.passNeed = 0;\r\n entry.passDraws = 0;\r\n entry.passAllOver = true;\r\n this._ageOut(entry, now);\r\n }\r\n this._enforceBudget();\r\n this._pump();\r\n }\r\n\r\n /**\r\n * What a settling caller must wait for before the frame is right: under\r\n * `exact`, every decode queued or running; under `interactive`, only a source\r\n * the last pass asked for with nothing resident. An upgrade is {@link hasUpgrades}.\r\n */\r\n hasPending(): boolean {\r\n for (const entry of this._entries.values()) {\r\n if (this._blocks(entry)) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** Await what {@link hasPending} reports, and report whether there was any. */\r\n async warm(): Promise<boolean> {\r\n if (this._policy === \"interactive\") {\r\n let did = false;\r\n for (let guard = 0; guard < 64; guard++) {\r\n const loading = [...this._entries.values()]\r\n .filter((e) => this._blocks(e))\r\n .map((e) => e.loading!);\r\n if (loading.length === 0) break;\r\n did = true;\r\n await Promise.all(loading.map((job) => job.catch(() => undefined)));\r\n }\r\n return did;\r\n }\r\n\r\n let did = false;\r\n for (const entry of this._entries.values()) {\r\n for (const edge of entry.wanted) this._request(entry, edge, false);\r\n entry.wanted.clear();\r\n }\r\n this._pump();\r\n for (let guard = 0; guard < 64; guard++) {\r\n const running = [...this._entries.values()]\r\n .map((e) => e.job ?? e.loading)\r\n .filter((job): job is Promise<void> => job !== null);\r\n if (running.length === 0) break;\r\n did = true;\r\n await Promise.all(running.map((job) => job.catch(() => undefined)));\r\n this._pump();\r\n }\r\n return did;\r\n }\r\n\r\n /** Whether, under `interactive`, a source the last pass drew below demand has a better level on its way. */\r\n hasUpgrades(): boolean {\r\n if (this._policy !== \"interactive\") return false;\r\n for (const entry of this._entries.values()) {\r\n if (this._upgrading(entry)) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** Await the upgrades {@link hasUpgrades} reports; whether one landed. */\r\n async warmUpgrades(): Promise<boolean> {\r\n let did = false;\r\n for (let guard = 0; guard < 64 && this._policy === \"interactive\"; guard++) {\r\n this._pump();\r\n const waiting = [...this._entries.values()].filter((e) => this._upgrading(e));\r\n if (waiting.length === 0) break;\r\n const own = waiting.flatMap((e) => (e.job ? [e.job] : []));\r\n // Queued behind other sources' decodes: those free the slot it needs.\r\n const running = own.length > 0 ? own : [...this._entries.values()].flatMap((e) => (e.job ? [e.job] : []));\r\n if (running.length === 0) break;\r\n await Promise.all(running.map((job) => job.catch(() => undefined)));\r\n if (own.length > 0) did = true;\r\n }\r\n return did;\r\n }\r\n\r\n stats(): { sources: number; levels: number; levelBytes: number; sourceBytes: number; inflight: number } {\r\n let levels = 0;\r\n let levelBytes = 0;\r\n let sourceBytes = 0;\r\n for (const entry of this._entries.values()) {\r\n levels += entry.levels.length;\r\n for (const level of entry.levels) levelBytes += bytesOf(level);\r\n sourceBytes += this._host.sourceBytes(entry.src);\r\n }\r\n return { sources: this._entries.size, levels, levelBytes, sourceBytes, inflight: this._inflight };\r\n }\r\n\r\n /** Every level held for `src`, ascending — for tests and stats. */\r\n levelsOf(src: string): readonly ImageLevel<G>[] {\r\n return this._entries.get(src)?.levels ?? [];\r\n }\r\n\r\n dispose(): void {\r\n for (const entry of this._entries.values()) {\r\n for (const level of entry.levels) this._host.released(entry.src, level);\r\n entry.levels = [];\r\n entry.queue = [];\r\n }\r\n this._entries.clear();\r\n }\r\n\r\n private _entry(src: string): Entry<G> {\r\n let entry = this._entries.get(src);\r\n if (!entry) {\r\n entry = {\r\n src, intrinsic: null, vector: false, cap: 1, levels: [],\r\n lastDemand: undefined, lastUsed: this._host.now(),\r\n passNeed: 0, passDraws: 0, passAllOver: true, overSince: null,\r\n wanted: new Set(), queue: [], job: null, failed: new Set(), loading: null,\r\n missedPass: -1, short: false,\r\n };\r\n this._entries.set(src, entry);\r\n }\r\n return entry;\r\n }\r\n\r\n private _blocks(entry: Entry<G>): boolean {\r\n if (this._policy === \"exact\") {\r\n return entry.job !== null || entry.queue.length > 0 || entry.wanted.size > 0 || entry.loading !== null;\r\n }\r\n return entry.levels.length === 0 && entry.missedPass === this._pass && entry.loading !== null;\r\n }\r\n\r\n private _upgrading(entry: Entry<G>): boolean {\r\n return entry.short && entry.levels.length > 0 && (entry.job !== null || entry.queue.length > 0);\r\n }\r\n\r\n /** The rung an interactive decode for `demand` asks for: with headroom, capped at the source. */\r\n private _interactiveEdge(entry: Entry<G>, demand: number): number {\r\n return levelFor(Math.min(demand * UPGRADE_HEADROOM, entry.cap), entry.cap);\r\n }\r\n\r\n private _learn(entry: Entry<G>, intrinsic: PixelSize, vector: boolean): void {\r\n entry.intrinsic = { width: intrinsic.width, height: intrinsic.height };\r\n entry.vector = vector;\r\n entry.cap = levelCap(intrinsic, vector);\r\n }\r\n\r\n private _add(entry: Entry<G>, decoded: { pixels: Uint8Array; width: number; height: number; edge: number }): void {\r\n const existing = entry.levels.findIndex((l) => l.edge === decoded.edge);\r\n if (existing >= 0) {\r\n this._host.released(entry.src, entry.levels[existing]);\r\n entry.levels.splice(existing, 1);\r\n }\r\n const level: ImageLevel<G> = {\r\n edge: decoded.edge,\r\n width: decoded.width,\r\n height: decoded.height,\r\n pixels: decoded.pixels,\r\n gpu: null,\r\n // Counts as selected now, so a level a settle pass decoded is not\r\n // evicted before the render that asked for it has run.\r\n lastSelected: this._host.now(),\r\n selectedPass: this._pass,\r\n };\r\n entry.levels.push(level);\r\n entry.levels.sort((a, b) => a.edge - b.edge);\r\n }\r\n\r\n /**\r\n * Queue a decode of `edge`. An interactive request replaces what is queued,\r\n * since only the latest demand matters; exact requests accumulate, since a\r\n * frame may need several rungs of one source.\r\n */\r\n private _request(entry: Entry<G>, edge: number, replace: boolean): void {\r\n if (entry.failed.has(edge) || !entry.intrinsic) return;\r\n if (entry.levels.some((l) => l.edge === edge)) return;\r\n if (replace) {\r\n entry.queue = [edge];\r\n } else if (!entry.queue.includes(edge)) {\r\n entry.queue.push(edge);\r\n }\r\n }\r\n\r\n private _pump(): void {\r\n for (const entry of this._entries.values()) {\r\n if (this._inflight >= MAX_CONCURRENT_DECODES) return;\r\n if (entry.job || entry.loading) continue;\r\n const edge = this._nextEdge(entry);\r\n if (edge === undefined) continue;\r\n const target = edge;\r\n this._inflight++;\r\n entry.job = this._host.decode(entry.src, () => target)\r\n .then((decoded) => {\r\n this._learn(entry, decoded.intrinsic, decoded.vector);\r\n if (!this._entries.has(entry.src)) {\r\n this._host.released(entry.src, { ...decoded, gpu: null, lastSelected: 0, selectedPass: 0 });\r\n return;\r\n }\r\n this._add(entry, decoded);\r\n this._enforceBudget();\r\n }, (error) => {\r\n entry.failed.add(target);\r\n this._host.onError?.(entry.src, error);\r\n })\r\n .finally(() => {\r\n this._inflight--;\r\n entry.job = null;\r\n this._pump();\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * The next queued rung worth decoding. Under `interactive` never one below the\r\n * latest demand, and no upgrade once what landed meanwhile already covers it.\r\n */\r\n private _nextEdge(entry: Entry<G>): number | undefined {\r\n for (let edge = entry.queue.shift(); edge !== undefined; edge = entry.queue.shift()) {\r\n if (this._policy === \"interactive\" && entry.lastDemand !== undefined) {\r\n const demand = Math.min(entry.lastDemand, entry.cap);\r\n const top = entry.levels[entry.levels.length - 1];\r\n if (top && edge > top.edge && top.edge >= demand) continue;\r\n if (edge < demand) edge = this._interactiveEdge(entry, demand);\r\n }\r\n if (!entry.failed.has(edge) && !entry.levels.some((l) => l.edge === edge)) return edge;\r\n }\r\n return undefined;\r\n }\r\n\r\n private _ageOut(entry: Entry<G>, now: number): void {\r\n if (entry.levels.length <= 1) return;\r\n const top = entry.levels[entry.levels.length - 1];\r\n const keep = entry.levels.filter((l) => now - l.lastSelected < LEVEL_AGE_OUT_MS || (l === top && retained(l)));\r\n if (keep.length === entry.levels.length) return;\r\n // Never the last one: fall back to the most recently used.\r\n if (keep.length === 0) keep.push(entry.levels.reduce((a, b) => (b.lastSelected > a.lastSelected ? b : a)));\r\n for (const level of entry.levels) if (!keep.includes(level)) this._host.released(entry.src, level);\r\n entry.levels = keep;\r\n }\r\n\r\n private _total(): number {\r\n let total = 0;\r\n for (const entry of this._entries.values()) {\r\n for (const level of entry.levels) total += bytesOf(level);\r\n total += this._host.sourceBytes(entry.src);\r\n }\r\n return total;\r\n }\r\n\r\n /**\r\n * Cheapest loss first: levels nothing drew this pass, then levels a preview\r\n * holds bigger than it needs, then sources nobody has looked at in a while,\r\n * then their compressed bytes. A source's last level is never evicted — a\r\n * draw finding nothing would throw rather than degrade.\r\n */\r\n private _enforceBudget(): void {\r\n let total = this._total();\r\n if (total <= this._budget) return;\r\n const now = this._host.now();\r\n\r\n const unselected: { entry: Entry<G>; level: ImageLevel<G> }[] = [];\r\n for (const entry of this._entries.values()) {\r\n const drawn = entry.levels.some((l) => l.selectedPass >= this._pass);\r\n entry.levels.forEach((level, index) => {\r\n if (level.selectedPass >= this._pass) return;\r\n // A source nothing drew keeps its smallest level as the one that\r\n // survives, not whichever happened to be used last.\r\n if (!drawn && index === 0) return;\r\n unselected.push({ entry, level });\r\n });\r\n }\r\n unselected.sort((a, b) => a.level.lastSelected - b.level.lastSelected || bytesOf(b.level) - bytesOf(a.level));\r\n for (const { entry, level } of unselected) {\r\n if (total <= this._budget) return;\r\n if (entry.levels.length <= 1) continue;\r\n entry.levels = entry.levels.filter((l) => l !== level);\r\n this._host.released(entry.src, level);\r\n total -= bytesOf(level);\r\n }\r\n\r\n if (this._policy === \"interactive\") {\r\n for (const entry of this._entries.values()) {\r\n if (entry.lastDemand === undefined) continue;\r\n const target = levelFor(entry.lastDemand, entry.cap);\r\n const top = entry.levels[entry.levels.length - 1];\r\n if (top && top.edge > target) this._request(entry, target, true);\r\n }\r\n }\r\n\r\n for (const entry of this._entries.values()) {\r\n if (total <= this._budget) break;\r\n if (now - entry.lastUsed < IDLE_TO_FLOOR_MS || entry.levels.length === 0) continue;\r\n const smallest = entry.levels[0];\r\n for (const level of entry.levels.slice(1)) {\r\n this._host.released(entry.src, level);\r\n total -= bytesOf(level);\r\n }\r\n entry.levels = [smallest];\r\n const floor = floorLevel(entry.cap);\r\n if (smallest.edge > floor) this._request(entry, floor, true);\r\n }\r\n\r\n for (const entry of this._entries.values()) {\r\n if (total <= this._budget) break;\r\n if (now - entry.lastUsed < IDLE_DROP_SOURCE_MS) continue;\r\n const bytes = this._host.sourceBytes(entry.src);\r\n if (bytes === 0) continue;\r\n this._host.dropSource(entry.src);\r\n total -= bytes;\r\n }\r\n }\r\n}\r\n\r\nfunction bytesOf(level: ImageLevel<unknown>): number {\r\n return level.width * level.height * 4 * (level.gpu ? 2 : 1);\r\n}\r\n\r\nfunction retained(level: ImageLevel<unknown>): boolean {\r\n return level.width * level.height * 4 <= RETAINED_LEVEL_BYTES;\r\n}\r\n\r\n/** The resident level closest to `edge`, preferring the larger on a tie. */\r\nfunction nearest<G>(levels: ImageLevel<G>[], edge: number): ImageLevel<G> {\r\n let best = levels[0];\r\n for (const level of levels) {\r\n const d = Math.abs(Math.log(level.edge / edge));\r\n const b = Math.abs(Math.log(best.edge / edge));\r\n if (d < b || (d === b && level.edge > best.edge)) best = level;\r\n }\r\n return best;\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"levels.js","sourceRoot":"","sources":["../../src/decode/levels.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,wDAAwD;AACxD,MAAM,CAAC,MAAM,YAAY,GAAsB,CAAC,GAAG,EAAE;IACjD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,GAAI,CAAC,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,IAAI,GAAG,qBAAqB;YAAE,MAAM;QACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC,CAAC,EAAE,CAAC;AAOL;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAoB,EAAE,MAAe;IAC1D,IAAI,MAAM;QAAE,OAAO,qBAAqB,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,GAAW;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACjE,IAAI,IAAI,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAC5B,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,GAAG,CAAC;QAC7B,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,UAAU,CAAC,GAAW;IAClC,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,SAAoB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO;QACH,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QACvD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;KAC5D,CAAC;AACN,CAAC","sourcesContent":["/**\n * The resolutions a source may be decoded at.\n *\n * Levels sit half an octave apart, so a decode is never more than √2 larger than\n * what a draw asked for, and a slow zoom re-decodes a dozen times across the\n * whole range rather than once per frame.\n */\n\n/**\n * Longest edge any level is decoded at. The conventional maximum GPU texture\n * edge, so pixels past it could not be sampled in one draw anyway; a 4096²\n * RGBA level is 67 MB resident.\n */\nexport const MAX_IMAGE_DECODE_EDGE = 4096;\n\nexport const MIN_LEVEL_EDGE = 64;\n\n/** `64·2^(k/2)` up to {@link MAX_IMAGE_DECODE_EDGE}. */\nexport const LEVEL_LADDER: readonly number[] = (() => {\n const out: number[] = [];\n for (let k = 0; ; k++) {\n const edge = Math.round(MIN_LEVEL_EDGE * 2 ** (k / 2));\n if (edge > MAX_IMAGE_DECODE_EDGE) break;\n out.push(edge);\n }\n return out;\n})();\n\nexport interface PixelSize {\n width: number;\n height: number;\n}\n\n/**\n * The largest level worth holding: the source's own long edge for a raster\n * (there is no more detail to reveal), the ceiling for a vector.\n */\nexport function levelCap(intrinsic: PixelSize, vector: boolean): number {\n if (vector) return MAX_IMAGE_DECODE_EDGE;\n const long = Math.max(1, Math.round(Math.max(intrinsic.width, intrinsic.height)));\n return Math.min(long, MAX_IMAGE_DECODE_EDGE);\n}\n\n/**\n * The level for a draw that needs `demand` device px along the source's long\n * edge: the smallest rung at or above it, snapped to `cap` once a rung is\n * within half an octave of it — a level at 0.8× the source costs almost what\n * the source does and looks worse.\n */\nexport function levelFor(demand: number, cap: number): number {\n const need = Number.isFinite(demand) ? Math.max(0, demand) : cap;\n if (need >= cap) return cap;\n const snap = cap / Math.SQRT2;\n for (const edge of LEVEL_LADDER) {\n if (edge >= snap) return cap;\n if (edge >= need) return edge;\n }\n return cap;\n}\n\n/** The smallest level a source can be held at. */\nexport function floorLevel(cap: number): number {\n return levelFor(0, cap);\n}\n\n/** Pixel dimensions of `edge`, keeping the intrinsic aspect. */\nexport function levelSize(edge: number, intrinsic: PixelSize): PixelSize {\n const long = Math.max(intrinsic.width, intrinsic.height, 1);\n const scale = edge / long;\n return {\n width: Math.max(1, Math.round(intrinsic.width * scale)),\n height: Math.max(1, Math.round(intrinsic.height * scale)),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"levels.js","sourceRoot":"","sources":["../../src/decode/levels.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,wDAAwD;AACxD,MAAM,CAAC,MAAM,YAAY,GAAsB,CAAC,GAAG,EAAE;IACjD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,GAAI,CAAC,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,IAAI,GAAG,qBAAqB;YAAE,MAAM;QACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC,CAAC,EAAE,CAAC;AAOL;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAoB,EAAE,MAAe;IAC1D,IAAI,MAAM;QAAE,OAAO,qBAAqB,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,GAAW;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACjE,IAAI,IAAI,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAC5B,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAC9B,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,GAAG,CAAC;QAC7B,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,UAAU,CAAC,GAAW;IAClC,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,SAAoB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO;QACH,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QACvD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;KAC5D,CAAC;AACN,CAAC","sourcesContent":["/**\r\n * The resolutions a source may be decoded at.\r\n *\r\n * Levels sit half an octave apart, so a decode is never more than √2 larger than\r\n * what a draw asked for, and a slow zoom re-decodes a dozen times across the\r\n * whole range rather than once per frame.\r\n */\r\n\r\n/**\r\n * Longest edge any level is decoded at. The conventional maximum GPU texture\r\n * edge, so pixels past it could not be sampled in one draw anyway; a 4096²\r\n * RGBA level is 67 MB resident.\r\n */\r\nexport const MAX_IMAGE_DECODE_EDGE = 4096;\r\n\r\nexport const MIN_LEVEL_EDGE = 64;\r\n\r\n/** `64·2^(k/2)` up to {@link MAX_IMAGE_DECODE_EDGE}. */\r\nexport const LEVEL_LADDER: readonly number[] = (() => {\r\n const out: number[] = [];\r\n for (let k = 0; ; k++) {\r\n const edge = Math.round(MIN_LEVEL_EDGE * 2 ** (k / 2));\r\n if (edge > MAX_IMAGE_DECODE_EDGE) break;\r\n out.push(edge);\r\n }\r\n return out;\r\n})();\r\n\r\nexport interface PixelSize {\r\n width: number;\r\n height: number;\r\n}\r\n\r\n/**\r\n * The largest level worth holding: the source's own long edge for a raster\r\n * (there is no more detail to reveal), the ceiling for a vector.\r\n */\r\nexport function levelCap(intrinsic: PixelSize, vector: boolean): number {\r\n if (vector) return MAX_IMAGE_DECODE_EDGE;\r\n const long = Math.max(1, Math.round(Math.max(intrinsic.width, intrinsic.height)));\r\n return Math.min(long, MAX_IMAGE_DECODE_EDGE);\r\n}\r\n\r\n/**\r\n * The level for a draw that needs `demand` device px along the source's long\r\n * edge: the smallest rung at or above it, snapped to `cap` once a rung is\r\n * within half an octave of it — a level at 0.8× the source costs almost what\r\n * the source does and looks worse.\r\n */\r\nexport function levelFor(demand: number, cap: number): number {\r\n const need = Number.isFinite(demand) ? Math.max(0, demand) : cap;\r\n if (need >= cap) return cap;\r\n const snap = cap / Math.SQRT2;\r\n for (const edge of LEVEL_LADDER) {\r\n if (edge >= snap) return cap;\r\n if (edge >= need) return edge;\r\n }\r\n return cap;\r\n}\r\n\r\n/** The smallest level a source can be held at. */\r\nexport function floorLevel(cap: number): number {\r\n return levelFor(0, cap);\r\n}\r\n\r\n/** Pixel dimensions of `edge`, keeping the intrinsic aspect. */\r\nexport function levelSize(edge: number, intrinsic: PixelSize): PixelSize {\r\n const long = Math.max(intrinsic.width, intrinsic.height, 1);\r\n const scale = edge / long;\r\n return {\r\n width: Math.max(1, Math.round(intrinsic.width * scale)),\r\n height: Math.max(1, Math.round(intrinsic.height * scale)),\r\n };\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-levels.js","sourceRoot":"","sources":["../../src/decode/video-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DecodePolicy } from \"./image-levels\";\nimport { DOWNGRADE_AFTER_MS } from \"./image-levels\";\nimport { floorLevel, levelCap, levelFor, type PixelSize } from \"./levels\";\n\n/**\n * Decoded frames one clip's window may hold, in bytes. Frames are counted at\n * their decoded level, so a clip drawn small keeps a long window and one drawn\n * full-screen a short one, rather than a fixed count sized for neither.\n */\nexport const VIDEO_WINDOW_BUDGET_BYTES = 512 * 1024 * 1024;\n\nconst MAX_FORWARD_FRAMES = 96;\nconst MAX_BACK_FRAMES = 32;\n\n/** Forward and back window lengths for frames of `size`, split 3:1 like the fixed window was. */\nexport function videoWindowFrames(size: PixelSize | null): { forward: number; back: number } {\n if (!size || !(size.width > 0) || !(size.height > 0)) {\n return { forward: MAX_FORWARD_FRAMES, back: MAX_BACK_FRAMES };\n }\n const frames = VIDEO_WINDOW_BUDGET_BYTES / (size.width * size.height * 4);\n return {\n forward: Math.max(4, Math.min(MAX_FORWARD_FRAMES, Math.floor(frames * 0.75))),\n back: Math.max(1, Math.min(MAX_BACK_FRAMES, Math.floor(frames * 0.25))),\n };\n}\n\n/**\n * The level one clip's frames are snapshotted at, and how it follows demand.\n *\n * A clip has one level at a time, not one per draw: the decoder is sequential,\n * and a frame is resized as it is snapshotted, so changing the level changes\n * what the next snapshot produces without restarting the stream.\n */\nexport class VideoLevel {\n level: number;\n private _cap: number;\n private _passNeed = 0;\n private _passDraws = 0;\n private _passAllOver = true;\n private _overSince: number | null = null;\n\n constructor(intrinsic: PixelSize, policy: DecodePolicy, lastDemand: number | undefined, surfaceEdge: number) {\n this._cap = levelCap(intrinsic, false);\n this.level = policy === \"exact\"\n ? floorLevel(this._cap)\n : levelFor(lastDemand ?? Math.min(Math.max(intrinsic.width, intrinsic.height), surfaceEdge), this._cap);\n }\n\n get cap(): number {\n return this._cap;\n }\n\n /** Record one draw's footprint; absent means the clip's own resolution. */\n note(footprint: number | undefined): void {\n const need = footprint !== undefined && Number.isFinite(footprint) && footprint > 0\n ? Math.min(footprint, this._cap)\n : this._cap;\n this._passNeed = Math.max(this._passNeed, need);\n this._passDraws++;\n if (this.level < 2 * need) this._passAllOver = false;\n }\n\n /** Whether a frame snapshotted at `frameLevel` is good enough to count as the frame. */\n accepts(frameLevel: number | undefined, policy: DecodePolicy): boolean {\n if (frameLevel === undefined) return true;\n return policy === \"exact\" ? frameLevel === this.level : frameLevel >= this.level;\n }\n\n /** The pass's demand is complete: move the level, and report whether it moved. */\n endPass(policy: DecodePolicy, playing: boolean, now: number): boolean {\n const draws = this._passDraws;\n const need = this._passNeed;\n const allOver = this._passAllOver;\n this._passDraws = 0;\n this._passNeed = 0;\n this._passAllOver = true;\n if (draws === 0) return false;\n\n const target = levelFor(need, this._cap);\n const before = this.level;\n if (policy === \"exact\") {\n this.level = target;\n } else if (this.level < need) {\n this.level = target;\n this._overSince = null;\n } else if (allOver && !playing) {\n // Only while paused: a playing clip dropping resolution is visible\n // as a pop, and its window would re-decode behind the playhead.\n this._overSince ??= now;\n if (now - this._overSince >= DOWNGRADE_AFTER_MS) {\n this.level = target;\n this._overSince = null;\n }\n } else {\n this._overSince = null;\n }\n return this.level !== before;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"video-levels.js","sourceRoot":"","sources":["../../src/decode/video-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DecodePolicy } from \"./image-levels\";\r\nimport { DOWNGRADE_AFTER_MS } from \"./image-levels\";\r\nimport { floorLevel, levelCap, levelFor, type PixelSize } from \"./levels\";\r\n\r\n/**\r\n * Decoded frames one clip's window may hold, in bytes. Frames are counted at\r\n * their decoded level, so a clip drawn small keeps a long window and one drawn\r\n * full-screen a short one, rather than a fixed count sized for neither.\r\n */\r\nexport const VIDEO_WINDOW_BUDGET_BYTES = 512 * 1024 * 1024;\r\n\r\nconst MAX_FORWARD_FRAMES = 96;\r\nconst MAX_BACK_FRAMES = 32;\r\n\r\n/** Forward and back window lengths for frames of `size`, split 3:1 like the fixed window was. */\r\nexport function videoWindowFrames(size: PixelSize | null): { forward: number; back: number } {\r\n if (!size || !(size.width > 0) || !(size.height > 0)) {\r\n return { forward: MAX_FORWARD_FRAMES, back: MAX_BACK_FRAMES };\r\n }\r\n const frames = VIDEO_WINDOW_BUDGET_BYTES / (size.width * size.height * 4);\r\n return {\r\n forward: Math.max(4, Math.min(MAX_FORWARD_FRAMES, Math.floor(frames * 0.75))),\r\n back: Math.max(1, Math.min(MAX_BACK_FRAMES, Math.floor(frames * 0.25))),\r\n };\r\n}\r\n\r\n/**\r\n * The level one clip's frames are snapshotted at, and how it follows demand.\r\n *\r\n * A clip has one level at a time, not one per draw: the decoder is sequential,\r\n * and a frame is resized as it is snapshotted, so changing the level changes\r\n * what the next snapshot produces without restarting the stream.\r\n */\r\nexport class VideoLevel {\r\n level: number;\r\n private _cap: number;\r\n private _passNeed = 0;\r\n private _passDraws = 0;\r\n private _passAllOver = true;\r\n private _overSince: number | null = null;\r\n\r\n constructor(intrinsic: PixelSize, policy: DecodePolicy, lastDemand: number | undefined, surfaceEdge: number) {\r\n this._cap = levelCap(intrinsic, false);\r\n this.level = policy === \"exact\"\r\n ? floorLevel(this._cap)\r\n : levelFor(lastDemand ?? Math.min(Math.max(intrinsic.width, intrinsic.height), surfaceEdge), this._cap);\r\n }\r\n\r\n get cap(): number {\r\n return this._cap;\r\n }\r\n\r\n /** Record one draw's footprint; absent means the clip's own resolution. */\r\n note(footprint: number | undefined): void {\r\n const need = footprint !== undefined && Number.isFinite(footprint) && footprint > 0\r\n ? Math.min(footprint, this._cap)\r\n : this._cap;\r\n this._passNeed = Math.max(this._passNeed, need);\r\n this._passDraws++;\r\n if (this.level < 2 * need) this._passAllOver = false;\r\n }\r\n\r\n /** Whether a frame snapshotted at `frameLevel` is good enough to count as the frame. */\r\n accepts(frameLevel: number | undefined, policy: DecodePolicy): boolean {\r\n if (frameLevel === undefined) return true;\r\n return policy === \"exact\" ? frameLevel === this.level : frameLevel >= this.level;\r\n }\r\n\r\n /** The pass's demand is complete: move the level, and report whether it moved. */\r\n endPass(policy: DecodePolicy, playing: boolean, now: number): boolean {\r\n const draws = this._passDraws;\r\n const need = this._passNeed;\r\n const allOver = this._passAllOver;\r\n this._passDraws = 0;\r\n this._passNeed = 0;\r\n this._passAllOver = true;\r\n if (draws === 0) return false;\r\n\r\n const target = levelFor(need, this._cap);\r\n const before = this.level;\r\n if (policy === \"exact\") {\r\n this.level = target;\r\n } else if (this.level < need) {\r\n this.level = target;\r\n this._overSince = null;\r\n } else if (allOver && !playing) {\r\n // Only while paused: a playing clip dropping resolution is visible\r\n // as a pop, and its window would re-decode behind the playhead.\r\n this._overSince ??= now;\r\n if (now - this._overSince >= DOWNGRADE_AFTER_MS) {\r\n this.level = target;\r\n this._overSince = null;\r\n }\r\n } else {\r\n this._overSince = null;\r\n }\r\n return this.level !== before;\r\n }\r\n}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-nodes.js","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,KAAK,UAAU,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;;;;GAaG;AAEH,mFAAmF;AACnF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,SAAS,sBAAsB;IAC3B,OAAO,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,IAAI,eAAyE,CAAC;AAE9E,iFAAiF;AACjF,SAAS,eAAe,CAAC,OAAgB;IACrC,OAAO,CAAC,eAAe,KAAK,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,EAAE,CAAC,CAAC;QAC3E,OAAO,KAAK,EAAE,MAAc,EAAE,EAAE;YAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE;gBAC7C,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,QAAQ;gBAChB,oEAAoE;gBACpE,qEAAqE;gBACrE,GAAG,EAAE,WAAW;gBAChB,eAAe,EAAE,mBAAmB;gBACpC,WAAW,EAAE,EAAE,eAAe,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,EAAE;aACrE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC;IACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACV,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAC/B,OAA+C,EAC/C,eAAwB;IAExB,OAAO;QACH,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG;QACxB,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,UAAU;QAC7C,OAAO,EAAE,OAAO,EAAE,OAAO;QACzB,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;KACpG,CAAC;AACN,CAAC","sourcesContent":["import type { DynamicNodeConfig } from \"@motionscript/core\";\nimport * as sdk from \"@motionscript/core/sdk\";\nimport * as jsxRuntime from \"@motionscript/core/jsx/jsx-runtime\";\nimport * as esbuild from \"esbuild-wasm\";\n\n/**\n * `browser`'s real, working default for `Registry.addNodeFromSource` — the\n * `sdk`/`jsxRuntime` halves are cheap (re-exported classes, no WASM) and\n * imported eagerly; the compiler is the expensive half, and stays behind a\n * lazy `import()` so an app that never registers a source-string node never\n * pays for it, however long the engine has been running.\n *\n * `core` has no compiler dependency of its own and cannot import the published\n * `@motionscript/sdk` barrel (that package depends on `core`, so the reverse\n * would be circular) — `browser` is where this concern actually belongs: the\n * package whose entire job is \"what running in an actual browser tab looks\n * like\" already owns `getCanvasKit`/`WebAudioSink`/`WebTicker` on exactly that\n * basis.\n */\n\n// Default to the wasm shipped by esbuild-wasm, resolved lazily — `import.meta.url`\n// is empty in a non-ESM output (an IIFE bundle, which is what the headless page\n// loads), and `new URL(\"…\", \"\")` throws on import, before a caller has had the\n// chance to pass an explicit URL that would have made the default irrelevant.\n// Mirrors `getCanvasKit`'s own `defaultWasmUrl` in `getter.ts`.\nfunction defaultCompilerWasmUrl(): string {\n return new URL(\"esbuild-wasm/esbuild.wasm\", import.meta.url).href;\n}\n\nlet compilerPromise: Promise<(source: string) => Promise<string>> | undefined;\n\n/** Loaded once, memoized process-wide — mirrors how `getCanvasKit` is shared. */\nfunction defaultCompiler(wasmUrl?: string): Promise<(source: string) => Promise<string>> {\n return (compilerPromise ??= (async () => {\n await esbuild.initialize({ wasmURL: wasmUrl ?? defaultCompilerWasmUrl() });\n return async (source: string) => {\n const { code } = await esbuild.transform(source, {\n loader: \"tsx\",\n format: \"cjs\",\n target: \"es2020\",\n // JSX becomes `require(\"@motionscript/sdk/jsx-runtime\")`, which the\n // registry links to `jsxRuntime`. A pragma in the script still wins.\n jsx: \"automatic\",\n jsxImportSource: \"@motionscript/sdk\",\n tsconfigRaw: { compilerOptions: { experimentalDecorators: true } },\n });\n return code;\n };\n })());\n}\n\n/**\n * Resolves a full `DynamicNodeConfig`, filling in whatever the caller left\n * unset with `browser`'s own default. Synchronous — the WASM import happens\n * on `compile`'s first actual call, not here, so attaching this to every\n * engine costs nothing until a source string is genuinely registered.\n */\nexport function resolveDynamicNodes(\n partial: Partial<DynamicNodeConfig> | undefined,\n compilerWasmUrl?: string,\n): DynamicNodeConfig {\n return {\n sdk: partial?.sdk ?? sdk,\n jsxRuntime: partial?.jsxRuntime ?? jsxRuntime,\n modules: partial?.modules,\n compile: partial?.compile ?? (async (source) => (await defaultCompiler(compilerWasmUrl))(source)),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dynamic-nodes.js","sourceRoot":"","sources":["../src/dynamic-nodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,KAAK,UAAU,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;;;;GAaG;AAEH,mFAAmF;AACnF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,SAAS,sBAAsB;IAC3B,OAAO,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED,IAAI,eAAyE,CAAC;AAE9E,iFAAiF;AACjF,SAAS,eAAe,CAAC,OAAgB;IACrC,OAAO,CAAC,eAAe,KAAK,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,EAAE,CAAC,CAAC;QAC3E,OAAO,KAAK,EAAE,MAAc,EAAE,EAAE;YAC5B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE;gBAC7C,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,QAAQ;gBAChB,oEAAoE;gBACpE,qEAAqE;gBACrE,GAAG,EAAE,WAAW;gBAChB,eAAe,EAAE,mBAAmB;gBACpC,WAAW,EAAE,EAAE,eAAe,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,EAAE;aACrE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC;IACN,CAAC,CAAC,EAAE,CAAC,CAAC;AACV,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAC/B,OAA+C,EAC/C,eAAwB;IAExB,OAAO;QACH,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG;QACxB,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,UAAU;QAC7C,OAAO,EAAE,OAAO,EAAE,OAAO;QACzB,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,eAAe,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;KACpG,CAAC;AACN,CAAC","sourcesContent":["import type { DynamicNodeConfig } from \"@motionscript/core\";\r\nimport * as sdk from \"@motionscript/core/sdk\";\r\nimport * as jsxRuntime from \"@motionscript/core/jsx/jsx-runtime\";\r\nimport * as esbuild from \"esbuild-wasm\";\r\n\r\n/**\r\n * `browser`'s real, working default for `Registry.addNodeFromSource` — the\r\n * `sdk`/`jsxRuntime` halves are cheap (re-exported classes, no WASM) and\r\n * imported eagerly; the compiler is the expensive half, and stays behind a\r\n * lazy `import()` so an app that never registers a source-string node never\r\n * pays for it, however long the engine has been running.\r\n *\r\n * `core` has no compiler dependency of its own and cannot import the published\r\n * `@motionscript/sdk` barrel (that package depends on `core`, so the reverse\r\n * would be circular) — `browser` is where this concern actually belongs: the\r\n * package whose entire job is \"what running in an actual browser tab looks\r\n * like\" already owns `getCanvasKit`/`WebAudioSink`/`WebTicker` on exactly that\r\n * basis.\r\n */\r\n\r\n// Default to the wasm shipped by esbuild-wasm, resolved lazily — `import.meta.url`\r\n// is empty in a non-ESM output (an IIFE bundle, which is what the headless page\r\n// loads), and `new URL(\"…\", \"\")` throws on import, before a caller has had the\r\n// chance to pass an explicit URL that would have made the default irrelevant.\r\n// Mirrors `getCanvasKit`'s own `defaultWasmUrl` in `getter.ts`.\r\nfunction defaultCompilerWasmUrl(): string {\r\n return new URL(\"esbuild-wasm/esbuild.wasm\", import.meta.url).href;\r\n}\r\n\r\nlet compilerPromise: Promise<(source: string) => Promise<string>> | undefined;\r\n\r\n/** Loaded once, memoized process-wide — mirrors how `getCanvasKit` is shared. */\r\nfunction defaultCompiler(wasmUrl?: string): Promise<(source: string) => Promise<string>> {\r\n return (compilerPromise ??= (async () => {\r\n await esbuild.initialize({ wasmURL: wasmUrl ?? defaultCompilerWasmUrl() });\r\n return async (source: string) => {\r\n const { code } = await esbuild.transform(source, {\r\n loader: \"tsx\",\r\n format: \"cjs\",\r\n target: \"es2020\",\r\n // JSX becomes `require(\"@motionscript/sdk/jsx-runtime\")`, which the\r\n // registry links to `jsxRuntime`. A pragma in the script still wins.\r\n jsx: \"automatic\",\r\n jsxImportSource: \"@motionscript/sdk\",\r\n tsconfigRaw: { compilerOptions: { experimentalDecorators: true } },\r\n });\r\n return code;\r\n };\r\n })());\r\n}\r\n\r\n/**\r\n * Resolves a full `DynamicNodeConfig`, filling in whatever the caller left\r\n * unset with `browser`'s own default. Synchronous — the WASM import happens\r\n * on `compile`'s first actual call, not here, so attaching this to every\r\n * engine costs nothing until a source string is genuinely registered.\r\n */\r\nexport function resolveDynamicNodes(\r\n partial: Partial<DynamicNodeConfig> | undefined,\r\n compilerWasmUrl?: string,\r\n): DynamicNodeConfig {\r\n return {\r\n sdk: partial?.sdk ?? sdk,\r\n jsxRuntime: partial?.jsxRuntime ?? jsxRuntime,\r\n modules: partial?.modules,\r\n compile: partial?.compile ?? (async (source) => (await defaultCompiler(compilerWasmUrl))(source)),\r\n };\r\n}\r\n"]}
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,MAAM,EACN,aAAa,EAWb,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,WAAW,EACnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAkC,KAAK,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGhH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,cAAc,CAAC;IACvF;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,GAAG,WAAW,CAAC;IAC5D;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACjC,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,MAAM,EACN,aAAa,EAWb,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,WAAW,EACnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAkC,KAAK,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGhH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,cAAc,CAAC;IACvF;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,GAAG,WAAW,CAAC;IAC5D;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACjC,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AA2GD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACrC,OAAO,GAAE,oBAAyB,GACnC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAgEpC"}
|
package/dist/engine.js
CHANGED
|
@@ -40,6 +40,7 @@ class WebExporter {
|
|
|
40
40
|
scale: request.scale,
|
|
41
41
|
manifest: request.manifest,
|
|
42
42
|
includeAudio: request.includeAudio,
|
|
43
|
+
frames: request.frames,
|
|
43
44
|
video: encode.video ?? this.defaults.video,
|
|
44
45
|
audio: encode.audio ?? this.defaults.audio,
|
|
45
46
|
supersample: encode.supersample,
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,MAAM,GAeT,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,IAAI,eAAe,EAAsD,MAAM,YAAY,CAAC;AAChH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA4CxC;;;;;;;;;;GAUG;AACH,MAAM,WAAW;IAOQ;IACA;IACA;IARb,KAAK,GAAyB,IAAI,CAAC;IACnC,aAAa,GAAoB,IAAI,CAAC;IAC9C,kEAAkE;IAC1D,UAAU,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEzD,YACqB,SAAoB,EACpB,QAAsE,EACtE,OAAgB;QAFhB,cAAS,GAAT,SAAS,CAAW;QACpB,aAAQ,GAAR,QAAQ,CAA8D;QACtE,YAAO,GAAP,OAAO,CAAS;IACjC,CAAC;IAEL,KAAK,CAAC,WAAW,CAAC,OAA2B;QACzC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAyB,CAAC;QAC9D,OAAO,eAAe,CAAC;YACnB,QAAQ,EAAE,OAAO,CAAC,OAAO;YACzB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1C,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK;YAC1C,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,sEAAsE;YACtE,mDAAmD;YACnD,WAAW,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI;YACrC,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,IAAI,CAAC,OAAO;SACxB,CAAC,CAAC;IACP,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,OAA2B;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,OAAO;QACH,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,OAA2B;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE;YACjD,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;SAChC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;YACjC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;SAC3B,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;IACzE,CAAC;IAED;;;;OAIG;IACK,QAAQ,CAAC,OAA2B;QACxC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;QACtG,CAAC;QACD,8DAA8D;QAC9D,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,aAAa,KAAK,QAAQ;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAClE,IAAI,CAAC,KAAK,KAAK,IAAI,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ;SACX,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,OAAO,CAAC,QAAQ;YAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;CACJ;AAED,8EAA8E;AAC9E,SAAS,WAAW,CAAC,KAAkC;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC1D,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACrC,UAAgC,EAAE;IAElC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;IAC/B,4EAA4E;IAC5E,yEAAyE;IACzE,wEAAwE;IACxE,yCAAyC;IACzC,MAAM,UAAU,GAAG,CACf,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAwB,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QACrE,CAAC,CAAC,QAAQ,CACuC,CAAC;IAC1D,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QAC5D,CAAC,CAAC,EAAE,CAAC;IACT,qEAAqE;IACrE,qEAAqE;IACrE,mEAAmE;IACnE,kBAAkB;IAClB,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAExF,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAC5B,SAAS,EACT,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,EAC9C,OAAO,CAAC,OAAO,CAClB,CAAC;IAEF,MAAM,QAAQ,GAAsC;QAChD,oBAAoB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAgB;YACzD,OAAO,IAAI,iBAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QACpE,CAAC;QACD,mBAAmB,CAAC,OAA0B;YAC1C,OAAO,IAAI,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,oBAAoB,CAAC,OAA0B;YAC3C,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,eAAe;YACX,OAAO,IAAI,YAAY,EAAE,CAAC;QAC9B,CAAC;QACD,YAAY,CAAC,GAAW,EAAE,WAAsB;YAC5C,oEAAoE;YACpE,qEAAqE;YACrE,yBAAyB;YACzB,OAAO,IAAI,SAAS,CAAC;gBACjB,OAAO,EAAG,WAA4B,CAAC,UAAU,EAAE;gBACnD,GAAG;aACN,CAAC,CAAC;QACP,CAAC;QACD,cAAc;YACV,OAAO,QAAQ,CAAC;QACpB,CAAC;QACD,OAAO;YACH,QAAQ,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACJ,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAoB,QAAQ,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;IACxG,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QAC/B,gEAAgE;QAChE,+DAA+D;QAC/D,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["import type { CanvasKit } from \"@motionscript/canvaskit\";\nimport {\n AudioSink,\n Engine,\n EngineOptions,\n EnginePlatform,\n EngineRenderContext,\n Exporter,\n ExportStillRequest,\n ExportVideoRequest,\n ExportedStill,\n Registry,\n Ticker,\n MeasureContext2D,\n PlatformDeps,\n type DynamicNodeConfig,\n type NodeConstructor,\n type NodeTypeMap,\n} from \"@motionscript/core\";\n\nimport { getCanvasKit } from \"./getter\";\nimport { resolveDynamicNodes } from \"./dynamic-nodes\";\nimport { WebAudioSink } from \"./audio/player\";\nimport { WebTicker } from \"./ticker\";\nimport { WebMeasureContext } from \"./measure-context\";\nimport { WebRenderContext } from \"./render-context\";\nimport { WebStorageAdapter } from \"./storage-adapter\";\nimport { exportVideo as exportVideoFile, type AudioEncoderOptions, type VideoEncoderOptions } from \"./exporter\";\nimport { StillRenderer } from \"./still\";\n\nexport interface BrowserEngineOptions extends Omit<EngineOptions, \"nodes\" | \"dynamicNodes\"> {\n /**\n * Node types any document opened here may name — decorated classes as an\n * array or a record naming each key outright, same as `EngineOptions`,\n * plus a TS/JSX source string in the **array** form only. A string\n * self-declares its key via `@node(...)` in its own source — the record\n * form stays class-only, so there is exactly one place a dynamic node's\n * key comes from, not two that could disagree.\n */\n nodes?: readonly (NodeConstructor | string)[] | NodeTypeMap;\n /**\n * Enables a source string in `nodes`, and `engine.registerNode(source)`\n * at any point afterward. Any field left unset falls back to `browser`'s\n * own default — `@motionscript/core/sdk` for `sdk`, a lazily-loaded\n * `esbuild-wasm` for `compile` — so this never needs to be passed at all\n * for the common case. `modules` has no default: only the host knows\n * which loaded packages a script it compiles may reach.\n */\n dynamicNodes?: Partial<DynamicNodeConfig>;\n /**\n * Where `esbuild-wasm`'s own `.wasm` binary is served — only read if the\n * default compiler is actually used. Mirrors `wasmUrl` for CanvasKit.\n */\n compilerWasmUrl?: string;\n /** URL of the CanvasKit `.wasm` binary, forwarded to `getCanvasKit`. */\n wasmUrl?: string;\n /** Default video encoder settings for `session.export()`. Overridable per call. */\n video?: VideoEncoderOptions;\n /** Default audio encoder settings for `session.export()`. Overridable per call. */\n audio?: AudioEncoderOptions;\n}\n\n/** What `session.export({ encode })` accepts in this environment. */\nexport interface BrowserEncodeOptions {\n video?: VideoEncoderOptions;\n audio?: AudioEncoderOptions;\n supersample?: number;\n /** Set false to get the bytes back instead of triggering a browser download. */\n download?: boolean;\n filename?: string;\n}\n\n/**\n * The browser's {@link Exporter}: WebCodecs + mediabunny for video, the\n * browser's own image codecs for a still.\n *\n * **It does not reuse a session's storage adapter, and that is deliberate.** An\n * adapter's viewport clamps the resolution images decode at — `StorageAdapter`\n * documents that it has no setter for exactly this reason — so handing an export\n * the preview's adapter would paint a 2× render from decodes sized for 1×, and\n * the result would be soft in a way nothing reports. What is safely shared is\n * CanvasKit itself, which `getCanvasKit` already memoizes process-wide.\n */\nclass WebExporter implements Exporter {\n private still: StillRenderer | null = null;\n private stillRegistry: Registry | null = null;\n /** Settles once every still asked for so far has been encoded. */\n private stillQueue: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly canvasKit: CanvasKit,\n private readonly defaults: { video?: VideoEncoderOptions; audio?: AudioEncoderOptions },\n private readonly wasmUrl?: string,\n ) { }\n\n async exportVideo(request: ExportVideoRequest): Promise<Uint8Array | void> {\n const encode = (request.encode ?? {}) as BrowserEncodeOptions;\n return exportVideoFile({\n document: request.project,\n registry: request.registry,\n scale: request.scale,\n manifest: request.manifest,\n includeAudio: request.includeAudio,\n video: encode.video ?? this.defaults.video,\n audio: encode.audio ?? this.defaults.audio,\n supersample: encode.supersample,\n filename: encode.filename,\n // A `Session` asks for bytes; only an explicit `download: true` takes\n // the DOM anchor path this function offers a page.\n returnBytes: encode.download !== true,\n onProgress: request.onProgress,\n signal: request.signal,\n wasmUrl: this.wasmUrl,\n });\n }\n\n /**\n * Queued, rather than a renderer per call: the one renderer is what keeps a\n * GL context and the decode cache warm between stills, and its draw and the\n * encode of it are two steps another call's draw would land between.\n */\n exportStill(request: ExportStillRequest): Promise<ExportedStill> {\n const still = this.stillQueue.then(() => this.drawStill(request));\n this.stillQueue = still.catch(() => undefined);\n return still;\n }\n\n dispose(): void {\n this.still?.dispose();\n this.still = null;\n }\n\n private async drawStill(request: ExportStillRequest): Promise<ExportedStill> {\n const renderer = this.renderer(request);\n const drawn = await renderer.render(request.project, {\n scale: request.scale,\n ...frameOption(request.frame),\n });\n const bytes = await renderer.toBytes({\n format: request.format,\n quality: request.quality,\n });\n return { bytes, frame: drawn.frame, totalFrames: drawn.totalFrames };\n }\n\n /**\n * One `StillRenderer`, reused across calls and re-viewported when the ask\n * changes — `setViewport` is a no-op when the size already matches, and a\n * full reset when it does not, which is the same trade the class documents.\n */\n private renderer(request: ExportStillRequest): StillRenderer {\n const { project, registry } = request;\n if (project.viewport === undefined || project.fps === undefined) {\n throw new Error(\"exportStill takes a project whose fps and viewport are resolved — see Session.\");\n }\n // A renderer compiles against the registry it was built with.\n if (this.still && this.stillRegistry !== registry) this.dispose();\n this.still ??= new StillRenderer(this.canvasKit, {\n viewport: project.viewport,\n fps: project.fps,\n manifest: request.manifest,\n registry,\n });\n this.stillRegistry = registry;\n this.still.setViewport(project.viewport);\n if (request.manifest) this.still.setManifest(request.manifest);\n return this.still;\n }\n}\n\n/** `ExportStillRequest.frame` in the shape `StillRenderer.render` accepts. */\nfunction frameOption(frame: ExportStillRequest[\"frame\"]): { frame?: number | \"first\" | \"last\" } {\n if (frame.kind === \"frame\") return { frame: frame.frame };\n return { frame: frame.kind };\n}\n\n/**\n * An engine backed by the browser: WebGL/Skia surfaces, Web Audio, WebCodecs.\n *\n * Async because CanvasKit is a wasm module and every seam below needs it. Await\n * once, at app start, and share the result — the caches underneath (registered\n * fonts, decoded images, shaped paragraphs) are the point of keeping one engine\n * rather than one per canvas.\n *\n * ```ts\n * const engine = await createBrowserEngine({ nodes: { pulseRing: PulseRing } });\n * const session = engine.open({ document, target: canvasEl });\n * session.playback!.play();\n * ```\n */\nexport async function createBrowserEngine(\n options: BrowserEngineOptions = {},\n): Promise<Engine<WebStorageAdapter>> {\n const rawNodes = options.nodes;\n // `Array.isArray` doesn't narrow a `readonly T[] | U` union cleanly against\n // its `arg is any[]` (mutable) predicate — the `.filter` predicate below\n // already guarantees no string survives, so the cast is just recovering\n // what the runtime check already proved.\n const classNodes = (\n Array.isArray(rawNodes)\n ? rawNodes.filter((n): n is NodeConstructor => typeof n !== \"string\")\n : rawNodes\n ) as NodeTypeMap | readonly NodeConstructor[] | undefined;\n const sourceNodes = Array.isArray(rawNodes)\n ? rawNodes.filter((n): n is string => typeof n === \"string\")\n : [];\n // Cheap — see dynamic-nodes.ts: no WASM import happens here, only on\n // `compile`'s first actual call. Attached unconditionally so a later\n // `engine.registerNode(source)` works even when `nodes` carried no\n // string at boot.\n const dynamicNodes = resolveDynamicNodes(options.dynamicNodes, options.compilerWasmUrl);\n\n const canvasKit = await getCanvasKit(options.wasmUrl);\n const exporter = new WebExporter(\n canvasKit,\n { video: options.video, audio: options.audio },\n options.wasmUrl,\n );\n\n const platform: EnginePlatform<WebStorageAdapter> = {\n createStorageAdapter({ catalog, viewport, fps }: PlatformDeps): WebStorageAdapter {\n return new WebStorageAdapter(canvasKit, catalog, viewport, fps);\n },\n createRenderContext(storage: WebStorageAdapter): EngineRenderContext {\n return new WebRenderContext(canvasKit, storage);\n },\n createMeasureContext(storage: WebStorageAdapter): MeasureContext2D {\n return new WebMeasureContext(storage);\n },\n createAudioSink(): AudioSink {\n return new WebAudioSink();\n },\n createTicker(fps: number, audioDevice: AudioSink): Ticker {\n // Shares the device's own `AudioContext`: two independent timebases\n // in one session drift against each other, and sound is the one that\n // cannot be nudged back.\n return new WebTicker({\n context: (audioDevice as WebAudioSink).getContext(),\n fps,\n });\n },\n createExporter(): Exporter {\n return exporter;\n },\n dispose(): void {\n exporter.dispose();\n },\n };\n\n const engine = new Engine<WebStorageAdapter>(platform, { ...options, nodes: classNodes, dynamicNodes });\n for (const source of sourceNodes) {\n // Sequential — one script's error shouldn't mask another's, and\n // registration order should match array order for determinism.\n await engine.registerNode(source);\n }\n return engine;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CanvasKit } from \"@motionscript/canvaskit\";\r\nimport {\r\n AudioSink,\r\n Engine,\r\n EngineOptions,\r\n EnginePlatform,\r\n EngineRenderContext,\r\n Exporter,\r\n ExportStillRequest,\r\n ExportVideoRequest,\r\n ExportedStill,\r\n Registry,\r\n Ticker,\r\n MeasureContext2D,\r\n PlatformDeps,\r\n type DynamicNodeConfig,\r\n type NodeConstructor,\r\n type NodeTypeMap,\r\n} from \"@motionscript/core\";\r\n\r\nimport { getCanvasKit } from \"./getter\";\r\nimport { resolveDynamicNodes } from \"./dynamic-nodes\";\r\nimport { WebAudioSink } from \"./audio/player\";\r\nimport { WebTicker } from \"./ticker\";\r\nimport { WebMeasureContext } from \"./measure-context\";\r\nimport { WebRenderContext } from \"./render-context\";\r\nimport { WebStorageAdapter } from \"./storage-adapter\";\r\nimport { exportVideo as exportVideoFile, type AudioEncoderOptions, type VideoEncoderOptions } from \"./exporter\";\r\nimport { StillRenderer } from \"./still\";\r\n\r\nexport interface BrowserEngineOptions extends Omit<EngineOptions, \"nodes\" | \"dynamicNodes\"> {\r\n /**\r\n * Node types any document opened here may name — decorated classes as an\r\n * array or a record naming each key outright, same as `EngineOptions`,\r\n * plus a TS/JSX source string in the **array** form only. A string\r\n * self-declares its key via `@node(...)` in its own source — the record\r\n * form stays class-only, so there is exactly one place a dynamic node's\r\n * key comes from, not two that could disagree.\r\n */\r\n nodes?: readonly (NodeConstructor | string)[] | NodeTypeMap;\r\n /**\r\n * Enables a source string in `nodes`, and `engine.registerNode(source)`\r\n * at any point afterward. Any field left unset falls back to `browser`'s\r\n * own default — `@motionscript/core/sdk` for `sdk`, a lazily-loaded\r\n * `esbuild-wasm` for `compile` — so this never needs to be passed at all\r\n * for the common case. `modules` has no default: only the host knows\r\n * which loaded packages a script it compiles may reach.\r\n */\r\n dynamicNodes?: Partial<DynamicNodeConfig>;\r\n /**\r\n * Where `esbuild-wasm`'s own `.wasm` binary is served — only read if the\r\n * default compiler is actually used. Mirrors `wasmUrl` for CanvasKit.\r\n */\r\n compilerWasmUrl?: string;\r\n /** URL of the CanvasKit `.wasm` binary, forwarded to `getCanvasKit`. */\r\n wasmUrl?: string;\r\n /** Default video encoder settings for `session.export()`. Overridable per call. */\r\n video?: VideoEncoderOptions;\r\n /** Default audio encoder settings for `session.export()`. Overridable per call. */\r\n audio?: AudioEncoderOptions;\r\n}\r\n\r\n/** What `session.export({ encode })` accepts in this environment. */\r\nexport interface BrowserEncodeOptions {\r\n video?: VideoEncoderOptions;\r\n audio?: AudioEncoderOptions;\r\n supersample?: number;\r\n /** Set false to get the bytes back instead of triggering a browser download. */\r\n download?: boolean;\r\n filename?: string;\r\n}\r\n\r\n/**\r\n * The browser's {@link Exporter}: WebCodecs + mediabunny for video, the\r\n * browser's own image codecs for a still.\r\n *\r\n * **It does not reuse a session's storage adapter, and that is deliberate.** An\r\n * adapter's viewport clamps the resolution images decode at — `StorageAdapter`\r\n * documents that it has no setter for exactly this reason — so handing an export\r\n * the preview's adapter would paint a 2× render from decodes sized for 1×, and\r\n * the result would be soft in a way nothing reports. What is safely shared is\r\n * CanvasKit itself, which `getCanvasKit` already memoizes process-wide.\r\n */\r\nclass WebExporter implements Exporter {\r\n private still: StillRenderer | null = null;\r\n private stillRegistry: Registry | null = null;\r\n /** Settles once every still asked for so far has been encoded. */\r\n private stillQueue: Promise<unknown> = Promise.resolve();\r\n\r\n constructor(\r\n private readonly canvasKit: CanvasKit,\r\n private readonly defaults: { video?: VideoEncoderOptions; audio?: AudioEncoderOptions },\r\n private readonly wasmUrl?: string,\r\n ) { }\r\n\r\n async exportVideo(request: ExportVideoRequest): Promise<Uint8Array | void> {\r\n const encode = (request.encode ?? {}) as BrowserEncodeOptions;\r\n return exportVideoFile({\r\n document: request.project,\r\n registry: request.registry,\r\n scale: request.scale,\r\n manifest: request.manifest,\r\n includeAudio: request.includeAudio,\r\n frames: request.frames,\r\n video: encode.video ?? this.defaults.video,\r\n audio: encode.audio ?? this.defaults.audio,\r\n supersample: encode.supersample,\r\n filename: encode.filename,\r\n // A `Session` asks for bytes; only an explicit `download: true` takes\r\n // the DOM anchor path this function offers a page.\r\n returnBytes: encode.download !== true,\r\n onProgress: request.onProgress,\r\n signal: request.signal,\r\n wasmUrl: this.wasmUrl,\r\n });\r\n }\r\n\r\n /**\r\n * Queued, rather than a renderer per call: the one renderer is what keeps a\r\n * GL context and the decode cache warm between stills, and its draw and the\r\n * encode of it are two steps another call's draw would land between.\r\n */\r\n exportStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const still = this.stillQueue.then(() => this.drawStill(request));\r\n this.stillQueue = still.catch(() => undefined);\r\n return still;\r\n }\r\n\r\n dispose(): void {\r\n this.still?.dispose();\r\n this.still = null;\r\n }\r\n\r\n private async drawStill(request: ExportStillRequest): Promise<ExportedStill> {\r\n const renderer = this.renderer(request);\r\n const drawn = await renderer.render(request.project, {\r\n scale: request.scale,\r\n ...frameOption(request.frame),\r\n });\r\n const bytes = await renderer.toBytes({\r\n format: request.format,\r\n quality: request.quality,\r\n });\r\n return { bytes, frame: drawn.frame, totalFrames: drawn.totalFrames };\r\n }\r\n\r\n /**\r\n * One `StillRenderer`, reused across calls and re-viewported when the ask\r\n * changes — `setViewport` is a no-op when the size already matches, and a\r\n * full reset when it does not, which is the same trade the class documents.\r\n */\r\n private renderer(request: ExportStillRequest): StillRenderer {\r\n const { project, registry } = request;\r\n if (project.viewport === undefined || project.fps === undefined) {\r\n throw new Error(\"exportStill takes a project whose fps and viewport are resolved — see Session.\");\r\n }\r\n // A renderer compiles against the registry it was built with.\r\n if (this.still && this.stillRegistry !== registry) this.dispose();\r\n this.still ??= new StillRenderer(this.canvasKit, {\r\n viewport: project.viewport,\r\n fps: project.fps,\r\n manifest: request.manifest,\r\n registry,\r\n });\r\n this.stillRegistry = registry;\r\n this.still.setViewport(project.viewport);\r\n if (request.manifest) this.still.setManifest(request.manifest);\r\n return this.still;\r\n }\r\n}\r\n\r\n/** `ExportStillRequest.frame` in the shape `StillRenderer.render` accepts. */\r\nfunction frameOption(frame: ExportStillRequest[\"frame\"]): { frame?: number | \"first\" | \"last\" } {\r\n if (frame.kind === \"frame\") return { frame: frame.frame };\r\n return { frame: frame.kind };\r\n}\r\n\r\n/**\r\n * An engine backed by the browser: WebGL/Skia surfaces, Web Audio, WebCodecs.\r\n *\r\n * Async because CanvasKit is a wasm module and every seam below needs it. Await\r\n * once, at app start, and share the result — the caches underneath (registered\r\n * fonts, decoded images, shaped paragraphs) are the point of keeping one engine\r\n * rather than one per canvas.\r\n *\r\n * ```ts\r\n * const engine = await createBrowserEngine({ nodes: { pulseRing: PulseRing } });\r\n * const session = engine.open({ document, target: canvasEl });\r\n * session.playback!.play();\r\n * ```\r\n */\r\nexport async function createBrowserEngine(\r\n options: BrowserEngineOptions = {},\r\n): Promise<Engine<WebStorageAdapter>> {\r\n const rawNodes = options.nodes;\r\n // `Array.isArray` doesn't narrow a `readonly T[] | U` union cleanly against\r\n // its `arg is any[]` (mutable) predicate — the `.filter` predicate below\r\n // already guarantees no string survives, so the cast is just recovering\r\n // what the runtime check already proved.\r\n const classNodes = (\r\n Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is NodeConstructor => typeof n !== \"string\")\r\n : rawNodes\r\n ) as NodeTypeMap | readonly NodeConstructor[] | undefined;\r\n const sourceNodes = Array.isArray(rawNodes)\r\n ? rawNodes.filter((n): n is string => typeof n === \"string\")\r\n : [];\r\n // Cheap — see dynamic-nodes.ts: no WASM import happens here, only on\r\n // `compile`'s first actual call. Attached unconditionally so a later\r\n // `engine.registerNode(source)` works even when `nodes` carried no\r\n // string at boot.\r\n const dynamicNodes = resolveDynamicNodes(options.dynamicNodes, options.compilerWasmUrl);\r\n\r\n const canvasKit = await getCanvasKit(options.wasmUrl);\r\n const exporter = new WebExporter(\r\n canvasKit,\r\n { video: options.video, audio: options.audio },\r\n options.wasmUrl,\r\n );\r\n\r\n const platform: EnginePlatform<WebStorageAdapter> = {\r\n createStorageAdapter({ catalog, viewport, fps }: PlatformDeps): WebStorageAdapter {\r\n return new WebStorageAdapter(canvasKit, catalog, viewport, fps);\r\n },\r\n createRenderContext(storage: WebStorageAdapter): EngineRenderContext {\r\n return new WebRenderContext(canvasKit, storage);\r\n },\r\n createMeasureContext(storage: WebStorageAdapter): MeasureContext2D {\r\n return new WebMeasureContext(storage);\r\n },\r\n createAudioSink(): AudioSink {\r\n return new WebAudioSink();\r\n },\r\n createTicker(fps: number, audioDevice: AudioSink): Ticker {\r\n // Shares the device's own `AudioContext`: two independent timebases\r\n // in one session drift against each other, and sound is the one that\r\n // cannot be nudged back.\r\n return new WebTicker({\r\n context: (audioDevice as WebAudioSink).getContext(),\r\n fps,\r\n });\r\n },\r\n createExporter(): Exporter {\r\n return exporter;\r\n },\r\n dispose(): void {\r\n exporter.dispose();\r\n },\r\n };\r\n\r\n const engine = new Engine<WebStorageAdapter>(platform, { ...options, nodes: classNodes, dynamicNodes });\r\n for (const source of sourceNodes) {\r\n // Sequential — one script's error shouldn't mask another's, and\r\n // registration order should match array order for determinism.\r\n await engine.registerNode(source);\r\n }\r\n return engine;\r\n}\r\n"]}
|
package/dist/exporter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Registry, type AssetManifest, type ProjectDocument, type
|
|
1
|
+
import { Registry, type AssetManifest, type FrameRange, type ProjectDocument, type VisualDocument, type Size2D, type Theme } from "@motionscript/core";
|
|
2
2
|
import { type Quality } from 'mediabunny';
|
|
3
3
|
/** Reports export progress in [0, 1]; video encoding occupies most of the range, audio mixing/finalize the tail. */
|
|
4
4
|
export type ExportProgressCallback = (progress: number) => void;
|
|
@@ -95,7 +95,7 @@ export type AudioEncoderOptions = {
|
|
|
95
95
|
};
|
|
96
96
|
export type ExportParams = {
|
|
97
97
|
/** A project, or a lone still or composition as the one track of one. */
|
|
98
|
-
document: ProjectDocument |
|
|
98
|
+
document: ProjectDocument | VisualDocument;
|
|
99
99
|
/** The vocabulary its clips compile against. Defaults to what the library ships. */
|
|
100
100
|
registry?: Registry;
|
|
101
101
|
/** Win over the document's own; 1920×1080 at 60 fps when neither says. */
|
|
@@ -115,6 +115,12 @@ export type ExportParams = {
|
|
|
115
115
|
video?: VideoEncoderOptions;
|
|
116
116
|
/** Audio encoder settings; ignored when `includeAudio` is false. */
|
|
117
117
|
audio?: AudioEncoderOptions;
|
|
118
|
+
/**
|
|
119
|
+
* Encode only these frames (`end` excluded), timestamped from 0 and silent:
|
|
120
|
+
* one segment of a split export, for `concatVideoSegments` to join. Mix
|
|
121
|
+
* the audio once with `mixTimelineAudio` instead.
|
|
122
|
+
*/
|
|
123
|
+
frames?: FrameRange;
|
|
118
124
|
onProgress?: ExportProgressCallback;
|
|
119
125
|
signal?: AbortSignal;
|
|
120
126
|
wasmUrl?: string;
|
package/dist/exporter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exporter.d.ts","sourceRoot":"","sources":["../src/exporter.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"exporter.d.ts","sourceRoot":"","sources":["../src/exporter.ts"],"names":[],"mappings":"AAAA,OAAO,EACmC,QAAQ,EAA0B,KAAK,aAAa,EAC1F,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,KAAK,KAAK,EACtF,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAOH,KAAK,OAAO,EAEf,MAAM,YAAY,CAAC;AAMpB,oHAAoH;AACpH,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACvC;;;;;;;;;;;;;;;OAeG;IACH,oBAAoB,CAAC,EAAE,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;IAC/E;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,6FAA6F;IAC7F,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACtC;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACrC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAsBF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGxF;AAED,4CAA4C;AAC5C,MAAM,MAAM,mBAAmB,GAAG;IAC9B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAQF,MAAM,MAAM,YAAY,GAAG;IACvB,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,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,yDAAyD;IACzD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gFAAgF;IAChF,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,oEAAoE;IACpE,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAA;AAgKD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuFlF"}
|