Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Vanilagy/mediabunny/llms.txt

Use this file to discover all available pages before exploring further.

Mediabunny ships with a powerful built-in file conversion abstraction that makes it easy to convert media files between formats, resize video, change codecs, trim clips, and more.

Features

The conversion API supports:
  • Transmuxing - Change the container format
  • Transcoding - Change track codecs
  • Track removal - Remove unwanted tracks
  • Compression - Reduce file size with lower bitrates
  • Trimming - Extract specific time ranges
  • Video resizing & fitting - Change dimensions and aspect ratio
  • Video rotation - Rotate video frames
  • Video cropping - Extract rectangular regions
  • Frame rate adjustment - Change video frame rate
  • Audio resampling - Change sample rate and channel count
  • Custom processing - Apply filters and effects

Basic usage

1

Create input and output

import {
  Input,
  Output,
  Conversion,
  ALL_FORMATS,
  BlobSource,
  WebMOutputFormat,
  BufferTarget,
} from 'mediabunny';

const input = new Input({
  source: new BlobSource(file),
  formats: ALL_FORMATS,
});

const output = new Output({
  format: new WebMOutputFormat(),
  target: new BufferTarget(),
});
2

Initialize the conversion

const conversion = await Conversion.init({ input, output });

if (!conversion.isValid) {
  // Check why tracks were discarded
  console.log(conversion.discardedTracks);
  return;
}
3

Execute the conversion

await conversion.execute();

// Get the final file
const convertedFile = output.target.buffer;
The Output passed to the conversion must be fresh - no tracks added, no metadata set, and in the 'pending' state.

Monitoring progress

Track conversion progress with the onProgress callback:
const conversion = await Conversion.init({ input, output });

conversion.onProgress = (progress) => {
  // progress is a number between 0 and 1
  console.log(`${(progress * 100).toFixed(1)}%`);
};

await conversion.execute();
A progress of 1 doesn’t mean the conversion is finished - the conversion is only complete when execute() resolves.

Video options

Configure video track conversion with the video option:

Resizing video

const conversion = await Conversion.init({
  input,
  output,
  video: {
    width: 1280,
    height: 720,
    fit: 'contain', // 'fill' | 'contain' | 'cover'
  },
});
Contains the entire image within the box while preserving aspect ratio. May result in letterboxing.
If only width or height is provided, the other dimension is calculated automatically to preserve aspect ratio.

Rotating video

video: {
  rotate: 90, // Degrees clockwise: 0 | 90 | 180 | 270
  allowRotationMetadata: false, // Bake rotation into frames
}
Rotation is applied on top of any rotation metadata in the input file and happens before cropping and resizing.

Cropping video

video: {
  crop: {
    left: 100,
    top: 50,
    width: 1280,
    height: 720,
  },
}
Cropping is applied after rotation but before resizing.

Transcoding video

import { QUALITY_HIGH } from 'mediabunny';

video: {
  codec: 'vp9',
  bitrate: 2e6, // 2 Mbps, or use QUALITY_HIGH
  keyFrameInterval: 5, // Key frame every 5 seconds
  frameRate: 30,
  alpha: 'keep', // 'discard' | 'keep'
}

Processing video frames

Apply custom processing to each frame:
let ctx: CanvasRenderingContext2D | null = null;

video: {
  process: (sample) => {
    if (!ctx) {
      const canvas = new OffscreenCanvas(
        sample.displayWidth,
        sample.displayHeight
      );
      ctx = canvas.getContext('2d')!;
      
      // Convert to grayscale
      ctx.filter = 'saturate(0)';
    }
    
    sample.draw(ctx, 0, 0);
    return ctx.canvas;
  },
}
The process function can return:
  • A VideoSample
  • A CanvasImageSource (canvas, image, video frame)
  • An array of either
  • null to drop the frame

Audio options

Configure audio track conversion with the audio option:

Resampling audio

const conversion = await Conversion.init({
  input,
  output,
  audio: {
    numberOfChannels: 2, // Stereo
    sampleRate: 48000, // 48 kHz
  },
});
Mediabunny performs automatic up/downmixing using the same algorithm as the Web Audio API.

Transcoding audio

import { QUALITY_MEDIUM } from 'mediabunny';

audio: {
  codec: 'opus',
  bitrate: 128e3, // 128 kbps, or use QUALITY_MEDIUM
}

Processing audio samples

audio: {
  process: (sample) => {
    // Apply audio effects, transformations, etc.
    return sample;
  },
}

Track-specific options

Apply different options to each track using functions:
const conversion = await Conversion.init({
  input,
  output,
  
  // Called for each video track
  video: (videoTrack) => {
    if (videoTrack.number > 1) {
      // Keep only the first video track
      return { discard: true };
    }
    
    return {
      // Shrink width to 640 only if wider
      width: Math.min(videoTrack.displayWidth, 640),
    };
  },
  
  // Called for each audio track
  audio: async (audioTrack) => {
    if (audioTrack.languageCode !== 'eng') {
      // Keep only English audio tracks
      return { discard: true };
    }
    
    return {
      codec: 'aac',
      bitrate: 128e3,
    };
  },
});

Trimming

Extract a specific time range from the input:
const conversion = await Conversion.init({
  input,
  output,
  trim: {
    start: 10,  // Start at 10 seconds
    end: 25,    // End at 25 seconds
  },
});
The output will be 15 seconds long and will begin at timestamp 0. You can also use negative values to add padding:
trim: {
  start: -2, // Two seconds of freeze frame/silence at start
}

Metadata tags

Control metadata tags in the output:
const conversion = await Conversion.init({
  input,
  output,
  tags: {
    title: 're:Turning',
    artist: 'Alexander Panos',
  },
});

Discarded tracks

If an input track is excluded from the output, it’s considered discarded:
const conversion = await Conversion.init({ input, output });

console.log(conversion.discardedTracks);
// => DiscardedTrack[]

console.log(conversion.isValid);
// => boolean
Possible discard reasons:
  • 'discarded_by_user' - You set discard: true
  • 'max_track_count_reached' - No room for more tracks
  • 'max_track_count_of_type_reached' - No room for this track type
  • 'unknown_source_codec' - Codec not recognized
  • 'undecodable_source_codec' - Can’t decode the source
  • 'no_encodable_target_codec' - Can’t find an encodable codec for output format

Canceling a conversion

await conversion.cancel();
This frees up resources and causes any ongoing execute() call to throw a ConversionCanceledError.

Examples

import { Conversion, QUALITY_VERY_LOW } from 'mediabunny';

const conversion = await Conversion.init({
  input,
  output,
  video: {
    width: 320,
    bitrate: QUALITY_VERY_LOW,
  },
  audio: {
    bitrate: 32e3,
  },
});

await conversion.execute();

Build docs developers (and LLMs) love