Quick start

Decode frames, scale a frame, probe metadata, decode audio, and grab a thumbnail — each in a few lines with the high-level wasmpeg API.

Everything below uses the high-level default export. Call load() once up front; it’s safe to call again (subsequent calls resolve immediately).


await wasmpeg.load();

Every method accepts any input type — a File, Blob, an http(s) URL, a Uint8Array/ArrayBuffer, or a live HTMLVideoElement / HTMLCanvasElement / ImageData. The examples use file as a stand-in for whichever you have.

The method at a glance

MethodReturnsOwns a slot?
probe(input)metadata objectNo (closes internally)
decode(input, { format })decoder with nextFrame() / close()Yes — you close it
decodeAudio(input, { format })decoder with nextSamples() / close()Yes — you close it
scale(input, w, h, filter?)Uint8ClampedArray RGBA8No
encode(input, opts)Uint8Array container bytesNo
run(input, args)RGBA pixels or a decoderDepends on the command

Decode video frame by frame

const dec = await wasmpeg.decode(file);
console.log(dec.width, dec.height, dec.fps);

let frame;
while ((frame = dec.nextFrame())) {
    // Uint8ClampedArray, length = dec.width * dec.height * 4 (RGBA8)
}
dec.close();

// Decode straight to a target size (one GPU scale, no intermediate copy):
const small = dec.nextFrame(320, 180);

nextFrame() returns null at end of stream. Each frame is a fresh copy that survives the next call, so you can hold onto it. Pass a target width and height to resize during decode rather than after — see How it works. → Guide

Painting frames to a canvas

const ctx = canvas.getContext('2d');
const dec = await wasmpeg.decode(file);
canvas.width = dec.width;
canvas.height = dec.height;

let frame;
while ((frame = dec.nextFrame())) {
    ctx.putImageData(new ImageData(frame, dec.width, dec.height), 0, 0);
    await new Promise(requestAnimationFrame);
}
dec.close();

Forcing a demuxer

Most containers identify themselves. For formats that don’t (some legacy and game audio), pass format to name the demuxer explicitly:

const dec = await wasmpeg.decode(bytes, { format: 'g722' });

Scale / filter a single frame

// Returns a Uint8ClampedArray of RGBA8 pixels at the target size.
const rgba = await wasmpeg.scale(file, 1280, 720);

// Any FFmpeg filtergraph works (output size is taken from the scale= in the graph):
const flipped = await wasmpeg.scale(file, 1280, 720, 'scale=1280:720,hflip');

scale() grabs the first frame, runs the filtergraph, and returns the result — it opens and closes its own decoder, so there’s nothing for you to clean up. When you pass a custom filtergraph, the output dimensions come from the scale= in the graph, so keep it in sync with the width/height arguments. → Guide

Probe metadata (no decoding)

const info = await wasmpeg.probe(file);
// {
//   format: 'mov,mp4,m4a,3gp,3g2,mj2',
//   duration: 12.4,                       // seconds, or null
//   bitrate: 2400,                        // kb/s
//   streams: [{ index: 0, type: 'video' }, { index: 1, type: 'audio' }],
//   video: { width: 1920, height: 1080, fpsNum: 30000, fpsDen: 1001 },
//   audio: { sampleRate: 48000, channels: 2 },
// }

probe() reads container and stream metadata without decoding any frames, so it’s fast and cheap to call on upload. duration is null when the container doesn’t store it; fpsNum/ fpsDen give the exact frame rate as a fraction (here 30000/1001 ≈ 29.97). Stream type is one of video, audio, data, subtitle, or attachment. → Guide

Decode audio to PCM

const aud = await wasmpeg.decodeAudio(file);
console.log(aud.channels, aud.sampleRate);

let chunk;
while ((chunk = aud.nextSamples())) {
    // Float32Array, interleaved samples in [-1, 1]
}
aud.close();

nextSamples() returns one decoded frame’s worth of interleaved float samples, or null at EOF. Interleaved means channels alternate per sample (L, R, L, R, …); deinterleave if your sink wants planar data. Like decode(), the audio decoder holds a session slot until you close() it. → Guide

Grab a thumbnail / encode frames

// First-frame JPEG thumbnail (image2pipe is the default container):
const jpg = await wasmpeg.encode(file, { codec: 'mjpeg', frames: 1 });

// Encode a canvas straight to PNG:
const png = await wasmpeg.encode(canvas, { codec: 'png' });

encode() returns a Uint8Array of the encoded container bytes. The common options:

OptionDefaultMeaning
codecmjpegEncoder name — mjpeg, png, etc.
fmtimage2pipeContainer; the in-memory single-stream image container
width / heightsource sizeOutput dimensions
framesallCap on how many frames to encode
fps30Frame rate as a number or { num, den }
bitratecodec defaultTarget bitrate in bits/s

For a still, set frames: 1. → Guide

Always close()
Decoders and audio decoders hold one of 8 shared WASM session slots. Call close() when you’re done so the slot frees up — otherwise a long-lived page can exhaust them and the next decode() will throw. probe(), scale(), and encode() clean up their own slots; the two iterators you open yourself are the ones you have to close.

Drive it with FFmpeg arguments

If you’d rather pass an FFmpeg-style command, run() dispatches one through the same pipeline:

// Filter op → returns RGBA pixels:
const rgba = await wasmpeg.run(file, ['-i', 'in.mp4', '-vf', 'scale=640:360']);

// Decode-only → returns a decoder you iterate and close:
const dec = await wasmpeg.run(file, ['-i', 'in.mp4']);

run() parses the args and acts on the decode/filter subset. There’s no output-file path: a trailing output filename is parsed and ignored, and the result comes back in memory.

Next, read The three APIs to understand when to drop to a lower level.