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, covering 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 and 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 where they 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.