AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Ffmpeg Render Pro

mcp-beeswaxpat-ffmpeg-render-pro · by beeswaxpat

Parallel video rendering with live dashboard, GPU auto-detection, and stream-copy concat. MCP server with 7 typed tools for AI agents, a Claude Code skill, and a CLI.

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add mcp-beeswaxpat-ffmpeg-render-pro

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-beeswaxpat-ffmpeg-render-pro)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
24d ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ffmpeg Render Pro? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

  ╔══════════════════════════════════════════════════════╗
  ║                                                      ║
  ║   ████████ ████████ ██    ██ ██████  ████████  ████  ║
  ║   ██       ██       ███  ███ ██   ██ ██       ██     ║
  ║   ██████   ██████   ██ ██ ██ ██████  ██████   ██  ██ ║
  ║   ██       ██       ██    ██ ██      ██       ██  ██ ║
  ║   ██       ██       ██    ██ ██      ████████  ████  ║
  ║                                                      ║
  ║   ██████  ████████ ██    ██ ██████  ████████ ██████  ║
  ║   ██   ██ ██       ███   ██ ██   ██ ██       ██   ██ ║
  ║   ██████  ██████   ██ ██ ██ ██   ██ ██████   ██████  ║
  ║   ██   ██ ██       ██  ████ ██   ██ ██       ██   ██ ║
  ║   ██   ██ ████████ ██    ██ ██████  ████████ ██   ██ ║
  ║                                                      ║
  ║        ██████  ██████   ████                         ║
  ║        ██   ██ ██   ██ ██  ██                        ║
  ║        ██████  ██████  ██  ██                        ║
  ║        ██      ██   ██ ██  ██                        ║
  ║        ██      ██   ██  ████                         ║
  ║                                                      ║
  ║  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░ 8 WRKRS    ║
  ║  GPU: AUTO   DASHBOARD: LIVE   CONCAT: INSTANT       ║
  ╚══════════════════════════════════════════════════════╝

ffmpeg-render-pro

[](https://www.npmjs.com/package/ffmpeg-render-pro) [](LICENSE) [](https://github.com/beeswaxpat/ffmpeg-render-pro) [](https://nodejs.org/) [](https://modelcontextprotocol.io)

Parallel video rendering with live dashboard, GPU auto-detection, checkpoint system, and stream-copy concat. Includes an MCP server, a Claude Code skill, and a CLI.

Built by Beeswax Pat · Free and open source forever

Features

  • Parallel rendering: Split frames across N worker threads, concat with zero re-encoding
  • GPU auto-detection: Probes NVENC, VideoToolbox, AMF, VA-API, QSV with 1-frame validation
  • Live dashboard: Auto-opens in your browser with per-worker progress, FPS chart, ETA
  • Checkpoint system: 93% reduction in fast-forward overhead for long renders
  • Color grading: 5 built-in presets (noir, warm, cool, cinematic, vintage) plus custom filters
  • Audio merge: Combine video + audio with loudness normalization, no video re-encode
  • Deterministic output: Seeded RNG ensures parallel workers produce identical results to sequential
  • MCP server: Model Context Protocol server with 7 tools, works with Claude Code, Claude Desktop, and any MCP client
  • Cross-platform: Windows, macOS, Linux. Any GPU or CPU-only. Requires Node.js >= 18 plus ffmpeg.

What's New in v1.5.0

A reliability and agent-integration pass. Fully backward-compatible: every CLI command, API signature, MCP tool name, worker contract, and checkpoint file format from 1.4.x works unchanged.

  • Checkpoint resume is exact again. generateCheckpoints saved state one frame ahead of its label, so checkpoint-resumed workers rendered one frame out of sync with a sequential render. Regenerate checkpoint dirs created before 1.5.0 to pick up the fix.
  • VA-API actually works on Linux. The probe and codec args now include device init plus hwupload; previously every AMD/Intel GPU on Linux silently fell back to CPU.
  • VideoToolbox quality mapping fixed (it was inverted), and every encoder is now validated with its real production args, so Intel Macs fall back to CPU at detection time instead of failing at render time.
  • concatSegments validates segment compatibility (codec, resolution, fps, pixel format) with ffprobe before joining; mismatched inputs used to produce silently corrupt output.
  • The MCP server is agent-native: structured output and output schemas on every tool, progress notifications, clean cancellation, a new get_worker_template tool, and guaranteed protocol hygiene (stdout carries only JSON-RPC frames).
  • New renderParallel options: signal (AbortSignal) and quiet (byte-clean stdout). A failed worker's frame range is retried once automatically, and failures surface on the live dashboard.
  • New CLI flags --crf and --encoder-preset; FFMPEG_RENDER_PRO_FFMPEG / FFMPEG_RENDER_PRO_FFPROBE env vars support ffmpeg installs that are not on PATH.
  • Fractional fps x duration no longer drops a frame to float error (25fps x 4.6s renders 115 frames, not 114).
  • 81 tests grew to 241 across 12 suites, plus a GitHub Actions test matrix (Ubuntu/Windows/macOS x Node 18/20/22) with real ffmpeg installs.

See [CHANGELOG.md](CHANGELOG.md) for the complete list.

Requirements

  • Node.js >= 18
  • ffmpeg installed and on PATH (or pointed to via env var, below)

Using ffmpeg That Is Not on PATH

ffmpeg and ffprobe binaries are resolved at call time from two env vars (one exception: the best-effort post-render output check, noted below):

# Full path to the ffmpeg executable (used by every render, grade, merge, concat, probe)
FFMPEG_RENDER_PRO_FFMPEG=/opt/ffmpeg/bin/ffmpeg

# Optional: full path to ffprobe. When unset and FFMPEG_RENDER_PRO_FFMPEG is set,
# the sibling ffprobe next to that ffmpeg is used automatically if it exists.
FFMPEG_RENDER_PRO_FFPROBE=/opt/ffmpeg/bin/ffprobe

Because the vars are read at call time (never cached at module load), a long-lived process such as the MCP server picks up changes without a restart. Error messages name these variables when a binary cannot be found. A third variable, FFMPEG_RENDER_PRO_CACHE_DIR, overrides the GPU detection cache directory (default ~/.ffmpeg-render-pro).

Install

# Global install gives you the ffmpeg-render-pro + ffmpeg-render-pro-mcp binaries
npm install -g ffmpeg-render-pro

# Or clone the repo directly
git clone https://github.com/beeswaxpat/ffmpeg-render-pro.git
cd ffmpeg-render-pro

Quick Start

# System info (workers, RAM, CPU, ffmpeg version)
ffmpeg-render-pro info

# Probe hardware encoders
ffmpeg-render-pro detect-gpu

# 5s benchmark render (dashboard auto-opens at http://127.0.0.1:8080)
ffmpeg-render-pro benchmark

# Longer render, custom resolution
ffmpeg-render-pro benchmark --duration=30 --width=1080 --height=1920 --fps=30

# Force CPU / GPU encoding
ffmpeg-render-pro detect-gpu --cpu
ffmpeg-render-pro detect-gpu --gpu

CLI

ffmpeg-render-pro info                # System snapshot
ffmpeg-render-pro detect-gpu          # Probe hardware encoders
ffmpeg-render-pro render   # Render with your worker script
ffmpeg-render-pro benchmark           # Quick 5s test render
ffmpeg-render-pro version             # Print the installed version

Dashboard control flags for render and benchmark: --no-dashboard (disable entirely), --no-open (serve but don't open a browser), --port=8080, and --linger-ms=30000 (how long the dashboard stays up after completion; 0 exits immediately). Run ffmpeg-render-pro with no arguments for the full flag reference.

Quality flags for render and benchmark: --crf=NN (0-51, lower is higher quality) and --encoder-preset=NAME (any x264 preset name: ultrafast through placebo). Both are passed to workers via workerData.codecArgs; the bundled worker honors them, and custom workers can too.

Flag validation: an unknown flag prints a warning on stderr and execution continues (scripts stay forward compatible), but an unparseable value like --fps=abc or an out-of-range --crf=99 exits 1 with a clear error instead of silently rendering at the default.

Installed binaries: ffmpeg-render-pro (this CLI) and ffmpeg-render-pro-mcp (the MCP server). A legacy ffmpeg-render-mcp alias for the MCP server also exists and is kept permanently so older MCP configs never break.

API

const {
  renderParallel,       // Core: parallel rendering engine
  createEncoder,        // Pipe raw frames to ffmpeg
  detectGPU,            // Cross-platform GPU detection
  getConfig,            // Auto-tune workers, codec selection
  computeTotalFrames,   // Float-safe frame count for an fps/duration pair
  concatSegments,       // Stream-copy segment joining (validates by default)
  colorGrade,           // Apply color grades (presets or custom)
  mergeAudio,           // Combine video + audio
  startDashboard,       // Live progress dashboard
  ProgressTracker,      // Per-worker progress + dashboard JSON writer
  saveCheckpoint,       // Checkpoint serialization
  loadCheckpoint,       // Checkpoint restoration
  getEncoderIO,         // Encoder recipe split into inputArgs/filter/outputArgs
  getCodecArgs,         // Encoder recipe as one flat arg array
  getEncoderCandidates, // Platform's encoder candidates in priority order
  validateEncoder,      // 1-frame probe of one encoder with production args
  ffmpegBin,            // Resolved ffmpeg binary (env-var aware)
  ffprobeBin,           // Resolved ffprobe binary (env-var aware)
} = require('ffmpeg-render-pro');

renderParallel(options)

The main entry point. Splits a render across workers, shows a live dashboard, and produces a final MP4.

const controller = new AbortController();

await renderParallel({
  workerScript: './my-worker.js',  // Your frame generator
  outputPath: './output.mp4',
  width: 1920,
  height: 1080,
  fps: 60,
  duration: 60,         // seconds
  title: 'My Render',
  autoOpen: true,       // auto-open dashboard in browser
  maxWorkers: 8,        // cap for auto worker count (override with workerCount)
  dashboardLingerMs: 0, // 0 = resolve immediately; CLI default keeps it up 30s
  signal: controller.signal, // optional: abort stops workers + cleans temp files
  quiet: false,         // true keeps stdout byte-clean (status goes to stderr)
});

Width and height must be even (the pipeline encodes yuv420p). For library use, set dashboardLingerMs: 0 so the call resolves without holding the process open. renderParallel resolves with { outputPath, elapsed, totalFrames, avgFps }. Set FFMPEG_RENDER_PRO_DEBUG=1 in the environment for full stack traces from the CLI on error (library rejections carry the stack either way).

Reliability behavior baked into every render:

  • Abort: pass an AbortSignal as signal. Calling abort() stops all workers, removes temp files, and rejects the promise with an error whose name is 'AbortError'.
  • Quiet mode: quiet: true routes all status lines to stderr and disables the terminal progress ticker, so stdout stays byte-clean for protocol use (this is how the MCP server runs). Dashboard JSON files are still written.
  • One-shot retry: a failed worker's frame range is respawned once (to a fresh segment path) before the render is failed; a stderr warning names the worker and attempt.
  • Output verification: after concat, one cheap ffprobe metadata read warns on stderr if the output is shorter than requested. It never fails the render and is silent when ffprobe is missing. This best-effort check looks for ffprobe on PATH only; it is the one spawn that does not consult FFMPEG_RENDER_PRO_FFPROBE.
  • Failure surfaces live: on error the dashboard shows a red RENDER FAILED banner (and the browser tab title flips to FAILED) instead of freezing at the last good state.

The workerCount option is a request, not a guarantee: the renderer never spawns more workers than there are frames, so short renders may use fewer. Auto-detection caps at maxWorkers (default 8), free RAM, and CPU cores minus 2.

Encoder Helpers

getCodecArgs(encoder, { crf, cq, preset }) returns the full production arg array for any of the 11 supported encoders (libx264, NVENC, VideoToolbox, AMF, VA-API, QSV in H.264 and HEVC variants). For VA-API that array includes device init and a -vf format=nv12,hwupload pair, because the encoder cannot run without them.

getEncoderIO(encoder, opts) returns the same recipe split into { inputArgs, filter, outputArgs }. If you build your own -vf chain (color grading, scaling), merge getEncoderIO().filter into that chain instead of passing two -vf flags: ffmpeg only honors the last -vf per stream and silently drops the others. This also applies to colorGrade with a VA-API codec; keep the default libx264 for grading, or compose the invocation yourself via getEncoderIO.

computeTotalFrames(fps, duration) is the float-safe frame count the renderer itself uses (25fps x 4.6s correctly yields 115, not 114).

ProgressTracker accepts a terminalStream constructor option (default process.stdout; pass null to disable the terminal ticker while JSON keeps writing) and has a fail(message) method that writes a terminal error phase for the dashboard.

Writing a Worker

Workers receive frame ranges via workerData and pipe raw BGRA frames to ffmpeg:

const { workerData, parentPort } = require('worker_threads');
const { spawn } = require('child_process');

const { width, height, fps, startFrame, endFrame, segmentPath, workerId } = workerData;

async function main() {
  // Spawn ffmpeg encoder (env-var aware, falls back to PATH)
  const ffmpeg = spawn(process.env.FFMPEG_RENDER_PRO_FFMPEG || 'ffmpeg', [
    '-y', '-f', 'rawvideo', '-pixel_format', 'bgra',
    '-video_size', `${width}x${height}`, '-framerate', String(fps),
    '-i', 'pipe:0',
    '-c:v', 'libx264', '-preset', 'fast', '-crf', '20',
    '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
    segmentPath,
  ], { stdio: ['pipe', 'pipe', 'pipe'] });

  // Keep the last 8KB of ffmpeg stderr: it carries the error message on
  // failure, and an uncapped buffer grows unbounded on long renders.
  let stderrTail = '';
  ffmpeg.stderr.on('data', (c) => { stderrTail = (stderrTail + c).slice(-8192); });

  // Capture stdin errors (EPIPE when ffmpeg dies mid-pipe) and track the exit
  // from an EARLY close listener, so the final wait never attaches 'close'
  // after the event already fired (which would hang forever).
  let streamError = null;
  ffmpeg.stdin.on('error', (err) => { if (!streamError) streamError = err; });
  let closed = false;
  let closeCode = null;
  ffmpeg.on('close', (code) => { closed = true; closeCode = code; });

  const frameSize = width * height * 4; // BGRA
  const buffer = Buffer.alloc(frameSize);
  // Frames under the pipe's high-water mark can be queued BY REFERENCE even
  // when write() returns true, so small frames must be copied before writing.
  // Bigger frames keep zero-copy reuse: write() returns false and the drain
  // wait guarantees a full flush before the buffer is mutated again.
  const copyFrames = frameSize  ffmpeg.stdin.once('drain', r));

    // Report progress
    const done = f - startFrame + 1;
    parentPort.postMessage({
      type: 'progress', workerId,
      pct: (done / (endFrame - startFrame)) * 100,
      fps: 0, frame: done, eta: 0,
    });
  }

  // Close the encoder; resolve from captured state if it already exited.
  ffmpeg.stdin.end();
  await new Promise((resolve, reject) => {
    const finish = (code) => (code === 0
      ? resolve()
      : reject(new Error(`ffmpeg exited ${code}: ${stderrTail.slice(-500)}`)));
    if (closed) return finish(closeCode);
    ffmpeg.once('close', finish);
  });

  parentPort.postMessage({ type: 'done', workerId });
}

main().catch((err) => {
  parentPort.postMessage({ type: 'error', workerId, error: err.message });
});

See examples/basic-worker.js for the complete hardened reference worker (its drain wait also wakes on error/close, it never posts done after error, and it honors workerData.codecArgs). The MCP get_worker_template tool returns this contract plus the full reference source.

Worker data (injected via worker_threads workerData): width, height, fps, seed, startFrame, endFrame, segmentPath, workerId, totalFrames, duration, plus anything you pass in renderParallel({ workerData }).

Messages a worker posts to the parent via parentPort.postMessage(...):

|

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.