Skip to content

Latest commit

 

History

History
648 lines (519 loc) · 36.4 KB

File metadata and controls

648 lines (519 loc) · 36.4 KB

rivet CLI reference

What happens under the hood for any of these commands — demux → decode-once pump → multi-GPU encode → mux — is in pipeline & architecture.

The rivet binary has these subcommands: transcode, probe, devices, capabilities, pipe, batch (feature batch), ipc (feature ipc), and serve (feature server). Build it with:

cargo build --release                     # GPU decode + GPU encode tiers
cargo build --release --features rav1e-fallback,rav1d-fallback,h26x-fallback  # + software AV1 / H.264 / H.265
cargo build --release --features nvidia   # + NVENC AV1 encoder (Windows or Linux)

The binary is at target/release/rivet. Run rivet --help or rivet <command> --help for generated usage at any time.

rivet encodes AV1 (default, royalty-clean), H.264, or H.265 — select with --codec av1|h264|h265. The output container is MP4 (single file) or CMAF/HLS (segmented); all three codecs work in both. See the compatibility matrix for codecs in.

One vocabulary, every surface. Every worded value below — --mode, --audio, --color, --bit-depth, --seam-mode, --encode, --decode, --gpu-family, --encode-policy, … — means what rivet::settings says it means, and nothing else. The CLI hands each word to the same TranscodeSettings the IPC socket reads as key=value, the HTTP API as query/JSON fields, and the batch manifest as YAML/JSON keys — --audio opus, audio=opus, ?audio=opus and audio: opus are one code path. The flag tables here therefore also document those keys.


rivet transcode

rivet transcode <INPUT> [OPTIONS]

Transcodes <INPUT> (any supported container/codec) to AV1 (default), H.264, or H.265 — pick with --codec.

Arguments

Argument Description
<INPUT> Input media file. Container/codec is auto-detected.

Options

Flag Values / default Description
-o, --output <PATH> default <input>.av1.mp4 Output file (single mode, one rung) or directory (multi-rung single mode, or HLS).
--mode <MODE> single (default), hls Output shape: one self-contained MP4 per rung, or a CMAF/HLS package.
--rung <WxH[@RATE]> repeatable A ladder rung, e.g. --rung 1920x1080 --rung 1280x720. Omit for a single rung at the source resolution. WxH@RATE (1280x720@3M) codes that rung to a bitrate — see --video-bitrate.
--ladder flag Auto-derive a standard ABR ladder from the source resolution (instead of --rung).
--max-short-side <N> default 1080 With --ladder, cap the tallest rung's short side.
--segment-seconds <S> default 4.0 HLS target segment length (segments still break on keyframes).
--crf <N> encoder-native Constant rate factor (lower = better quality). Names the quantiser directly; when set, --target is not consulted.
--video-bitrate <BPS> e.g. 3M, 800k Code every rung that does not name its own (--rung WxH@RATE, or bitrate= in --encode-policy) to this bitrate rather than to --target: the encoder's rate controller picks a quantiser per picture to spend it. The native software H.264 / H.265 encoder (h26x-fallback) codes to a rate. On a host whose encode pool is GPUs the job is refused before a frame is decoded, by name, saying how to reach the software pool. A CRF, --seam-mode constqp or --codec av1 beside a rate is refused too. Measured in codec-encode.md.
--video-buffer <DURATION> default 1s for a bitrate rung; e.g. 500ms; 0 for none The coded picture buffer every bitrate rung declares (the stream's HRD) and keeps to. It bounds any stretch of the stream at the rate plus the buffer, which is what bounds an HLS segment's peak and so its BANDWIDTH. The unit is required.
--target <T> visually_lossless, high, standard (default), low, vmaf=N Perceptual quality target for every rung. vmaf=N aims for a VMAF score — mapped to each backend's quantiser through the calibrated tables in codec::encode::tuning, so the same target means the same perceived quality on NVENC, QSV, AMF and rav1e. Measure it with bench/.
--gop <FRAMES> (--keyframe-interval) frames GOP length for every rung (default: two seconds at the output rate). Single file: the keyframe cadence and, across GPUs, the chunk grid. HLS: the segment grid stays --segment-seconds; a shorter GOP adds keyframes inside each segment (for seeking); a longer one is silently the segment, since every segment opens on an IDR anyway.
--audio <POLICY> auto (default), opus, drop auto: passthrough AAC/Opus/AC-3/E-AC-3, transcode MP3/Vorbis to Opus, drop the rest. opus: force Opus. drop: video only.
--audio-bitrate <BPS> e.g. 240k Opus target for transcoded audio. Omit to derive it from the channel layout — 64k mono, 96k stereo, 320k for 5.1. Ignored for passthrough tracks, which keep the bitrate they were authored at.
--audio-filter <CHAIN> e.g. channelmap=FL-FL|FR-FR:stereo Audio filter chain applied to decoded PCM before the encoder — see audio filters. Forces a decode/re-encode, so it can't be combined with a passthrough-only source codec.
--subtitles <SELECTION> all (default), none, eng,deu Which of the source's text subtitle tracks to carry: every one, none, or a language list. Single file: a tx3g track per language. HLS: a WebVTT rendition per language. Bitmap subtitles (PGS / VobSub / DVB) are always dropped. See Subtitles.
--max-fps <F> Cap the output frame rate (source cadence otherwise preserved).
--color <POLICY> sdr (default), hdr10, hlg, passthrough Output color / tonemap policy — see Color & bit depth.
--pixel-format <FMT> auto (default), 8bit, 10bit Output luma bit depth.
--filter <CHAIN> e.g. crop=1280:720,hflip Video filter chain applied before scaling — see Video filters.
--trim-start <S> seconds Splice/trim: keep from this time. The output is re-based to zero. Trimmed jobs take the serial encode path.
--trim-end <S> seconds Splice/trim: keep until this time. The kept range is [start, end), exact at any frame rate. To join clips, use rivet splice.
--codec <CODEC> av1 (default), h264, h265 Output video codec. av1 is royalty-clean (the project default); h264/h265 are for legacy-player compatibility (patent-licensing caveats). All three work for single-file MP4 and CMAF/HLS. H.264/H.265 are encoded on NVENC (validated on RTX 3090) + QSV (validated on Intel Arc); AMF H.264/H.265 is a follow-up.

GPU selection

Flag Description
--encode <PLAN> The encode plan — which cards, and how the work is laid across them, as one value so the halves cannot contradict: all (default) — every capable card, each worker serving every rung and taking the next chunk of whichever is furthest behind (a card idles only when the job is out of work); per-rung — every card, each pinned to its own rungs (one rung, one GPU when the ladder fits the pool; predictable placement, idle cards when a rung is blocked); single — one card, one encoder per rung, serial (single-file output is seam-free by construction); gpu:N — single, pinned to card N; family:nvidia|amd|intel — one vendor's cards, ladder-scheduled.
--gpu <N> / --single-gpu / --gpu-family <VENDOR> Older spellings of --encode gpu:N / single / family:VENDOR. Still work; --encode wins when both are given.
--decode <PLAN> The decode plan — which card(s), and whether the decode is one pump or split into ranges, as one value: auto (default) — cut an un-spliced H.264/H.265 source into one range per capable card at keyframes on chunk boundaries, one decode pump per card, each decoding its own stretch (whole where the source cannot be split); whole — one decoder for the whole source (the control arm of any comparison); fastest — benchmark every decode-capable card on a prefix of the input and put one decoder on the quickest; gpu:N — one decoder pinned to card N (e.g. an iGPU while the dGPUs encode); ranges:N — a range count (more than the cards is legal and is how the split is exercised on a one-card host). Output is byte-identical whichever you pick. --decode-gpu N still works and means gpu:N.
--encode-policy <recommended|off|SPEC> Per-rung encoder knobs by ladder position. recommended is the measured ladder policy (+2 libaom-CQ steps softer per rung going down, no top bonus, one tile below 4K, three reference frames — about −20% storage on a five-rung ladder for a fraction of a VMAF point); off is none (the default); or the rule grammar, e.g. qstep=2;top:q=-2;short<=2159:tiles=1x1;any:refs=3 — see output-spec.md.
--seam-mode <parallel|constqp> Seam quality on the multi-GPU single-file path — how the chunks it stitches are rate-controlled. Nothing else: no seams at all is an encode plan (--encode single), not a seam mode. serial still parses as the older spelling of --encode single.

See GPU scheduling for how AllGpus / SingleGpu / Family actually distribute work.

A pin nothing can serve is refused, by name

family:VENDOR and gpu:N name silicon. When nothing they name can encode the job's codec in this build — the family is absent, the card index does not exist, or the card is there but the build cannot drive it for that codec — the job is refused before a frame is decoded, on every path (single-file serial, chunked, HLS), and the error says what is present and what would work:

error: transcoding in.mp4: no encoder matches `--encode family:intel` for H.264 on this host: no Intel GPU is present. Present: NVIDIA GeForce RTX 3090 (gpu 0, NVIDIA, encodes H.264); AMD Radeon(TM) Graphics (gpu 1, AMD, cannot encode H.264 in this build). Fix: pin a card that can (`--encode family:nvidia` or `--encode gpu:0`) or drop the pin (`--encode all`, the default) to use them; to run on the software H.264 encoder (`h26x-fallback`) instead, drop the pin and hide the cards (`CUDA_VISIBLE_DEVICES=-1` hides NVIDIA), or build without the vendor features — the software pool takes the job only when no card can encode H.264 and none is pinned.

A pin never falls through: not to another vendor, and not to the software encoders — on the serial path either, where a pinned card that fails to start fails the job naming the vendor rather than sliding down the NVIDIA → AMD → Intel → software chain. The software pool (the ladder on CPU leases, one software encoder per slot) is what an unpinned plan (all, per-rung, single) gets when no card can encode the codec in this build and a software encoder is compiled in (h26x-fallback / rav1e-fallback): hide the cards (CUDA_VISIBLE_DEVICES=-1 for NVIDIA) or build without nvidia / amd / qsv. TRANSCODE_ENCODER_BACKEND=h26x|rav1e|nvenc|amf|qsv still pins a backend by name on the serial path.

Chunk seams (--seam-mode)

When more than one GPU encodes a single file, each rung is chunked at GOP boundaries, encoded in parallel, and the AV1 packets are stitched into one MP4. Each chunk is an independent IDR-led GOP, so the result always plays — but each chunk's rate control is independent, so quality can step at the ~2 s seams. AMD (AMF) and Intel (QSV) chunks are constant-QP and already seam-flat; this knob chiefly governs NVENC (which otherwise runs VBR per chunk):

Mode Seams Speed Notes
parallel (default) possible mild NVENC steps fastest (all GPUs) each chunk uses its encoder's normal rate control
constqp flat fast (all GPUs) forces constant-QP; the QP is derived from the quality target, so quality still tracks it

Wanting no seams at all is not a seam mode — it is one encoder per rung, which is --encode single (or gpu:N); --seam-mode serial still parses as that, for scripts that predate the split of the two questions.

(Single-GPU hosts, --single-gpu/--gpu, and HLS jobs are unaffected — HLS segments are independent files by design.)

Color & bit depth

The decode pump tonemaps only when the policy says so — it never decides on its own:

--color Output Bit depth Needs
sdr (default) tonemap HDR → SDR BT.709 8-bit any encoder
passthrough source color verbatim source 10-bit encoder if source is 10-bit
hdr10 BT.2020 + PQ 10-bit a 10-bit encoder (below)
hlg BT.2020 + HLG 10-bit a 10-bit encoder (below)

10-bit / HDR output needs a 10-bit encoder for the output codec in this build, and rivet transcode checks that before anything is decoded:

--codec 10-bit / HDR with 8-bit only on
av1 (Main) nvidia (NVENC), amd (AMF), qsv (oneVPL P010), on a GPU with AV1 encode the software tier, rav1e (rav1e-fallback)
h265 (Main 10) nvidia, amd, qsv, or the software tier h26x-fallback
h264 (High 10) the software tier h26x-fallback only NVENC, AMF, QSV (no hardware Hi10P encoder)

It's 4:2:0 10-bit, HDR-tagged in the container (colr/mdcv/clli) and, for H.264 / H.265, in the SPS VUI. A request the build can't produce for its codec is refused by name, before the input is decoded — here on a --features nvidia build:

$ rivet transcode in.mp4 -o out.mp4 --codec h264 --color hdr10
error: building output spec: invalid output spec: h264 at 10 bits (color=Hdr10, bit_depth=Auto) cannot be encoded: this build encodes h264 with nvenc (8-bit SDR). h264 at 10 bits needs the software tier (build with `h26x-fallback`); no hardware backend encodes h264 at 10 bits

What the source makes of the output is checked too, once the input is probed and still before a frame is decoded. --pixel-format auto keeps a 10-bit source at 10 bits, and --color passthrough keeps an HDR source's transfer, so a request that names neither can still need a 10-bit or HDR encoder. Until 2026-09-14 such a job passed validation, started decoding and failed building the encoder ("all 1 rung(s) failed"). Here on a --features nvidia build, with a 10-bit SDR HEVC source:

$ rivet transcode clip10_hevc.mp4 -o out.mp4 --codec h264
error: transcoding clip10_hevc.mp4: invalid OutputSpec: h264 at 10 bits (color=TonemapToSdr, bit_depth=Auto) cannot be encoded: this build encodes h264 with nvenc (8-bit SDR). h264 at 10 bits needs the software tier (build with `h26x-fallback`); no hardware backend encodes h264 at 10 bits; the source is Yuv420p10le and bit_depth=Auto keeps its 10 bits: `--pixel-format 8bit` encodes it at 8 bits

A splice is checked against its first clip, which the output follows; an HDR source under --color passthrough is told --color sdr tonemaps it.

On a build with both a card and the software tier (--features nvidia,h26x-fallback), a 10-bit H.264 output passes that check — h26x encodes it — and every path then gets an encoder that can take it:

  • The serial single-file encoder (one card, and every splice) is built by the dispatcher, which falls back from NVENC, with no 10-bit H.264, to h26x.
  • The chunk-and-stitch engine and the HLS ladder lease an encoder per chunk or segment, with no fallback, so their pool is built for the output's depth: a card whose encoder takes the codec only at 8 bits is left out, and software slots take its place. Until 2026-09-14 they leased the card and failed after decoding had started (ladder worker 0 failed: creating encoder for segment: … NVENC on GPU 0 does not support 10-bit H264 encode).
  • A policy that pins the card (--encode family:nvidia, --encode gpu:N) gets neither fallback nor software, so it is refused before decoding, naming the format: no encoder matches --encode family:nvidia for 10-bit H.264 on this host … drop the pin.

A backend pinned by name counts as well: TRANSCODE_ENCODER_BACKEND=h26x builds the software encoder with or without h26x-fallback (the feature only gates the automatic fallback), so it makes --codec h264|h265 at 10 bits valid on any build; the pin applies to the serial single-file path. The check is against the build, not the card: a --features nvidia binary accepts --codec av1 --color hdr10, and a GPU without AV1 encode (an RTX 30-series, say) then refuses it when the encoder is built. rivet capabilities prints the per-codec answer.

12-bit sources (HEVC Main 12 / RExt 4:2:2 / 4:4:4 12-bit from the native decoder) are accepted: they are narrowed to 10-bit with rounding (or to 8-bit for an 8-bit output), and 4:2:2 / 4:4:4 chroma is downsampled to 4:2:0. No encoder in the tree takes more than 10 bits, so --pixel-format auto gives a 10-bit output for a 12-bit source.

--chroma-downsample box|lanczos picks the 4:4:4 → 4:2:0 chroma filter for 4:4:4 sources (ProRes 4444, HEVC RExt 4:4:4): box (default) is the 2×2 average and keeps outputs byte-identical to earlier releases; lanczos is a separable Lanczos-2 sited where 4:2:0 decoders expect the chroma (co-sited horizontally, midway vertically), measurably closer to the source after a round trip (numbers in codec-encode.md). The same word is the chroma-downsample settings key on the IPC socket, the HTTP API, and the batch manifest (chroma_downsample:). No effect on 4:2:0 / 4:2:2 sources.

Audio

Two knobs beyond the --audio policy, both affecting transcoded audio only (a passthrough track is copied verbatim by definition):

  • --audio-bitrate sets the Opus target, ffmpeg-style (240k, 1.5M, or a plain bits-per-second count). Omitted, the encoder derives it from the channel layout: 64 kbps per uncoupled stream + 96 kbps per coupled pair, so 64k mono, 96k stereo, 320k for 5.1.
  • --audio-filter runs a chain over the decoded PCM before the encoder — today channelmap, for remapping / reordering / selecting channels. Full reference: audio filters.

Multichannel is carried end to end: 3–8 channels ride Opus's channel-mapping family 1 (RFC 7845 §5.1.1.2). The limit is on the decode side — rivet decodes MP3, Vorbis, AC-3 and E-AC-3, so a 5.1 Vorbis / AC-3 / E-AC-3 source can be re-encoded to Opus 5.1 (--audio opus) while a 5.1 AAC source can only be passed through.

Asking for --audio drop together with either knob is rejected rather than silently ignored.

Subtitles

--subtitles all (the default) carries every text subtitle track the source has — the -c:s copy equivalent. --subtitles none drops them, and a language list such as --subtitles eng,deu keeps only those tracks, in that order. Codes match by language, not spelling (en finds a track tagged eng, ger finds deu), and a listed language the source lacks is logged, not an error. rivet probe lists the tracks a file has.

Source Carried
Matroska S_TEXT/UTF8 (SRT)
Matroska S_TEXT/ASS / S_TEXT/SSA ✅ (markup stripped)
Matroska S_TEXT/WEBVTT ✅ (tags stripped)
MP4 tx3g (mov_text)
MP4 wvtt (WebVTT in ISOBMFF) ✅ (tags stripped)
PGS / VobSub / DVB (bitmap) ❌ dropped with a warning — no text form exists

Where they go depends on the output:

  • single — one tx3g track per language (3GPP timed text, what ffmpeg calls mov_text, the only subtitle format MP4 natively holds), each with its own mdhd language code for the player's track picker.
  • hls — one segmented-WebVTT rendition per language under subs/<lang>/ (seg-NNNNN.vtt + subtitles.m3u8), listed in the master playlist as an EXT-X-MEDIA:TYPE=SUBTITLES group that every variant names with SUBTITLES="subs". The subtitle segments sit on the video's segment grid — same boundaries, same EXTINF durations — and a cue that spans a boundary is repeated on both sides, per RFC 8216 §3.5. The first rendition is DEFAULT=YES.

Styling is not preserved: tx3g keeps style in side boxes keyed by byte range rather than inline, so ASS override blocks ({\an8}), SRT/WebVTT tags (<i>, <font>), and the ASS field prefix are stripped down to the text. Cue timing is preserved; in tx3g, cue gaps become empty samples so the timeline stays aligned.

Trims and splices carry them too: --trim-start/--trim-end clip the cues to the kept window and re-base them to zero, and rivet splice moves each clip's cues onto the joined timeline and merges tracks by language.

Output layout

  • single — one MP4 per rung. One rung → the -o file (faststart AV1 + audio). Multiple rungs → -o must be a directory; files are named per rung.
  • hls-o is the asset root: master.m3u8, an audio/ rendition group, and video/<height>p/{init.mp4, seg-*.m4s, playlist.m3u8} per rung, segment-aligned across the ladder for clean ABR.

Examples

# Single MP4 at the source resolution
rivet transcode input.mkv -o output.mp4

# Explicit 3-rung ladder → a directory of MP4s
rivet transcode input.mkv -o out_dir/ --rung 1920x1080 --rung 1280x720 --rung 640x360

# Auto ABR ladder capped at 1080p short side
rivet transcode input.mkv -o out_dir/ --ladder --max-short-side 1080

# CMAF/HLS package, 4 s segments
rivet transcode input.mkv -o hls_dir/ --mode hls --ladder --segment-seconds 4

# Quality + audio + frame-rate knobs
rivet transcode input.mkv -o out.mp4 --crf 28 --audio opus --max-fps 30

# Re-encode 5.1 audio to Opus at 240 kbps, re-tagging side surrounds as back
rivet transcode input.mkv -o out.mp4 --audio opus --audio-bitrate 240k \
  --audio-filter 'channelmap=FL-FL|FR-FR|FC-FC|LFE-LFE|SL-BL|SR-BR:5.1'

# Non-local-means denoise with explicit patch / research-window sizes
rivet transcode input.mkv -o out.mp4 --filter 'nlmeans=s=1:p=7:pc=5:r=3:rc=3'

# Temporal denoise (ffmpeg's hqdn3d parameters: luma/chroma spatial, luma/chroma temporal)
rivet transcode input.mkv -o out.mp4 --filter 'hqdn3d=4:3:6:4.5'

# Keep every text subtitle track (the default), only some languages, or none
rivet transcode input.mkv -o out.mp4 --subtitles eng,deu
rivet transcode input.mkv -o out.mp4 --subtitles none

# Pin to one GPU / one vendor / decode elsewhere
rivet transcode input.mkv -o out.mp4 --gpu 1
rivet transcode input.mkv -o out.mp4 --encode family:nvidia --decode gpu:0

# Benchmark decoders up front and decode on the fastest GPU (multi-GPU hosts)
rivet transcode input.mkv -o out.mp4 --decode fastest

# HDR10 passthrough (needs a nvidia/amd hardware or ffmpeg build)
rivet transcode input.mkv -o out.mp4 --color hdr10 --pixel-format 10bit

# Splice/trim: cut a single input to [2s, 7s)
rivet transcode input.mkv -o cut.mp4 --trim-start 2 --trim-end 7

rivet splice

Concatenate (and per-clip trim) several inputs into one output. Clips are joined in order; each is decoded with its own decoder, trimmed to its window, and the kept frames are re-encoded into one continuous, zero-based timeline (the muxer numbers frames by count, so the join is gap-free with no PTS rewriting). Because everything is re-encoded to a uniform output, the inputs may differ in codec, resolution, or color — output config follows the first clip. Audio is trimmed per clip and concatenated to match. Outputs a single MP4 (--mode single, the default) or a CMAF/HLS package (--mode hls) — for HLS the spliced frame stream feeds the same multi-GPU engine as a normal ladder, so segments stay keyframe-aligned across the join.

rivet splice -o <OUTPUT> [OPTIONS] <CLIP>...

<OUTPUT> is a file for --mode single, or a directory for --mode hls. Each <CLIP> is a path, or PATH@START-END to trim it (seconds, either side optional). @ is the separator so a Windows drive C:\… is unambiguous:

Clip spec Meaning
a.mp4 the whole clip
a.mp4@2-7 seconds [2, 7)
a.mp4@2- from 2 s to the end
a.mp4@-7 from the start to 7 s
Flag Values / default Description
-o, --output <PATH> required Output MP4 file (single) or directory (hls).
--mode <MODE> single (default), hls Output shape: one MP4, or a CMAF/HLS package.
--segment-seconds <S> default 4.0 HLS target segment length (--mode hls only).
--codec <CODEC> av1 (default), h264, h265 Output video codec (as for transcode).
--crf <N> encoder-native Constant rate factor.
--target <TARGET> standard (default), visually_lossless, high, low, vmaf=N Perceptual quality target, as for transcode.
--gop <N> two seconds GOP length in frames (alias --keyframe-interval).
--color <POLICY> sdr (default), hdr10, hlg, passthrough Output colour, as for transcode. The output follows the first clip; later clips are mapped into it.
--pixel-format <DEPTH> auto (default), 8bit, 10bit Output bit depth, as for transcode. 8bit is how a 10-bit first clip is joined into 8-bit H.264 on a build whose H.264 encoder is 8-bit — the remedy the depth refusal names.
--chroma-downsample <FILTER> box (default), lanczos 4:4:4 → 4:2:0 chroma filter for 4:4:4 clips.
--filter <CHAIN> none Video filter chain applied to every clip before scaling, as for transcode.
--video-bitrate <BPS> / --video-buffer <DURATION> e.g. 3M / 500ms Code the output to a rate, with its coded picture buffer (1 s unless given), as for transcode.
--audio <POLICY> auto (default), opus, drop Audio handling.
--audio-bitrate <BPS> derived Opus bitrate for transcoded audio (ignored for passthrough).
--audio-filter <CHAIN> none Audio filter chain before the Opus encoder, as for transcode.
--subtitles <SELECTION> all (default), none, eng,deu Subtitle tracks to carry, as for transcode. Each clip's cues are clipped to its trim window, moved onto the joined timeline, and merged by language.
--decode <PLAN> auto (default), whole, fastest, gpu:N, ranges:N The decode plan, as for transcode (--decode-gpu N still works).
--encode <PLAN> all (default), per-rung, single, gpu:N, family:VENDOR The encode plan, as for transcode. A splice always takes the serial encode path, so here it chooses the card.

Text subtitles come along: each clip's cues are trimmed with the clip, shifted by the length of the clips before it, and tracks join by language (clip 2's eng continues clip 1's eng; a language only some clips have is carried where it exists).

Examples

# Join three clips end-to-end
rivet splice -o out.mp4 intro.mp4 body.mkv outro.mov

# Join with per-clip trims (first 5 s of A, then 10–20 s of B, then all of C)
rivet splice -o out.mp4 a.mp4@0-5 b.mp4@10-20 c.mp4 --codec h265

# A single trimmed clip is just a trim (same as transcode --trim-*)
rivet splice -o cut.mp4 a.mp4@2-7

# Concatenate straight into an HLS package
rivet splice -o out_hls/ --mode hls a.mp4 b.mp4 c.mp4 --codec h265

The library equivalents are rivet::run_splice_job(Vec<Clip>, &spec, …) and OutputSpec::with_trim(start, end) for the single-input case.


rivet probe

rivet probe <INPUT> [--json]

Inspect a file without transcoding. --json emits a machine-readable object (video_codec, width, height, frame_rate, duration); otherwise a human summary is printed.

rivet probe input.mkv
rivet probe input.mkv --json

rivet devices

rivet devices [--json]

List the GPUs rivet detects on this host — vendor, name, generation, VRAM, PCI address, and (NVIDIA only, via NVML) a live load snapshot (GPU / encoder / decoder utilization, memory, temperature). --json emits { "gpus": [ { index, vendor, name, generation, vram_mib, pci, load? } ] }.

rivet devices
rivet devices --json

This is hardware inventory — what's plugged in. What this build can actually do with it is rivet capabilities (it depends on which GPU feature the binary was compiled with).

rivet capabilities (alias caps)

rivet capabilities [--json]
rivet caps [--json]

Report what this build + host can do:

  • Encode — AV1 / H.264 / H.265 4:2:0: the compiled backends (nvenc / amf / qsv / rav1e / h26x), then by codec the bit depth (8 or 10) and whether HDR (PQ/HLG, BT.2020) is producible for each output codec, with each compiled backend's own answer: H.264 is 8-bit SDR on every hardware backend and 10-bit HDR only on h26x; AV1 is 8-bit on rav1e. The by-codec answer is what rivet transcode checks --color / --pixel-format against (rivet::spec::CodecOutputCaps). The last line, every codec, is what every output codec meets (the lowest depth, HDR only when every codec has it). --json carries the same numbers: encode.by_codec[{"codec","max_bit_depth","hdr","backends":[{"backend","max_bit_depth","hdr"}]}] — and encode.max_bit_depth / encode.hdr for every codec. Until 2026-09-14 the JSON fields were the union — the best codec's answer — and said 10-bit HDR on an h26x-fallback-only build, which has no AV1 encoder; the text report led with that union (max depth / HDR lines) until 2026-09-18. Read by_codec for one codec's answer.
  • Decode — a codec → backends table (which of nvdec / amf / qsv / rav1d decode h264 / hevc / vp8 / vp9 / av1 / mpeg2 / mpeg4 / prores; rav1d decodes AV1 only).
  • Devices — a one-line summary of the detected GPUs.

A backend only appears if its feature was compiled in (--features nvidia etc.); the actual silicon (e.g. NVENC AV1 needs Ada+) is verified at encode time.

cargo build --release --features qsv
rivet capabilities            # Encode: qsv 10-bit HDR · Decode: h264/hevc/av1/vp9 → qsv
rivet caps --json

rivet pipe

rivet pipe [--crf N] [--target T] [--gop FRAMES]
           [--video-bitrate BPS] [--video-buffer DURATION]
           [--audio auto|opus|drop] [--audio-bitrate BPS] [--audio-filter CHAIN]
           [--color sdr|hdr10|hlg|passthrough] [--bit-depth auto|8bit|10bit]
           [--max-fps F] [--width W] [--height H] [--gpu I]
           [--decode PLAN] [--encode PLAN] [--filter CHAIN]

Stream a transcode through standard I/O: read media from stdin, write the AV1/MP4 to stdout (progress goes to stderr so stdout stays clean). With no flags it's the zero-config transcode (rivet::transcode_bytes: source resolution, AV1, audio passthrough, and rivet transcode's default picture — an HDR source tonemapped to 8-bit SDR, an SDR source at its own depth). A 10-bit SDR source on a build whose AV1 encoder is 8-bit (rav1e) is refused before anything is decoded, naming the setting that narrows it: --pixel-format 8bit (alias of --bit-depth), which sends the job through the job engine, as any flag does. (Until 2026-09-18 it asked rav1e for 10-bit AV1 and failed with "no Av1 encoder available … rebuild with --features rav1e-fallback".) The flags override per job — --width/--height scale, --color/--bit-depth set HDR/depth, --crf/--speed set quality:

cat input.mkv | rivet pipe > output.mp4                       # defaults
cat input.mkv | rivet pipe --crf 28 --width 1280 --height 720 > out.mp4
ffmpeg -i src.mov -f matroska - | rivet pipe --color hdr10 | ./my-uploader

Single MP4 only — for an HLS package or a multi-rung ladder use transcode with a directory output, or the HTTP API.

rivet batch

cargo build --release --features batch   # opt-in
rivet batch <MANIFEST> [--dry-run] [--stop-on-error]

Convert many files in one run from a YAML or JSON manifest — you list the files (and how), rivet does them. Each job is an input (file or glob), an output, and any transcode setting, on top of optional shared defaults. --dry-run parses + expands globs + lists the planned jobs without converting; --stop-on-error aborts on the first failure (default keeps going and exits non-zero if any failed).

rivet batch jobs.yaml --dry-run
rivet batch jobs.yaml
output_dir: out
defaults: { crf: 28, color: sdr }
jobs:
  - input: in/a.mkv
    output: out/a.mp4
    crf: 24
  - input: "clips/*.mp4"   # glob -> one job per file -> out/<name>.mp4
    output: out/

Full DSL reference: batch.md — every key, the output-path rules, glob inputs, defaults merge, and JSON examples. A ready-to-edit manifest is in examples/batch.yaml / .json.

rivet ipc

cargo build --release --features ipc   # opt-in; the subcommand only exists in an ipc build
rivet ipc --socket <PATH>

Run a Unix-domain-socket server (opt-in ipc feature; Unix only at runtime) so a long-running application can stream jobs in and out without spawning a process per file or going through HTTP. rivet pipe (stdin/stdout streaming) is always available and needs no feature. Bind a socket, then for each connection: the client optionally writes a settings header line, then the input media, half-closes its write side (signals end-of-input), and reads the transcoded AV1/MP4 back until EOF. One thread per connection; the process-wide GPU pool serializes the actual GPU work, so concurrent clients simply queue.

Settings header (optional): if the stream begins with #rivet, the first line is parsed as space-separated key=value settings and stripped before decode. The keys are the shared TranscodeSettings vocabulary — the same names as the CLI flags (mode rung ladder max-short-side segment-seconds crf target gop video-bitrate video-buffer audio audio-bitrate audio-filter subtitles color bit-depth seam max-fps encode decode gpu gpu-family single-gpu decode-gpu encode-policy width height filter codec), with the same values and the same meaning — a #rivet encode=per-rung decode=whole header is exactly --encode per-rung --decode whole. Real container magic bytes never start with #rivet, so a raw media stream without a header just gets the defaults. (A single socket connection produces one MP4, so mode=hls/multi-rung isn't supported here — use the HTTP API for that.)

#rivet crf=28 color=hdr10 width=1280 height=720\n
<media bytes…>
rivet ipc --socket /tmp/rivet.sock &
# any client that does write → shutdown(WR) → read works, e.g. socat (no header):
socat - UNIX-CONNECT:/tmp/rivet.sock < input.mkv > output.mp4

A minimal client with settings (Python):

import socket
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect("/tmp/rivet.sock")
s.sendall(b"#rivet crf=28 width=1280 height=720\n")   # optional settings header
s.sendall(open("input.mkv", "rb").read())
s.shutdown(socket.SHUT_WR)                             # end-of-input
out = b"".join(iter(lambda: s.recv(65536), b""))
open("output.mp4", "wb").write(out)                    # AV1/MP4

Single MP4 per connection. On Windows rivet ipc is unavailable — use rivet pipe (stdin/stdout) or rivet serve (HTTP).


rivet serve

rivet serve [--addr <ADDR>]

Runs the HTTP transcode API (requires a --features server build). --addr defaults to 127.0.0.1:8080. See the HTTP API reference for endpoints.

cargo build --release --features server,nvidia
rivet serve --addr 0.0.0.0:8080

Environment variables

Variable Effect
RUST_LOG Log filter, e.g. RUST_LOG=debug or RUST_LOG=rivet=info.
TRANSCODE_ENCODER_BACKEND Force an encoder backend: nvenc | amf | qsv.
DISABLE_NVDEC Skip NVDEC for every codec (fall through to the next decode tier).
DISABLE_NVDEC_<CODEC> Skip NVDEC for one family, e.g. DISABLE_NVDEC_AV1=1.
RIVET_TEST_MEDIA Integration tests: directory of real media to run against.