Skip to content

afmpeg logo

afmpeg

Pure-Go FFmpeg on a virtual filesystem — no CGO, no host FFmpeg, no temp files.

A pure-Go FFmpeg binding that runs on a virtual / in-memory filesystem. No CGO, no host FFmpeg install, no temp files: FFmpeg is supplied as a separate WebAssembly module and executed via wazero (a zero-dependency, pure-Go WASM runtime), with its I/O bridged to an afero.Fs — so inputs and outputs can live entirely in memory (or any afero backend), and the whole thing cross-compiles to a single static binary.

Status: released

afmpeg runs real FFmpeg over a virtual filesystem today: the vfs bridge, the runtime (New/Run/RunJob/Probe/Close), the Command builder (JobSpec()/RunJob for the ffmpeg-wasi engine), and both signature-verified WithModuleRelease and bring-your-own WithModuleURL module acquisition. Pair it with a released ffmpeg-wasi module to transcode, remux, clip, filter, burn in subtitles, edit metadata, extract frames, and read analysis-filter measurements (ProcessResult.Analysis) — entirely in memory. For encode- or throughput-bound work there is also an opt-in native backend — the same engine as a signed native subprocess, for native-speed encode and the full profile's HEVC/AV1. See the latest afmpeg release; design rationale is in the specs under Development (start with 0001).

Why it exists

It was extracted from a need in keryx: keryx renders short reels by shelling out to the ffmpeg binary, which needs real files on disk — so it can't render an in-memory project (a remote cloned into RAM, no local checkout). Every existing Go option was rejected: purego bindings are immature and still need host libav; CGO bindings break a clean static cross-compile; and the existing wazero/WASM binding lacks the filters and codecs real workflows need. afmpeg is the "wazero + WASM done right" synthesis — a maintained FFmpeg-WASM build with the codecs/filters we need, a first-class afero virtual-filesystem I/O layer, and a clean Go API. keryx now renders its reels through afmpeg — in-memory, pure Go, with no local checkout.

How it works

Three layers — the middle one is the novel engineering:

  1. The FFmpeg-WASM module — current FFmpeg compiled to wasm32-wasi, configured to only the codecs/filters needed; shipped as a separate artifact, never embedded.
  2. The afero ↔ wazero vfs bridge (the heart) — routes the guest ffmpeg's WASI filesystem syscalls to a sys.FS backed by the caller's afero.Fs, so reads and writes hit an in-memory filesystem with no host disk touched.
  3. The Go API — compile the module once into a reusable Runtime, then Run an ffmpeg invocation over a supplied afero.Fs; a general command builder layers on top.

The runtime sits behind a backend seam (spec 0028): the WASM module is the default, and an opt-in native backend runs the same engine as a native subprocess for native-speed encode and HEVC/AV1 — same API, same afero I/O, no CGO.

See the architecture explainer for the full flow, and the roadmap for how the specs decompose the build.

Where to go next

The Go API reference lives on pkg.go.dev.