rff-targets
"I have this file — what can I turn it into?"
Given the streams of an input, rff-targets enumerates every container the
engine can actually write for it, and says for each one exactly what would
happen to each stream: a byte-exact stream copy, a re-encode (and whether that
re-encode is lossy), or a drop the container forces.
$ rffprobe -show_targets clip.webm
Input #0, matroska, from 'clip.webm':
Stream #0:0: video: vp9
Convert to (15 targets):
video
.mkv copy vp9 copy
$ rff -i clip.webm -c:v copy clip.mkv
.webm copy vp9 copy
$ rff -i clip.webm -c:v copy clip.webm
.ivf copy vp9 copy
$ rff -i clip.webm -c:v copy clip.ivf
.y4m lossless vp9->rawvideo
$ rff -i clip.webm -c:v rawvideo clip.y4m
.mp4 lossy vp9->h264
! vp9 -> h264 re-encodes already-lossy data (generation loss)
$ rff -i clip.webm -c:v h264 clip.mp4
...
image
.png lossless vp9->png
! holds one picture — only the first frame is written
$ rff -i clip.webm -c:v png clip.png
...
Why this is a crate and not a lookup table
The naive version of this feature is a hard-coded map of "these extensions can become those extensions". That map is wrong the moment a codec is added, a feature flag turns one off, or a muxer learns a new codec — and it is wrong silently, offering conversions that fail.
This crate derives the answer from the engine's own registries instead:
MuxCapson each registeredFormatdeclares what that container's muxer accepts. It lives next to the muxer, in the same crate, andcrates/rff/tests/mux_caps.rsdrives every declared(format, codec)pair through the real muxer to prove the declaration is true.- The
CodecRegistrysays which codecs this build can encode and decode.
So a build compiled without the VP9 codec reports fewer targets, automatically
and correctly. And crates/rff/tests/targets_end_to_end.rs closes the loop:
it takes a real file, asks what it can become, and runs every answer through
the transcoder.
Using it
let engine = new;
let plan = targets?;
for t in &plan.targets
// The subset that needs no re-encoding at all:
for t in plan.stream_copies
Each Target carries a ready-to-run args list (["-c:v", "copy", "-c:a", "opus"]), so a caller does not have to trust that the engine's defaults match
what was reported — the plan pins its own codecs.
For a web app
Plan::to_json() renders the whole answer as JSON with no serde dependency:
Two more helpers need no input file at all:
format_matrix(&engine.formats)— the whole read/write matrix for this build, to populate a format picker up front.readable_extensions(&engine.formats)— the accept list for a file input.
What the fields mean
| Field | Meaning |
|---|---|
fidelity: Copy |
Every kept stream is muxed through untouched. Bytes preserved. |
fidelity: Lossless |
Something is re-encoded, but every hop is mathematically lossless (PCM→FLAC, →PNG, →WebP/VP8L). |
fidelity: Lossy |
At least one stream goes through a lossy encoder. |
notes |
Caveats worth showing a user: a dropped stream, a still-image truncation, a second lossy generation, a multi-file output. |
kind |
video / audio / image / subtitle — what a UI groups the target under. |
Targets are ordered: the kind matching the input first, then copy before
lossless before lossy, then a per-kind running order (a .flac input leads with
.mp3 and .wav, not with the .mkv that also happens to accept its audio).
A container where every stream would be dropped is not offered at all — listing a target that produces nothing would be a lie.