The VideoDecoder class decodes compressed video data (EncodedVideoChunk) into raw video frames (VideoFrame). It supports H.264, VP8, VP9, HEVC, and AV1 codecs with optional hardware acceleration.
import { VideoDecoder, EncodedVideoChunk } from 'node-webcodecs';// Create decoder with output and error callbacksconst decoder = new VideoDecoder({ output: (frame) => { console.log(`Decoded frame: ${frame.codedWidth}x${frame.codedHeight}`); console.log(`Timestamp: ${frame.timestamp}μs`); // Process the frame (display, save, re-encode, etc.) processFrame(frame); // IMPORTANT: Always close frames to prevent memory leaks frame.close(); }, error: (err) => { console.error('Decoding error:', err.message); }});// Configure for H.264 decodingdecoder.configure({ codec: 'avc1.42001f', // H.264 Baseline Level 3.1 codedWidth: 1920, codedHeight: 1080,});// Decode a keyframeconst chunk = new EncodedVideoChunk({ type: 'key', timestamp: 0, data: h264KeyframeData});decoder.decode(chunk);// Wait for all frames to be decodedawait decoder.flush();decoder.close();
Always call frame.close() in your output callback!VideoFrames hold native memory that is not visible to JavaScript’s garbage collector. Failing to close frames will cause memory leaks that can quickly exhaust system memory.
Callback invoked when a decoding error occurs. The decoder transitions to the closed state after an error.
Example: Creating a decoder
Copy
const decoder = new VideoDecoder({ output: (frame) => { // Process the decoded frame const data = new Uint8Array(frame.allocationSize()); await frame.copyTo(data); // MUST close to prevent memory leaks frame.close(); }, error: (err) => { console.error('Decoder error:', err.name, err.message); }});
Number of decode operations currently pending in the queue. Useful for implementing backpressure to avoid memory exhaustion when decoding faster than frames can be processed.
Copy
// Wait if queue gets too largewhile (decoder.decodeQueueSize > 10) { await new Promise(resolve => { decoder.addEventListener('dequeue', resolve, { once: true }); });}
Waits for all pending decode operations to complete.
Copy
flush(): Promise<void>
Returns:Promise<void> - Resolves when all queued chunks have been decoded and all output callbacks have been invoked.Throws:
InvalidStateError if the decoder is not configured
Example: Ensuring all frames are decoded
Copy
// Decode all chunksfor (const chunk of chunks) { decoder.decode(chunk);}// Wait for decoding to completeawait decoder.flush();console.log('All frames decoded!');// Safe to close nowdecoder.close();
Resets the decoder to the unconfigured state, discarding all pending work.
Copy
reset(): void
Aborts all pending decode operations and clears the decode queue. The decoder returns to the 'unconfigured' state and must be reconfigured before use.Throws:
InvalidStateError if the decoder is closed
Example: Resetting for new stream
Copy
// Abort current decoding and reconfiguredecoder.reset();// Now reconfigure for a different videodecoder.configure({ codec: 'vp09.00.10.08', codedWidth: 1280, codedHeight: 720});
If true, the listener is automatically removed after being invoked once.
Example: Backpressure using dequeue event
Copy
// Wait for queue to have spaceasync function waitForQueueSpace(decoder, maxQueueSize) { while (decoder.decodeQueueSize >= maxQueueSize) { await new Promise(resolve => { decoder.addEventListener('dequeue', resolve, { once: true }); }); }}// Use in decode loopfor (const chunk of chunks) { await waitForQueueSpace(decoder, 10); decoder.decode(chunk);}
Hardware-accelerated decoding uses GPU decoders for improved performance and reduced CPU usage. This is especially beneficial for 4K+ video or when processing multiple streams.
Hardware decoder availability depends on your system: