afmpeg

module
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: MIT

README

afmpeg

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 — the latest release is always current; CHANGELOG.md has the history. The runtime (New / Run / RunJob / Probe / Frames / Close), the Command builder, and certified module acquisition (WithModuleRelease) are all shipped and stable. It drives the companion ffmpeg-wasi engine over the structured job spec: transcode, remux/stream-copy, seeking & clips, multi-input filter_complex, subtitles & burn-in, metadata/chapters, frame extraction, analysis measurements, and live progress (WithProgress). A native backend (WithBackend / native.NewFromRelease) drives ffmpeg-wasi's native driver for 48–58× faster software encode plus HEVC/AV1 encode — still CGO-free. The design record lives in docs/development/specs/; the current build order is the implementation roadmap.

Why this exists

It was extracted from a need in keryx (the content-marketing tool): 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). keryx's spike (keryx/docs/development/spikes/ffmpeg-render-binding.md) evaluated the existing options and found none viable:

  • purego/dlopen bindings (e.g. ffgo) — immature, and still need host libav libs.
  • CGO libav bindings (e.g. go-astiav) — mature and in-memory-capable, but CGO breaks a clean static cross-compile.
  • wazero + embedded ffmpeg.wasm (e.g. go-ffmpreg) — the right posture (pure Go, no host deps, embeddable), but the stock builds lack the filters/codecs many workflows need (e.g. xfade, AAC) and aren't filesystem-virtualised.

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 — so a consumer (keryx, or anyone) can transcode / filter / mux entirely in memory, pure Go.

afmpeg now supplies that binding, and keryx renders reels through it — in-memory, pure Go, no local checkout required.

How it works

Three layers — the middle one is the novel engineering:

  1. The FFmpeg-WASM module — current FFmpeg compiled to wasm32-wasi (H.264 encode via openh264 on the LGPL default, or libx264 on the GPL variant), configured down to the codecs/filters real workflows need. Shipped as a separate downloadable artifact, never //go:embed-ed (see Licensing below).
  2. The afero ↔ wazero vfs bridge (the heart) — the guest ffmpeg's WASI filesystem syscalls are routed to a mounted experimental/sys.FS that afmpeg implements backed by an afero.Fs. The guest's reads and writes hit the caller's filesystem (e.g. an in-memory MemMapFs) with no host disk touched.
  3. The Go API — compile the module once into a reusable Runtime, then Run an ffmpeg invocation with its I/O bridged to a caller-supplied afero.Fs. A general, use-case-agnostic command builder layers on top (spec 0005).

A second native backend (spec 0028) swaps layer 1 for ffmpeg-wasi's native driver, run out-of-process with the same afero.Fs served over a seekable AVIO-over-IPC socket — same API, same no-host-disk guarantee, 48–58× faster software encode (and HEVC/AV1). Select it with WithBackend / native.NewFromRelease; WASM stays the default.

rt, _ := afmpeg.New(ctx, afmpeg.WithModuleRelease("n9.0.1-1", afmpeg.VariantLGPL)) // compile once, reuse
defer rt.Close(ctx)

fs := afero.NewMemMapFs()            // or the caller's in-memory worktree
// ... write inputs into fs ...
cmd := afmpeg.NewCommand(
    afmpeg.WithInput("in/clip.mp4"),
    afmpeg.WithFilterComplex("[0:v]scale=1280:-2[v]"),
    afmpeg.WithOutput("out/reel.mp4", afmpeg.Map("[v]"), afmpeg.VideoCodec("libx264")),
)
res, _ := rt.RunJob(ctx, fs, cmd)
out, _ := afero.ReadFile(fs, "out/reel.mp4")   // the result, in memory

Licensing

The Go package is permissively licensed. FFmpeg + x264 is GPL, so the full/GPL ffmpeg.wasm is distributed as a separate downloadable artifact rather than embedded — the copyleft obligation attaches only to a consumer who fetches and bundles it, not to the library. An LGPL/openh264 variant is tracked for fully-permissive consumers. x264 is the single GPL component in the target render set; AAC, xfade, the mp4 muxer, and the audio filters are all already LGPL-clean. See spec 0001 §10 (D-C).

Roadmap

The foundations (specs 0001–0007) and the full feature-parity roadmap (0013–0021, 0024, 0027) shipped; the strategic tier on top — signed releases (0010), the native backend (0028), HEVC/AV1 encode + AV1 decode (0023), analysis measurements, and live progress (0031/0032) — is shipped (job-spec vocab v9). The implementation roadmap tracks per-spec status and the current build order; the design records live in docs/development/specs/.

Spec Scope
0001 The thesis: design, requirements, the resolved decision record (§10)
0003 The afero.Fs → wazero sys.FS adapter (the core)
0004 New / Run / RunJob / Probe / Close — the public API
0007 The libav-direct engine + structured job spec (supersedes the CLI-string design)
0010 Signature-verified module acquisition (WithModuleRelease)
0028 The native subprocess backend — 48–58× faster software encode, HEVC/AV1, still CGO-free
0031 / 0032 Live job progress (WithProgress) — observed-fs (phase A) + engine side-channel (phase B)
0034 Which source Progress.Fraction derives from — engine time over input bytes, and -1 rather than a false "done"

What remains is a menu of trigger-gated work — a standalone CLI (0009), WASM threading (0030), native arm64/darwin (0022), HW-accel encoders, and measure-first perf/AV-sync (0026/0025) — none of it on a critical path. See the roadmap's pick-up menu.

  • Documentation: docs/ (Diátaxis — tutorials / how-to / reference / explanation)
  • Design + decision record: 0001-afmpeg
  • API overview: pkg/afmpeg/doc.go · published reference: pkg.go.dev
  • Local dev: just (build) · just test · just ci · just docs-serve

Directories

Path Synopsis
cmd
afmpeg-bench command
Command afmpeg-bench is the spec-0008 performance measurement rig: it runs a handful of representative media workloads through afmpeg's WASM runtime and against the host's native ffmpeg, and emits a markdown report with the per-workload timings and the native multiple (the honest wasm-vs-native ratio).
Command afmpeg-bench is the spec-0008 performance measurement rig: it runs a handful of representative media workloads through afmpeg's WASM runtime and against the host's native ffmpeg, and emits a markdown report with the per-workload timings and the native multiple (the honest wasm-vs-native ratio).
internal
vfs
Package vfs bridges an afero.Fs to wazero's experimental/sys.FS so a WebAssembly guest's WASI filesystem syscalls (path_open, fd_read, fd_write, fd_seek, …) read and write the caller's afero filesystem — including a fully in-memory MemMapFs — with no host disk access.
Package vfs bridges an afero.Fs to wazero's experimental/sys.FS so a WebAssembly guest's WASI filesystem syscalls (path_open, fd_read, fd_write, fd_seek, …) read and write the caller's afero filesystem — including a fully in-memory MemMapFs — with no host disk access.
pkg
afmpeg
Package afmpeg is a pure-Go FFmpeg binding whose filesystem I/O runs on an afero.Fs — including a fully in-memory filesystem — with no CGO, no host FFmpeg install, and no temp files.
Package afmpeg is a pure-Go FFmpeg binding whose filesystem I/O runs on an afero.Fs — including a fully in-memory filesystem — with no CGO, no host FFmpeg install, and no temp files.
afmpeg/native
Package native runs media jobs through a native ffmpeg-wasi driver subprocess (spec 0028 Backend B) instead of the sandboxed wasm module — the opt-in escape hatch for hardware acceleration and native-speed encode.
Package native runs media jobs through a native ffmpeg-wasi driver subprocess (spec 0028 Backend B) instead of the sandboxed wasm module — the opt-in escape hatch for hardware acceleration and native-speed encode.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL