Skip to main content

What’s in the Cookbook?

This cookbook provides practical, production-ready recipes for common video encoding tasks using node-webcodecs. Each recipe includes complete working code, explanations, and best practices.

Recipes

Quick Reference

Common Patterns

const encoder = new VideoEncoder({
  output: (chunk) => { /* save chunk */ },
  error: (err) => { throw err; }
});

encoder.configure({
  codec: 'avc1.42E01E',
  width: 1920,
  height: 1080,
  bitrate: 5_000_000
});

for (const frame of frames) {
  encoder.encode(frame);
  frame.close();
}

await encoder.flush();
encoder.close();
const decoder = new VideoDecoder({
  output: (frame) => {
    // Process decoded frame
    frame.close();
  },
  error: (err) => { throw err; }
});

decoder.configure({
  codec: 'avc1.42E01E',
  codedWidth: 1920,
  codedHeight: 1080,
  description: decoderConfig.description
});

for (const chunk of chunks) {
  decoder.decode(chunk);
}

await decoder.flush();
decoder.close();
// 1. Encode
const encodedChunks = [];
let decoderConfig = null;

const encoder = new VideoEncoder({
  output: (chunk, metadata) => {
    encodedChunks.push(chunk);
    if (metadata?.decoderConfig) {
      decoderConfig = metadata.decoderConfig;
    }
  },
  error: (err) => { throw err; }
});

encoder.configure({ /* config */ });
// ... encode frames ...
await encoder.flush();
encoder.close();

// 2. Decode
const decoder = new VideoDecoder({
  output: (frame) => {
    // Process frame
    frame.close();
  },
  error: (err) => { throw err; }
});

decoder.configure(decoderConfig);
for (const chunk of encodedChunks) {
  decoder.decode(chunk);
}
await decoder.flush();
decoder.close();

Common Operations

const fs = require('fs');
const { VideoDecoder, EncodedVideoChunk } = require('node-webcodecs');

// Read encoded video file
const videoData = fs.readFileSync('input.h264');

// Parse into chunks (simplified)
const chunk = new EncodedVideoChunk({
  type: 'key',
  timestamp: 0,
  data: videoData
});

decoder.decode(chunk);

Best Practices

Memory Management

  • Always call frame.close()
  • Close immediately after use
  • Monitor memory usage
  • Process in batches

Error Handling

  • Implement error callbacks
  • Validate inputs
  • Check codec support
  • Provide fallbacks

Performance

  • Use hardware acceleration
  • Batch process frames
  • Manage queue size
  • Profile bottlenecks

Quality

  • Choose appropriate bitrate
  • Match source resolution
  • Test output quality
  • Balance size vs quality

Code Style Guide

File Organization

// 1. Imports
const { VideoEncoder, VideoDecoder, VideoFrame } = require('node-webcodecs');
const fs = require('fs');

// 2. Configuration
const CONFIG = {
  codec: 'avc1.42E01E',
  width: 1920,
  height: 1080,
  bitrate: 5_000_000
};

// 3. Main function
async function main() {
  // Implementation
}

// 4. Helper functions
function createFrame(data) {
  // ...
}

// 5. Run
main().catch(console.error);

Error Handling

// ✅ Good: Comprehensive error handling
const encoder = new VideoEncoder({
  output: (chunk, metadata) => {
    try {
      processChunk(chunk, metadata);
    } catch (err) {
      console.error('Failed to process chunk:', err);
    }
  },
  error: (err) => {
    console.error('Encoder error:', err.message);
    // Clean up, notify, etc.
  }
});

try {
  encoder.configure(config);
  // ... encode ...
  await encoder.flush();
} catch (err) {
  console.error('Encoding failed:', err);
  throw err;
} finally {
  encoder.close();
}

Resource Cleanup

// ✅ Good: Always clean up
async function encodeVideo() {
  const frames = [];
  const encoder = new VideoEncoder({ /* ... */ });

  try {
    encoder.configure(config);

    for (const frame of frames) {
      encoder.encode(frame);
      frame.close();  // ← Close immediately
    }

    await encoder.flush();
  } finally {
    encoder.close();  // ← Always close encoder
  }
}

Testing Patterns

Unit Test Example

const { VideoEncoder, VideoFrame } = require('node-webcodecs');
const assert = require('assert');

async function testBasicEncoding() {
  let chunkCount = 0;

  const encoder = new VideoEncoder({
    output: (chunk) => {
      chunkCount++;
      assert(chunk.byteLength > 0, 'Chunk should have data');
    },
    error: (err) => {
      throw err;
    }
  });

  encoder.configure({
    codec: 'avc1.42E01E',
    width: 640,
    height: 480,
    bitrate: 1_000_000
  });

  // Encode one frame
  const data = new Uint8Array(640 * 480 * 4).fill(255);
  const frame = new VideoFrame(data, {
    format: 'RGBA',
    codedWidth: 640,
    codedHeight: 480,
    timestamp: 0
  });

  encoder.encode(frame);
  frame.close();

  await encoder.flush();
  encoder.close();

  assert(chunkCount > 0, 'Should produce at least one chunk');
  console.log('✓ Test passed');
}

testBasicEncoding().catch(console.error);

Next Steps