Skip to main content

VideoDecoder

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.

Quick Example

import { VideoDecoder, EncodedVideoChunk } from 'node-webcodecs';

// Create decoder with output and error callbacks
const 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 decoding
decoder.configure({
  codec: 'avc1.42001f',  // H.264 Baseline Level 3.1
  codedWidth: 1920,
  codedHeight: 1080,
});

// Decode a keyframe
const chunk = new EncodedVideoChunk({
  type: 'key',
  timestamp: 0,
  data: h264KeyframeData
});

decoder.decode(chunk);

// Wait for all frames to be decoded
await 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.

Constructor

Creates a new VideoDecoder instance.
new VideoDecoder(init: VideoDecoderInit)
init
VideoDecoderInit
required
Initialization object containing callbacks for decoded frames and errors.
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);
  }
});

Properties

state
CodecState
required
Current state of the decoder. Possible values:
ValueDescription
'unconfigured'Initial state, or after calling reset(). Must call configure() before decoding.
'configured'Ready to decode chunks via decode().
'closed'Decoder has been closed and cannot be used.
decodeQueueSize
number
required
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.
// Wait if queue gets too large
while (decoder.decodeQueueSize > 10) {
  await new Promise(resolve => {
    decoder.addEventListener('dequeue', resolve, { once: true });
  });
}

Static Methods

isConfigSupported()

Checks if a decoder configuration is supported by the current system.
static isConfigSupported(config: VideoDecoderConfig): Promise<VideoDecoderSupport>
config
VideoDecoderConfig
required
The decoder configuration to test for support.
Returns: Promise<VideoDecoderSupport> - Object indicating whether the config is supported.
const support = await VideoDecoder.isConfigSupported({
  codec: 'avc1.42001f',
  codedWidth: 1920,
  codedHeight: 1080
});

if (support.supported) {
  console.log('H.264 decoding is supported');
  decoder.configure(support.config);
} else {
  console.log('H.264 decoding is NOT supported');
}
// Check for hardware-accelerated decoding
const hwSupport = await VideoDecoder.isConfigSupported({
  codec: 'avc1.42001f',
  codedWidth: 3840,
  codedHeight: 2160,
  hardwareAcceleration: 'prefer-hardware'
});

if (!hwSupport.supported) {
  // Fallback to software decoder
  const swSupport = await VideoDecoder.isConfigSupported({
    codec: 'avc1.42001f',
    codedWidth: 3840,
    codedHeight: 2160,
    hardwareAcceleration: 'prefer-software'
  });
}

Methods

configure()

Configures the decoder with the specified codec and parameters.
configure(config: VideoDecoderConfig): void
config
VideoDecoderConfig
required
Configuration object specifying codec and video parameters.
Throws:
  • InvalidStateError if the decoder is closed
  • NotSupportedError if the codec is not supported
decoder.configure({
  codec: 'avc1.42001f',
  codedWidth: 1920,
  codedHeight: 1080
});
// H.264 with SPS/PPS from container
decoder.configure({
  codec: 'avc1.64001f',
  codedWidth: 1920,
  codedHeight: 1080,
  description: spsAndPpsBuffer  // From MP4 avcC box or similar
});
decoder.configure({
  codec: 'hev1.2.4.L153.B0',  // HEVC Main 10
  codedWidth: 3840,
  codedHeight: 2160,
  colorSpace: {
    primaries: 'bt2020',
    transfer: 'pq',           // HDR10 PQ transfer
    matrix: 'bt2020-ncl',
    fullRange: false
  },
  hardwareAcceleration: 'prefer-hardware'
});

decode()

Queues an encoded video chunk for decoding.
decode(chunk: EncodedVideoChunk): void
chunk
EncodedVideoChunk
required
The encoded video chunk to decode. Can be a keyframe (type: 'key') or delta frame (type: 'delta').
Throws:
  • InvalidStateError if the decoder is not configured
  • DataError if the chunk data is malformed
// Decode a sequence of frames
for (const chunkData of encodedFrames) {
  const chunk = new EncodedVideoChunk({
    type: chunkData.isKeyframe ? 'key' : 'delta',
    timestamp: chunkData.timestamp,
    duration: chunkData.duration,
    data: chunkData.data
  });

  decoder.decode(chunk);

  // Implement backpressure
  if (decoder.decodeQueueSize > 10) {
    await decoder.flush();
  }
}

flush()

Waits for all pending decode operations to complete.
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
// Decode all chunks
for (const chunk of chunks) {
  decoder.decode(chunk);
}

// Wait for decoding to complete
await decoder.flush();
console.log('All frames decoded!');

// Safe to close now
decoder.close();

reset()

Resets the decoder to the unconfigured state, discarding all pending work.
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
// Abort current decoding and reconfigure
decoder.reset();

// Now reconfigure for a different video
decoder.configure({
  codec: 'vp09.00.10.08',
  codedWidth: 1280,
  codedHeight: 720
});

close()

Closes the decoder and releases all resources.
close(): void
After calling close(), the decoder cannot be used. Any pending decode operations are aborted.
// Finish any pending work
await decoder.flush();

// Release resources
decoder.close();

// decoder.decode() will now throw InvalidStateError

addEventListener()

Adds an event listener for decoder events.
addEventListener(
  type: string,
  listener: () => void,
  options?: { once?: boolean }
): void
type
string
required
Event type. Currently only 'dequeue' is supported.
listener
() => void
required
Callback function to invoke when the event fires.
options
object
Event listener options.
// Wait for queue to have space
async function waitForQueueSpace(decoder, maxQueueSize) {
  while (decoder.decodeQueueSize >= maxQueueSize) {
    await new Promise(resolve => {
      decoder.addEventListener('dequeue', resolve, { once: true });
    });
  }
}

// Use in decode loop
for (const chunk of chunks) {
  await waitForQueueSpace(decoder, 10);
  decoder.decode(chunk);
}

removeEventListener()

Removes a previously added event listener.
removeEventListener(type: string, listener: () => void): void
type
string
required
Event type (e.g., 'dequeue').
listener
() => void
required
The callback function to remove.
function onDequeue() {
  console.log('Queue size:', decoder.decodeQueueSize);
}

decoder.addEventListener('dequeue', onDequeue);

// Later, remove the listener
decoder.removeEventListener('dequeue', onDequeue);

Interfaces

VideoDecoderConfig

Configuration options for the video decoder.
interface VideoDecoderConfig {
  codec: string;                                    // Required
  codedWidth?: number;
  codedHeight?: number;
  displayAspectWidth?: number;
  displayAspectHeight?: number;
  colorSpace?: {
    primaries?: string;
    transfer?: string;
    matrix?: string;
    fullRange?: boolean;
  };
  hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
  optimizeForLatency?: boolean;
  description?: BufferSource;
  useWorkerThread?: boolean;                        // node-webcodecs extension
}

VideoDecoderInit

Initialization callbacks for the decoder constructor.
interface VideoDecoderInit {
  output: (frame: VideoFrame) => void;              // Required
  error: (error: DOMException) => void;             // Required
}

VideoDecoderSupport

Result of isConfigSupported().
interface VideoDecoderSupport {
  supported: boolean;                               // Whether config is supported
  config: VideoDecoderConfig;                       // The tested configuration
}

Hardware Decoding

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:
  • macOS: VideoToolbox (all Macs)
  • Windows/Linux: NVDEC (NVIDIA GPUs), VA-API (Intel/AMD)
  • Raspberry Pi: V4L2 M2M
// Request hardware acceleration
decoder.configure({
  codec: 'avc1.42001f',
  codedWidth: 3840,
  codedHeight: 2160,
  hardwareAcceleration: 'prefer-hardware'
});

// Check if hardware decoding is actually being used
const support = await VideoDecoder.isConfigSupported({
  codec: 'avc1.42001f',
  codedWidth: 3840,
  codedHeight: 2160,
  hardwareAcceleration: 'prefer-hardware'
});

if (support.supported) {
  console.log('Hardware decoding available');
} else {
  console.log('Falling back to software decoding');
}

See Also