Inside the engine¶
The engine is deliberately small — roughly 2,900 lines of C under src/, plus vendored cJSON.
It has no public C API to call: you drive it as a binary over the invocation
contract, passing a JSON job spec.
This page is the maintainer's map — what each translation unit owns and how a job flows through
them — for anyone reading or changing the C. (Why it's built this way at all is Why
libav-direct; how it's compiled is The build.)
The translation units¶
| Unit | Lines | Owns |
|---|---|---|
driver.c |
~270 | Entry point & dispatch. main() reads the spec from argv[1], runs the vocabulary gate, and dispatches on "op" to one of the four handlers. Also holds op:probe, op:version, and the --report build smoke test. Defines AFMPEG_VOCAB_VERSION. |
process.c |
~1,500 | The transcode/filter/mux engine (op:process) — the heart. Opens inputs, builds the filter_complex graph (avfilter_graph_parse2), routes each graph pad by map to its output, encodes or stream-copies, muxes, and runs the decode→filter→encode→mux loop. Also the analysis-filter collection and the subtitle transcode lane. |
frames.c |
~500 | Still-frame extraction (op:frames) — the four selectors (timestamp/timestamps/interval/scene), seek-and-decode-forward, optional scale, and templated image output. |
meta.c |
~90 | Metadata & chapters — shared helpers for reading/writing container and per-stream tags, disposition flags, and chapters. Used by both probe (read) and process (write). |
nativeio.c |
~370 | The native AVIO-over-IPC bridge — native build only (-DAFMPEG_NATIVE). Installs a seekable AVIOContext that speaks the framed IPC protocol to the host, so the native driver reads and writes the caller's filesystem over a Unix socket instead of the host disk. |
progress.c |
~80 | The progress emitter (spec 0032) — a one-way, best-effort NDJSON writer to /dev/afmpeg-progress, throttled to ~100 ms of media time. Inert unless the job set "progress":true and the device opens. |
third_party/cJSON |
— | Vendored JSON parse/print — the spec in, the results out. |
Each *.c has a matching *.h declaring its small surface to driver.c.
How a job flows¶
main()(driver.c) parsesargv[1], applies the version gate, and dispatches on"op".- For
process, control entersprocess.c: inputs are opened (throughnativeio.c's AVIO on the native build, or WASI syscalls on the wasm build), the filtergraph is parsed and wired, and the decode→filter→encode→mux loop runs.meta.capplies any metadata/chapters;progress.cemits records as packets mux (when enabled). - Results are serialised with cJSON to stdout, one line; errors go to stderr with a non-zero exit code.
probe, frames, and version are the smaller paths off the same dispatch.
What the two builds share — and split¶
Both targets compile the same engine source; the difference is I/O and the toolchain, selected by
the TARGET build variable (The build):
- Shared — all of
driver.c,process.c,frames.c,meta.c,progress.c, and the whole job-spec vocabulary. The behaviour a consumer sees is identical. TARGET=wasm— cross-compiled towasm32-wasi; file I/O is WASI syscalls the host bridges to anafero.Fs;/dev/afmpeg-progressis a synthesised WASI device.TARGET=native— a host ELF with real threads + SIMD;nativeio.cis compiled in (-DAFMPEG_NATIVE) and file I/O rides the IPC bridge.progress.c's rawopen()of the progress device finds no host file, so engine-side progress is inert on native (host-observed byte progress covers it — afmpeg spec 0032/0033).
Related¶
- Driver invocation & ABI — the external contract these internals implement.
- The job-spec vocabulary — the fields
process.c/driver.cparse. - The build — how the units become a
.wasmor a native driver. - Why libav-direct — why there's a custom engine at all.