@grundyjs/canvas-take 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +90 -5
- package/dist/aac-PROVENANCE.md +171 -0
- package/dist/aac-licenses/COPYING.LGPLv2.1 +502 -0
- package/dist/aac-licenses/LICENSE.md +127 -0
- package/dist/aac-licenses/MPL-2.0.txt +373 -0
- package/dist/aac-licenses/bridge.c +191 -0
- package/dist/gif.d.ts +6 -0
- package/dist/gif.js +16 -2
- package/dist/index.d.ts +8 -1
- package/dist/index.js +90 -6
- package/dist/mediabunny-LICENSE.txt +373 -0
- package/dist/video-aac-worker.d.ts +1 -0
- package/dist/video-aac-worker.js +3356 -0
- package/dist/video-aac-worker.js.map +7 -0
- package/dist/video-aac.d.ts +8 -0
- package/dist/video-aac.js +174 -0
- package/dist/video-mp4.d.ts +3 -0
- package/dist/video-mp4.js +38 -0
- package/dist/video-mp4.js.map +7 -0
- package/dist/video-webcodecs.d.ts +3 -0
- package/dist/video-webcodecs.js +149 -0
- package/dist/video.d.ts +215 -0
- package/dist/video.js +503 -0
- package/docs/drafts/offline-video-api.d.ts +2 -0
- package/docs/drafts/offline-video-example.ts +38 -0
- package/docs/drafts/tsconfig.json +12 -0
- package/docs/gif-benchmark.md +129 -0
- package/docs/gif-encoder.md +6 -0
- package/docs/offline-aac.md +87 -0
- package/docs/offline-video-adapters.md +84 -0
- package/docs/offline-video-api.md +154 -0
- package/docs/video-recording.md +55 -0
- package/package.json +48 -7
- package/ru/README.md +166 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.5.0 — 2026-11-02
|
|
6
|
+
|
|
7
|
+
- Add offline video export through `/video` with caller-selected encoder/muxer factories, explicit frame/sample timing, prepared PCM, progress, bounded queues, limits and cancellation-safe resource ownership.
|
|
8
|
+
- Add separate WebCodecs H.264 Baseline, AAC-LC WASM Worker and MP4 adapters. Support 44.1/48 kHz mono/stereo audio, packet-derived priming, MP4 edit lists and final sample trimming; document decoder padding behavior, timebase precision, runtime assets and bundled licenses.
|
|
9
|
+
- Add recording `pause()`/`resume()`, a pause-aware `maxDurationMs`, the `finished` promise and `stopReason`, plus an audio/video browser example.
|
|
10
|
+
- Add GIF `maxFramePixels`, `maxFrames` and `maxDurationMs` limits, checked before readback, with terminal cleanup and actionable retry controls in the example.
|
|
11
|
+
- Skip blending for fully opaque GIF pixels. Add reproducible sync/Worker benchmarks, opacity scenes and paired gradient-regression tools/results, including measurement-noise caveats.
|
|
12
|
+
- Verify actual H.264/AAC and WebM recordings, offline MP4 sample counts and A/V markers with independent FFmpeg decoding, cancellation/retry, packed TypeScript consumers and browser CI. Add a packed-package 1080×1920 H.264/AAC release smoke test.
|
|
13
|
+
|
|
14
|
+
## 0.4.1 — 2026-10-28
|
|
15
|
+
|
|
16
|
+
- Correct package homepage to the CanvasTake page; refresh package description, keywords and English/Russian README navigation and test instructions.
|
|
17
|
+
|
|
18
|
+
- Browser CI tests in Chromium and Firefox exercise the real GIF module Worker, cancellation and a fresh export on the same page. Downloaded GIFs are independently decoded to verify pixels, dimensions, frame timings and loop count.
|
|
19
|
+
|
|
3
20
|
## 0.4.0 — 2026-10-26
|
|
4
21
|
|
|
5
22
|
- Ready-made `gif/worker` adapter with a dedicated module Worker, transferable frames, request timeouts and cleanup on completion/cancellation/errors.
|
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[Русский](ru/README.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[Website](https://grundyjs.ru/en/canvas-take/) · [npm](https://www.npmjs.com/package/@grundyjs/canvas-take) · [Issues](https://github.com/urffin/canvas-take/issues)
|
|
6
|
+
|
|
7
|
+
Independent TypeScript library for HTML canvas recording with audio, offline H.264/AAC MP4 and GIF export through separate entry points. ESM with TypeScript declarations, no external runtime npm dependencies or dependency on AlgiViz. CanvasTake source is MIT licensed; optional adapters include [Mediabunny MPL-2.0](docs/offline-video-adapters.md#distribution-and-sources) and [AAC WASM / FFmpeg LGPL components](docs/offline-aac.md#distribution-and-source). The package does not start or draw your animation.
|
|
6
8
|
|
|
7
9
|
## Install
|
|
8
10
|
|
|
@@ -25,9 +27,9 @@ recorder.dispose();
|
|
|
25
27
|
|
|
26
28
|
`start` resolves after the MediaRecorder start event. `stop` waits for the final dataavailable and stop events; repeated calls return the same promise. Errors reject pending operations, stop owned tracks and release chunks. `dispose` cancels an unfinished recording; it is idempotent. Each instance records once. State stays idle while start is pending; a second start is rejected.
|
|
27
29
|
|
|
28
|
-
Auto format order: WebM VP9, VP8, generic WebM, MP4. With audio, the VP9 and VP8 candidates include Opus. An explicit unsupported MIME fails without silently changing the format. The result reports the actual MIME and matching extension. FPS and bitrate are targets, not guarantees. durationMs measures recording
|
|
30
|
+
Auto format order: WebM VP9, VP8, generic WebM, MP4. With audio, the VP9 and VP8 candidates include Opus. An explicit unsupported MIME fails without silently changing the format. The result reports the actual MIME and matching extension. FPS and bitrate are targets, not guarantees. `durationMs` measures active recording time, excluding pauses; it is not parsed file duration.
|
|
29
31
|
|
|
30
|
-
Keep canvas bitmap dimensions fixed from start until stop. CSS scaling is allowed. Size changes fail the recording. Canvas must remain origin-clean.
|
|
32
|
+
Keep canvas bitmap dimensions fixed from start until stop, including while paused. CSS scaling is allowed. Size changes fail the recording. Canvas must remain origin-clean. The video recorder does not support screen capture, frame-by-frame video encoding or server upload. Chunks are retained in memory: use a duration limit or stop explicitly. If background-tab throttling is unacceptable, the caller should dispose when the page becomes hidden. Codec support depends on the browser and available resources.
|
|
31
33
|
|
|
32
34
|
Development: Node.js 22+, `npm install`, `npm test`, `npm pack`. ESM and declarations are emitted to dist. `npm pack` rebuilds automatically; `npm publish` runs tests before packing. The integration example expects AlgiViz in a sibling directory and is served by the included local server.
|
|
33
35
|
|
|
@@ -42,8 +44,36 @@ MediaRecorder WebM output may omit duration metadata: playback can work while th
|
|
|
42
44
|
| `videoBitsPerSecond` | Optional positive finite encoder bitrate target |
|
|
43
45
|
| `mimeType` | Optional explicit supported `video/webm` or `video/mp4` MIME, including codecs |
|
|
44
46
|
| `audioTracks` | Optional readonly list of live audio `MediaStreamTrack`s; omitted or empty records silently |
|
|
47
|
+
| `maxDurationMs` | Since 0.5.0: optional positive safe integer; active-time budget in ms, excluding pauses |
|
|
48
|
+
|
|
49
|
+
`stop()` returns `{ blob, mimeType, extension, durationMs, stopReason }` (`stopReason` is available since 0.5.0). Keep the recorder in a try/finally and call `dispose()` even if your animation fails. Revoke object URLs when no longer needed.
|
|
50
|
+
|
|
51
|
+
### Pause, resume and active-time limit (since 0.5.0)
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const recorder = createCanvasRecorder(canvas, { fps: 30, maxDurationMs: 30_000 });
|
|
55
|
+
try {
|
|
56
|
+
await recorder.start();
|
|
57
|
+
// In your UI handlers: await recorder.pause(); await recorder.resume();
|
|
58
|
+
const result = await recorder.finished;
|
|
59
|
+
// finished waits for automatic completion; calling stop() would stop immediately.
|
|
60
|
+
console.log(result.stopReason, result.durationMs); // 'duration-limit' or 'manual'
|
|
61
|
+
} finally {
|
|
62
|
+
recorder.dispose();
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`pause()` and `resume()` resolve after their native events. State changes between `recording` and `paused` at those events; the native recorder can change state earlier. Repeated calls toward the current stable state succeed without further action; repeated calls to the same pending operation return the same promise. An opposite operation while one is pending rejects. Calls before start completes, during stopping, or after completion/disposal reject. Stopping from a pause succeeds. Stopping, disposal or failure rejects an unfinished pause/resume operation; normal stopping still produces the final video.
|
|
67
|
+
|
|
68
|
+
`finished` is a stable promise available from creation. It resolves with the final result on manual or automatic completion, and rejects on failure/disposal. `stop()` remains idempotent and returns that same result. `stopReason` is `manual` or `duration-limit`, according to the first stop request. Without `maxDurationMs` there is no automatic stop. Invalid limits throw before capture; supplied limits are snapshotted at creation.
|
|
45
69
|
|
|
46
|
-
|
|
70
|
+
The budget starts at the start event, stops counting at the pause event, and resumes at the resume event. The limit requests normal stopping and retains the final chunk; it does not discard the recording. Timers, event delivery and encoding can be delayed, so this is not a frame-exact file-duration cap. The active time reported in `durationMs` can exceed the requested limit under load. Pause/stop never stop caller-owned audio tracks or pause your animation/narration source; coordinate those separately if their timeline should pause too. Owned capture tracks and audio clones are stopped, and timers/listeners/chunks released, on completion, failure or disposal.
|
|
71
|
+
|
|
72
|
+
Build and serve `examples/video.html` with the example server for format selection, a generated tone, pause/resume, manual stop, an active-time limit, preview and download. Browser tests record and play real WebM and supported H.264/AAC MP4, check decoded audio, pause exclusion and track ownership; unavailable codecs are explicitly skipped. Native event behavior is specified by [MediaStream Recording](https://www.w3.org/TR/mediastream-recording/#methods).
|
|
73
|
+
|
|
74
|
+
The [video verification notes](docs/video-recording.md) describe six flash/beep markers across two pauses, independent FFmpeg decoding with container timestamps, and a negative control with intentionally delayed audio. FFmpeg is a development-only dependency.
|
|
75
|
+
|
|
76
|
+
Initial captured video material can precede the native start event, especially while an audio engine starts. The active-time budget does not trim such startup material from the file; file duration can therefore include additional startup time as well as finalization delay.
|
|
47
77
|
|
|
48
78
|
## Record with narration
|
|
49
79
|
|
|
@@ -90,6 +120,25 @@ For a browser smoke test, run `node examples/server.cjs` after building and open
|
|
|
90
120
|
|
|
91
121
|
The audio track list is copied at creation and validated again at start. CanvasTake clones the tracks at start and stops only its clones and canvas capture tracks on completion, disposal or failure. The caller owns the original tracks and AudioContext. Unexpected termination of a recorded clone fails recording. Calling `stop()` on an original track does not stop its clone; call `recorder.stop()` to finish the recording. Finishing an AudioBufferSource does not end the destination track either.
|
|
92
122
|
|
|
123
|
+
## Offline video controller (since 0.5.0)
|
|
124
|
+
|
|
125
|
+
`@grundyjs/canvas-take/video` exports `createOfflineVideoExporter` and adapter contracts for offline frame/audio timelines. Supply separate encoder and muxer factories. Optional `/video/webcodecs` (`createWebCodecsVideoEncoder`), `/video/aac` (`createAacAudioEncoder`) and `/video/mp4` (`createMp4Muxer`) entries export real **H.264 Baseline / AAC-LC MP4**, with optional prepared PCM audio. Video requires native WebCodecs support; AAC runs in a dedicated Worker using a bundled libavcodec WASM backend, including where native AAC encoding is unavailable. Independent FFmpeg decoding checks sample counts, priming, padding and A/V timing.
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
import { createOfflineVideoExporter } from '@grundyjs/canvas-take/video';
|
|
129
|
+
// options supplies video.encoder/config, optional prepared audio, and muxer.
|
|
130
|
+
const exporter = await createOfflineVideoExporter(options);
|
|
131
|
+
try {
|
|
132
|
+
drawFrame(0);
|
|
133
|
+
await exporter.addFrame(canvas, { timestampUs: 0, durationUs: 33_333 });
|
|
134
|
+
const result = await exporter.finish();
|
|
135
|
+
} finally {
|
|
136
|
+
exporter.dispose();
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Frames form a contiguous timeline from zero in integer microseconds; durations and audio sample positions do not depend on encoding speed. The controller snapshots inputs, limits queues, serializes muxing and releases owned adapters on completion/failure/abort. Audio uses an explicit strict or trim/pad policy. See [real adapters, usage and timing precision](docs/offline-video-adapters.md), [AAC delay and sample-count verification](docs/offline-aac.md), the [runnable example](examples/offline-video.html) and [API details](docs/offline-video-api.md). Video boundaries use 57,600 Hz ticks; audio uses sample positions, a priming edit and a shortened final sample duration. MP4 presentation length excludes AAC padding; raw decoders that return full AAC frames must apply the container endpoint.
|
|
141
|
+
|
|
93
142
|
## Offline GIF export
|
|
94
143
|
|
|
95
144
|
Available since version 0.3.0.
|
|
@@ -158,7 +207,33 @@ The default Worker URL is resolved relative to the adapter module. For direct br
|
|
|
158
207
|
|
|
159
208
|
Progress works with every encoder, not just the Worker adapter. Immutable snapshots arrive after each completed frame (`encoding`), before finishing (`finalizing`), and at `finished`, `failed`, `aborted` or unfinished `disposed`. `framesEncoded` and `durationMs` include only successfully encoded frames, using rounded GIF durations. `totalFrames` is an optional positive-integer display hint, not a limit; absent hints are omitted from events. No percentage or ETA is guessed; a full frame count does not mean finalization is complete. There is no initial event. Observer exceptions are ignored; aborting or disposing from an observer is supported. Disposing after a terminal outcome emits no additional event.
|
|
160
209
|
|
|
161
|
-
The offline `examples/gif.html`
|
|
210
|
+
The offline `examples/gif.html` demonstrates this adapter, progress and cancellation. Its limit controls (since 0.5.0) default to the example's exact 800×450, 121-frame, 8,000 ms output, including the final hold. Empty fields omit limits. A limit error explains what to increase, restores the controls for retry, and clears any previous download/preview when a new attempt starts.
|
|
211
|
+
|
|
212
|
+
### GIF limits (since 0.5.0)
|
|
213
|
+
|
|
214
|
+
Set optional limits on `createGifExporter`; they apply to every encoder, including the synchronous and Worker adapters:
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
const gif = createGifExporter({
|
|
218
|
+
encoder: createGifWorkerEncoder(),
|
|
219
|
+
maxFramePixels: 1920 * 1080,
|
|
220
|
+
maxFrames: 600,
|
|
221
|
+
maxDurationMs: 30_000
|
|
222
|
+
});
|
|
223
|
+
try {
|
|
224
|
+
await gif.addFrame(canvas, 50);
|
|
225
|
+
const blob = await gif.finish();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
// A limit violation rejects addFrame with RangeError naming the limit.
|
|
228
|
+
console.error(error);
|
|
229
|
+
} finally {
|
|
230
|
+
gif.dispose();
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
All three values must be positive safe integers; omitting one leaves that dimension unlimited by this API (existing GIF format constraints still apply). Bounds are inclusive: `maxFramePixels` checks `width * height`, `maxFrames` counts successfully encoded frames, and `maxDurationMs` sums frame delays **after rounding each to GIF's 10 ms units**, excluding playback repetitions. For example, two 54 ms frames consume 100 ms; a 55 ms frame consumes 60 ms. `totalFrames` remains a display hint independent of `maxFrames`.
|
|
235
|
+
|
|
236
|
+
Invalid limit values throw `RangeError` during creation, before allocating an encoder/Worker. Exceeding a limit rejects `addFrame` before reading pixels or submitting the rejected frame. The exporter enters terminal `failed`, emits one failed progress event with only previously completed frames/duration, detaches its abort listener and disposes its encoder (terminating the owned Worker). `finish` cannot return a partial GIF after this failure; create a fresh exporter to retry. Source canvases are retained. These limits bound input work, not exact memory usage or output byte size.
|
|
162
237
|
|
|
163
238
|
### Supplying another encoder
|
|
164
239
|
|
|
@@ -193,6 +268,16 @@ Recording requires a browser with `canvas.captureStream`, `MediaRecorder` and `M
|
|
|
193
268
|
|
|
194
269
|
The integration example was verified with an actual VP9 recording at 1920×1080. This is not a promise that every browser can produce WebM or MP4. A canvas containing cross-origin content without CORS may not be captured.
|
|
195
270
|
|
|
271
|
+
## Development checks
|
|
272
|
+
|
|
273
|
+
Run `npm test` for unit and Node Worker tests, and `npm run test:package` for an isolated packed consumer. For real browser tests, first run `npx playwright install chromium firefox`, then `npm run test:browser`. These check recording, offline MP4/AAC and GIF export, cancellation/retry, resource cleanup and independently decoded timing/audio. `npm run test:next` checks the packed library with Next.js/Turbopack. See [GIF verification](docs/gif-encoder.md#verification) and [AAC/sample-count verification](docs/offline-aac.md#independent-verification).
|
|
274
|
+
|
|
275
|
+
`npm run test:release-video` packs and installs the candidate, then exports a 10-second 1080×1920 H.264/AAC video from only the installed assets. It checks all 300 decoded frames, 480,000 presentation audio samples, playback, dimensions and four independent A/V markers. MP4 and metrics are saved under `test-results/release-video/`.
|
|
276
|
+
|
|
277
|
+
GIF performance comparisons: run `npm run benchmark:gif` (or add `-- --quick` for a short run). See [benchmark methodology and reports](docs/gif-benchmark.md) for the resolution/duration matrix, timing boundaries and limitations.
|
|
278
|
+
|
|
279
|
+
Use `npm run benchmark:gif -- --compare-opacity` to compare the pre-optimization core with the current core on opaque, mixed-alpha and translucent-gradient scenes.
|
|
280
|
+
|
|
196
281
|
## Release
|
|
197
282
|
|
|
198
283
|
Run `npm test` and `npm run test:package` (packs and installs the tarball in an isolated consumer, then checks TypeScript declarations and runtime imports). The archive contains dist, README, LICENSE, CHANGELOG and package metadata. Source and issues: [urffin/canvas-take](https://github.com/urffin/canvas-take). The 0.x API may change in later minor releases.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# @mediabunny/aac-encoder
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@mediabunny/aac-encoder)
|
|
4
|
+
[](https://bundlephobia.com/package/@mediabunny/aac-encoder)
|
|
5
|
+
[](https://www.npmjs.com/package/@mediabunny/aac-encoder)
|
|
6
|
+
[](https://discord.gg/hmpkyYuS4U)
|
|
7
|
+
|
|
8
|
+
<div align="center">
|
|
9
|
+
<img src="../../docs/public/mediabunny-logo.svg" width="180" height="180">
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
Some browsers lack support for AAC encoding in their WebCodecs implementations. This extension package provides a reliable AAC-LC encoder for use with [Mediabunny](https://github.com/Vanilagy/mediabunny). It is implemented using Mediabunny's [custom coder API](https://mediabunny.dev/guide/supported-formats-and-codecs#custom-coders) and uses a fast, size-optimized WASM build of [FFmpeg](https://ffmpeg.org/)'s AAC encoder under the hood.
|
|
13
|
+
|
|
14
|
+
> This package, like the rest of Mediabunny, is enabled by its [sponsors](https://mediabunny.dev/#sponsors) and their donations. If you've derived value from this package, please consider [leaving a donation](https://github.com/sponsors/Vanilagy)! 💘
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
This library peer-depends on Mediabunny. Install both using npm:
|
|
19
|
+
```bash
|
|
20
|
+
npm install mediabunny @mediabunny/aac-encoder
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Alternatively, directly include them using a script tag:
|
|
24
|
+
```html
|
|
25
|
+
<script src="mediabunny.js"></script>
|
|
26
|
+
<script src="mediabunny-aac-encoder.js"></script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This will expose the global objects `Mediabunny` and `MediabunnyAacEncoder`. Use `mediabunny-aac-encoder.d.ts` to provide types for these globals. You can download the built distribution files from the [releases page](https://github.com/Vanilagy/mediabunny/releases).
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { registerAacEncoder } from '@mediabunny/aac-encoder';
|
|
35
|
+
|
|
36
|
+
registerAacEncoder();
|
|
37
|
+
```
|
|
38
|
+
That's it - Mediabunny now uses the registered AAC encoder automatically.
|
|
39
|
+
|
|
40
|
+
If you want to be more correct, check for native browser support first:
|
|
41
|
+
```ts
|
|
42
|
+
import { canEncodeAudio } from 'mediabunny';
|
|
43
|
+
import { registerAacEncoder } from '@mediabunny/aac-encoder';
|
|
44
|
+
|
|
45
|
+
if (!(await canEncodeAudio('aac'))) {
|
|
46
|
+
registerAacEncoder();
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Example
|
|
51
|
+
|
|
52
|
+
Here, we convert an input file to an MP4 with AAC audio:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import {
|
|
56
|
+
Input,
|
|
57
|
+
ALL_FORMATS,
|
|
58
|
+
BlobSource,
|
|
59
|
+
Output,
|
|
60
|
+
BufferTarget,
|
|
61
|
+
Mp4OutputFormat,
|
|
62
|
+
canEncodeAudio,
|
|
63
|
+
Conversion,
|
|
64
|
+
} from 'mediabunny';
|
|
65
|
+
import { registerAacEncoder } from '@mediabunny/aac-encoder';
|
|
66
|
+
|
|
67
|
+
if (!(await canEncodeAudio('aac'))) {
|
|
68
|
+
// Only register the custom encoder if there's no native support
|
|
69
|
+
registerAacEncoder();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const input = new Input({
|
|
73
|
+
source: new BlobSource(file), // From a file picker, for example
|
|
74
|
+
formats: ALL_FORMATS,
|
|
75
|
+
});
|
|
76
|
+
const output = new Output({
|
|
77
|
+
format: new Mp4OutputFormat(),
|
|
78
|
+
target: new BufferTarget(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const conversion = await Conversion.init({
|
|
82
|
+
input,
|
|
83
|
+
output,
|
|
84
|
+
audio: {
|
|
85
|
+
codec: 'aac',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
await conversion.execute();
|
|
89
|
+
|
|
90
|
+
output.target.buffer; // => ArrayBuffer containing the MP4 file
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For more ways of using Mediabunny, refer to its [guide](https://mediabunny.dev/guide/introduction).
|
|
94
|
+
|
|
95
|
+
## Building and development
|
|
96
|
+
|
|
97
|
+
For simplicity, all built WASM artifacts are included in the repo, since these rarely change. However, here are the instructions for building them from scratch:
|
|
98
|
+
|
|
99
|
+
[Install Emscripten](https://emscripten.org/docs/getting_started/downloads.html) and clone [FFmpeg](https://github.com/FFmpeg/FFmpeg). Then, from the Mediabunny root and with Emscripten sourced in:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
export FFMPEG_PATH=/path/to/ffmpeg
|
|
103
|
+
export MEDIABUNNY_ROOT=$PWD
|
|
104
|
+
|
|
105
|
+
# Build FFmpeg
|
|
106
|
+
cd $FFMPEG_PATH
|
|
107
|
+
emmake make distclean
|
|
108
|
+
emconfigure ./configure \
|
|
109
|
+
--target-os=none \
|
|
110
|
+
--arch=x86_32 \
|
|
111
|
+
--enable-cross-compile \
|
|
112
|
+
--disable-asm \
|
|
113
|
+
--disable-x86asm \
|
|
114
|
+
--disable-inline-asm \
|
|
115
|
+
--disable-programs \
|
|
116
|
+
--disable-doc \
|
|
117
|
+
--disable-debug \
|
|
118
|
+
--disable-all \
|
|
119
|
+
--disable-everything \
|
|
120
|
+
--disable-autodetect \
|
|
121
|
+
--disable-pthreads \
|
|
122
|
+
--disable-runtime-cpudetect \
|
|
123
|
+
--enable-avcodec \
|
|
124
|
+
--enable-encoder=aac \
|
|
125
|
+
--cc="emcc" \
|
|
126
|
+
--cxx=em++ \
|
|
127
|
+
--ar=emar \
|
|
128
|
+
--ranlib=emranlib \
|
|
129
|
+
--extra-cflags="-DNDEBUG -Oz -flto -msimd128" \
|
|
130
|
+
--extra-ldflags="-Oz -flto"
|
|
131
|
+
emmake make
|
|
132
|
+
|
|
133
|
+
# Compile the bridge between JavaScript and FFmpeg's API
|
|
134
|
+
cd $MEDIABUNNY_ROOT/packages/aac-encoder
|
|
135
|
+
emcc src/bridge.c \
|
|
136
|
+
$FFMPEG_PATH/libavcodec/libavcodec.a \
|
|
137
|
+
$FFMPEG_PATH/libavutil/libavutil.a \
|
|
138
|
+
-I$FFMPEG_PATH \
|
|
139
|
+
-s MODULARIZE=1 \
|
|
140
|
+
-s EXPORT_ES6=1 \
|
|
141
|
+
-s SINGLE_FILE=1 \
|
|
142
|
+
-s ALLOW_MEMORY_GROWTH=1 \
|
|
143
|
+
-s ENVIRONMENT=web,worker \
|
|
144
|
+
-s FILESYSTEM=0 \
|
|
145
|
+
-s MALLOC=emmalloc \
|
|
146
|
+
-s SUPPORT_LONGJMP=0 \
|
|
147
|
+
-s EXPORTED_RUNTIME_METHODS=cwrap,HEAPU8 \
|
|
148
|
+
-s EXPORTED_FUNCTIONS=_malloc,_free \
|
|
149
|
+
-msimd128 \
|
|
150
|
+
-flto \
|
|
151
|
+
-Oz \
|
|
152
|
+
-o build/aac.js
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This generates `build/aac.js`, which contains both the JavaScript "glue code" as well as the compiled WASM inlined.
|
|
156
|
+
|
|
157
|
+
### Building the package
|
|
158
|
+
|
|
159
|
+
Then, the complete JavaScript package can be built alongside the rest of Mediabunny by running `npm run build` in Mediabunny's root.
|
|
160
|
+
|
|
161
|
+
## Provenance
|
|
162
|
+
|
|
163
|
+
The build artifacts included in this repo, which are also the ones bundled into the published package, were built with exactly the commands above, from:
|
|
164
|
+
|
|
165
|
+
- FFmpeg [`60a2818f76dcd1fee3155b1791304af104a0fc1b`](https://github.com/FFmpeg/FFmpeg/commit/60a2818f76dcd1fee3155b1791304af104a0fc1b) (`n9.1-dev-2902-g60a2818f76d`), unmodified
|
|
166
|
+
- Emscripten 4.0.21 (`d6f42310010ddec0753a69bab01a4b33aae255b7`)
|
|
167
|
+
- `src/bridge.c` of this package
|
|
168
|
+
|
|
169
|
+
SHA-256 of the WASM binary inlined in `build/aac.js`: `4123d56ed650255a14535d8438680a78df67ae9324e4874a88becfb37bf18c66`
|
|
170
|
+
|
|
171
|
+
FFmpeg is licensed under the LGPL v2.1 or later; this build enables no GPL, version 3 or nonfree components. Its license files are in [`licenses/ffmpeg`](./licenses/ffmpeg). To use a modified FFmpeg, rebuild using the steps above.
|