Use this file to discover all available pages before exploring further.
Mediabunny is designed for high performance from the ground up. This guide covers best practices and optimization techniques to get the most out of the library.
Mediabunny is highly modular. Only the code you import gets bundled:
Minimal bundle
Medium bundle
Full bundle
// Only MP4 reading - ~15 KB gzippedimport { Input, Mp4InputFormat, BlobSource } from 'mediabunny';const input = new Input({ source: new BlobSource(file), formats: [new Mp4InputFormat()],});
// MP4 + WebM reading - ~25 KB gzippedimport { Input, Mp4InputFormat, WebMInputFormat, BlobSource,} from 'mediabunny';const input = new Input({ source: new BlobSource(file), formats: [new Mp4InputFormat(), new WebMInputFormat()],});
// All formats - ~50 KB gzippedimport { Input, ALL_FORMATS, BlobSource } from 'mediabunny';const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS,});
Only import the formats you actually need to minimize bundle size. If you only work with MP4 files, there’s no reason to include WebM or Matroska support.
Close media sources as soon as you’re done adding data:
const videoSource = new CanvasSource(canvas, config);for (let i = 0; i < frameCount; i++) { await videoSource.add(i * frameDuration, frameDuration);}videoSource.close(); // Signals no more data, allows muxer to optimize
When using CanvasSink, enable canvas pooling to reuse canvases:
// Without pooling - allocates new canvas each timeconst sink = new CanvasSink(videoTrack);// With pooling - reuses canvases from poolconst sink = new CanvasSink(videoTrack, { poolSize: 3 });
For sequential iteration, poolSize: 1 is optimal:
const sink = new CanvasSink(videoTrack, { poolSize: 1 });for await (const { canvas } of sink.canvases()) { // Same canvas reused each iteration await processCanvas(canvas);}
When using AudioSampleSource, batch small samples when possible:
// Less efficient - many small samplesfor (const smallSample of smallSamples) { await audioSource.add(smallSample);}// More efficient - combine into larger samplesconst largeSample = combineAudioSamples(smallSamples);await audioSource.add(largeSample);
// ❌ Bad - imports everythingimport * as Mediabunny from 'mediabunny';// ✅ Good - imports only what's neededimport { Input, Output, Mp4InputFormat, Mp4OutputFormat, BlobSource, BufferTarget,} from 'mediabunny';
// Only need MP4import { Input, Mp4InputFormat } from 'mediabunny';const input = new Input({ source: new BlobSource(file), formats: [new Mp4InputFormat()], // Only MP4 code included});
// ❌ Wrongfor (const frame of frames) { source.add(frame);}// ✅ Correctfor (const frame of frames) { await source.add(frame);}
Not closing samples
Problem: Memory leaks from unclosed VideoFrames/AudioData
// ❌ Wrongfor await (const sample of sink.samples()) { processFrame(sample);}// ✅ Correctfor await (const sample of sink.samples()) { processFrame(sample); sample.close();}
Using BufferTarget for large files
Problem: Entire file kept in memory
// ❌ Wrong for large filesconst target = new BufferTarget();// ✅ Better for large filesconst target = new StreamTarget();
Importing ALL_FORMATS when not needed
Problem: Unnecessarily large bundle size
// ❌ Wrong - includes all format codeimport { ALL_FORMATS } from 'mediabunny';// ✅ Better - only includes what you useimport { Mp4InputFormat, WebMInputFormat } from 'mediabunny';const formats = [new Mp4InputFormat(), new WebMInputFormat()];
Multiple getSample() calls instead of samplesAtTimestamps()