shrivel 0.2.1

Cross-platform FFmpeg orchestrator that transcodes videos to HEVC or AV1
shrivel-0.2.1 is not a library.

shrivel

A small cross-platform command-line tool that transcodes videos to H.265/HEVC or AV1 by orchestrating FFmpeg. HEVC is the default; select AV1 with --codec av1. Point it at a folder of videos and shrinks them using a shared compression scale mapped to the selected encoder.

Requirements

  • ffmpeg and ffprobe available in your PATH.
  • Rust (2024 edition) to build from source.

Installation

cargo install --path .

Or build without installing:

cargo build --release
# binary: target/release/shrivel

Usage

# Recursively convert every .mp4 in ./input into ./output
shrivel

# Custom folders and quality
shrivel --input ~/Videos/raw --output ~/Videos/small --cq 28

# Convert one file into the output directory
shrivel --input ~/Videos/clip.mp4 --output ~/Videos/converted

# Resample to 30 fps, denoise, and skip files already using the selected codec
shrivel --fps 30 --denoise --skip-same-codec

# Scale to 1280 pixels width, resample, and denoise
shrivel --scale 1280 --fps 30 --denoise

# Process several container types, forcing the CPU encoder
shrivel --ext mp4,mkv,mov --encoder software

# Encode to AV1 (auto-select hardware when usable, otherwise SVT-AV1 on CPU)
shrivel --codec av1

# Preview the FFmpeg commands without running them
shrivel --dry-run

When the input is a directory, it is searched recursively. Converted files keep their paths relative to the input folder, and the corresponding subdirectories are created inside the output folder. For example, input/trips/day1.mp4 is written to output/trips/day1.mp4. When the input is a single video file, it is written directly inside the output directory with the same filename.

Options

Option Default Description
--codec <CODEC> hevc Output codec: hevc or av1.
-i, --input <FILE_OR_DIR> input Video file or directory containing source videos.
-o, --output <DIR> output Directory for converted videos (created if missing). Must differ from the input directory.
--cq <N> 26 Normalized compression value, 0-51. Lower generally means better quality; mapped to the selected encoder's native scale.
--fps <FPS> source Target frame rate. The source frame rate is kept when omitted.
--skip-same-codec off Skip files already using the selected output codec.
--denoise off Apply a light hqdn3d=3:3:8:8 denoise filter.
--scale <WIDTH> source Target width for scaling. Height is calculated proportionally. Uses bicubic interpolation.
-e, --encoder <NAME> auto One of auto, nvenc, qsv, amf, videotoolbox, software; available choices depend on the codec.
--ext <EXT,...> mp4 Comma-separated input extensions, matched case-insensitively.
--skip-existing off Skip files whose output already exists instead of overwriting them.
--dry-run off Print the FFmpeg commands without running them.

The process exits with a non-zero status if at least one file failed to convert.

How it works

For each input file shrivel:

  1. Runs ffprobe to read the video codec, bitrate and frame rate. If the video stream has no bitrate (common in Matroska files), the container bitrate is used instead.
  2. Optionally skips the file when it already uses the selected codec (--skip-same-codec).
  3. Maps --cq to the selected encoder's native quality parameter. The mapping aligns the usable range endpoints; it does not promise equal perceptual quality or file size across codecs and encoders.
  4. Builds the video filter chain (denoise, then frame-rate change). A frame-rate change is applied only when the source differs from the target by more than 0.5 fps, and it forces constant-frame-rate output to avoid leftover variable-frame-rate drift.
  5. Runs FFmpeg, mapping the first video stream and all audio streams. Audio is copied without re-encoding. Other streams (subtitles, data) are dropped.
  6. Reports the input and output sizes. If FFmpeg fails, the partial output file is removed and the first lines of FFmpeg's error output are shown.

HEVC files in .mp4, .m4v and .mov outputs are tagged hvc1 so they play on Apple devices. AV1 outputs do not receive this HEVC-specific tag.

Encoders

With --encoder auto (the default), shrivel tries each hardware encoder supported by the selected codec in turn by encoding one tiny synthetic frame, and uses the first one that works. If none works it uses software x265 for HEVC or SVT-AV1 for AV1. An encoder being listed by ffmpeg -encoders is not enough, because hardware encoders are often compiled in but unusable on the current machine.

Encoder FFmpeg codec Native quality range used by --cq
nvenc hevc_nvenc, av1_nvenc HEVC 1-51; AV1 1-63. 0 means automatic in NVENC, so shrivel avoids it.
videotoolbox hevc_videotoolbox HEVC -q:v 100 (best) to 1 (most compression); HEVC only.
qsv hevc_qsv, av1_qsv 1-51; 1 is best quality in QSV ICQ mode.
amf hevc_amf, av1_amf HEVC QP 0-51; AV1 QP 0-255.
software libx265, libsvtav1 x265 CRF 0-51; SVT-AV1 CRF 0-63. FFmpeg must include the selected encoder.

The normalized --cq scale runs from 0 (best quality, usually larger output) to 51 (strongest compression, usually smaller output). shrivel maps it linearly onto each backend's usable native range, reversing the direction for VideoToolbox. The same value still does not imply equal quality or output size across codecs and encoders: rate-control behavior and perceptual quality differ. Check a short clip when changing backend or codec. SVT-AV1 uses preset 6 by default. Hardware AV1 encoding requires support from both FFmpeg and the system hardware/runtime.