Note: This description is automatically generated from the GitHub repository README.
avet is an AV1 encoding service that watches a folder, splits each video at its scene cuts and encodes the chunks in parallel with SVT-AV1. It writes a finished MKV with audio, subtitles and chapters carried over, and ships as a Docker image and as a Linux AppImage with every tool bundled.
services:
avet:
image: ivenos/avet:latest
user: "1000:1000"
volumes:
- ./input:/input
- ./output:/output
restart: unless-stopped
Create input/ and output/ before the first start, otherwise Docker creates them owned by root. Without user: the container runs as root and its files on the host belong to root. On a host with SELinux, such as Fedora or openSUSE, add security_opt: [label=disable] so the container can read the folders.
[target_quality] needs a GPU, nothing else does:
devices: ["/dev/dri:/dev/dri"] and group_add with the numeric ID of the host's render group (getent group render).The arm64 image has no hardware Vulkan driver.
Download the AppImage for your architecture from the latest release, make it executable with chmod +x and run it. It watches input/ and output/ in the working directory. It needs glibc 2.39 or newer and FUSE; without FUSE, run it with --appimage-extract-and-run.
Every subfolder of the input directory is a profile: one encode.toml and the videos it applies to.
input/
├── movies/
│ ├── encode.toml
│ └── The Movie (2021).mkv
├── anime/
│ ├── encode.toml
│ └── The Show/
│ └── Season 1/
│ └── Episode 01.mkv
└── processed/ # sources move here once encoded
output/
├── The Movie (2021).mkv
└── The Show/
└── Season 1/
└── Episode 01.mkv
mkv, mp4, mov, avi, ts, m2ts, flv, webm, m4v. Only the first video track is encoded.output/ and processed/. Once a folder's last video is encoded, the empty source folder is removed..mkv, so queued files that would end up at the same output path, such as film.mkv and film.mp4, wait until one is renamed..avet_<name>/ next to the output file. Delete that folder to encode a file from scratch.encode.toml avet cannot read or rejects, a timeout or a full disk, is retried: on the next scan, then at doubling intervals of up to 60 scans, and at once after a change of encode.toml. Any other failure, such as an option the encoder refuses, writes a .failed file into that folder, and the video is skipped until you delete it or change encode.toml..failed file as well. A stop by signal does not count.SIGTERM, SIGINT or SIGHUP avet exits after the current file, on a second signal at once. A file stopped mid-encode, e.g. by Ctrl-C in a terminal or by docker stop after its timeout, resumes from its last finished chunk..avet.lock file there.avet started log line names the version, and avet --version prints it.| Variable | Default | Description |
|---|---|---|
INPUT_DIR | ./input, /input in the image | Input directory |
OUTPUT_DIR | ./output, /output in the image | Output directory |
POLL_INTERVAL | 60 | Seconds between scans |
RUST_LOG | info | Log level, e.g. debug |
Only encoder is required. Unknown keys are rejected.
encoder = "svt-av1"
[encoder_params]
preset = 6
crf = 28
[avet]
crop = true
keyint = true
scale = 1080
bit_depth = 10
[audio]
language_whitelist = ["eng"]
mode = "encode"
codec = "libopus"
bitrate = { stereo = "192k", "5.1" = "320k", "7.1" = "512k", default = "192k" }
[audio.lossless]
codec = "flac"
options = { compression_level = 12 }
[subtitles]
language_whitelist = ["eng", "jpn"]
encodersvt-av1 or svt-av1-hdr. Not needed with avet.video = "copy".
[encoder_params]Passed to the encoder as --key value; booleans become 1/0. avet reads two of them itself: it encodes one chunk per lp CPU cores at once (6 when unset) as far as free RAM allows, and crf is the first probe when [target_quality] is set. color-primaries, transfer-characteristics, matrix-coefficients, color-range and chroma-sample-position take their numeric code, e.g. 9 for BT.2020. [target_quality] cannot be combined with tbr, cqp, an rc other than 0, color-primaries, transfer-characteristics, matrix-coefficients or color-range.
[target_quality]Replaces the fixed crf: avet probes each chunk at a few CRF values and encodes at the highest one that still holds jod. CVVDP scores in JOD from 0 to 10, where 10 means no visible difference from the source.
[target_quality]
jod = 9.5
| Key | Default | Description |
|---|---|---|
jod | - | Minimum CVVDP score per chunk, above 0 and below 10 (required) |
min_crf | 1 | Lowest CRF to try, at least 1 and below max_crf |
max_crf | 70 | Highest CRF to try, at most 70 |
min_probes | 2 | Probes before tolerance may stop the search, at least 2 |
max_probes | 7 | Maximum probes per chunk, at least min_probes |
tolerance | 0.05 | Stop once a probe is at most this far above jod, at least 0 |
probe_preset | 13 | Encoder preset for probes, 0 to 13 |
max_encoded_percent | 90 | Maximum chunk size as a percent of the source's bytes for that chunk, above 0 |
max_cambi | - | Maximum CAMBI of the encode, at least 0 |
max_cambi_diff | - | Maximum CAMBI the encode may add on top of its input, at least 0 |
avet.video = "encode" and cannot be combined with avet.scale.max_encoded_percent wins over jod: a chunk that would grow past it gets a higher CRF, and a warning is logged. The sizes are those of the probes; a finished chunk that still comes out larger is logged too. Where the source's packet sizes cannot be read, the limit is off for that file.0 without banding; Netflix places slightly annoying banding at around 5. max_cambi counts banding the source already has, max_cambi_diff does not. Both are compared with the 95th percentile of a chunk's frames and are measured without film grain.done line gives the lowest of these scores, and a chunk that came out under jod although its probe held it is logged as a warning.max_encoded_percent, or the smallest probe if none is under it.[avet]| Key | Default | Description |
|---|---|---|
video | "encode" | "copy" passes the video through and only processes audio and subtitles |
dv | false | Carry Dolby Vision from an HEVC source into the output as AV1 profile 10, converting profile 7 to 8.1 first. Not with bit_depth = 8. Without it, and for other profiles, only the base layer is kept. Dolby Vision whose base layer is no picture on its own, such as profile 5, is refused either way |
crop | false | Remove black bars |
keyint | false | Keyframe every ~5 s from the frame rate, unless keyint is in [encoder_params] |
scale | - | Maximum output height before rotation, at least 64. Taller sources are scaled down with Lanczos |
bit_depth | - | Encoder input bit depth, 8 or 10. Unset keeps the source depth, capped at 10 |
keep_temp | false | Keep .avet_<name>/ after a finished encode |
[audio]| Key | Default | Description |
|---|---|---|
mode | "copy" | "copy" or "encode" |
codec | - | ffmpeg encoder, e.g. "libopus". Required for "encode" |
bitrate | - | A single value such as "192k", or a table keyed by mono, stereo, 3.0, quad, 5.0, 5.1, 6.1, 7.1 and default. Required for lossy codecs; a layout missing from a table without default gets the encoder's own bitrate |
options | {} | Extra per-track encoder options, e.g. { compression_level = 12 } |
language_whitelist | [] | Keep only these ISO 639-2 languages. Empty keeps all |
deu and ger). Tracks without a language or tagged und are always kept.pt-BR or zh-Hant.English 5.1 (Opus).[audio.lossless] applies to tracks with a lossless source (dts only as DTS-HD MA). FLAC stores floating-point PCM as 24-bit integers. [audio.codec_rules] applies by source codec as ffprobe names it. Both take the keys above except language_whitelist. Unset keys come from [audio], except a non-empty options, which replaces it. A matching codec rule wins over [audio.lossless].
[audio.codec_rules]
opus = { mode = "copy" }
eac3 = { mode = "encode", codec = "libopus", bitrate = "192k" }
[subtitles]| Key | Default | Description |
|---|---|---|
mode | "copy" | "copy" or "strip" |
language_whitelist | [] | Same rules as for audio |
Chapters are always kept. MP4 text subtitles become SRT, and subtitle tracks Matroska cannot hold, such as TTML, are skipped.
[scene_detection]| Key | Default | Description |
|---|---|---|
min_scene_len | 24 | Minimum distance between scene cuts in frames, at least 1 |
extra_split_sec | 10 | Maximum chunk length in seconds, never below 24 frames, 0 disables |
extra_split | 0 | Maximum chunk length in frames, at least 24, 0 disables, overrides extra_split_sec |
speed | "standard" | "fast" trades accuracy for speed |
downscale_height | - | Detect scenes on a copy scaled to this height, at least 64 |
Copyright © Iven Schlösser. avet is free software, licensed under the GNU General Public License v3.0 only. You may use, modify and redistribute it. Anyone distributing a modified version must release it under the same license and make its source code available.
The Docker image and the AppImage bundle third-party software, each under its own license:
The license texts are in /usr/share/licenses/ in the image and in usr/share/licenses/ and usr/share/doc/ inside the AppImage. The image's other Alpine packages name their licenses in the apk database.
avet is not affiliated with or endorsed by any of them, Dolby Laboratories or HDR10+ Technologies. "Dolby Vision" is a trademark of Dolby Laboratories Licensing Corporation, "HDR10+" is a trademark of HDR10+ Technologies, LLC.
Content type
Image
Digest
sha256:d2c138165…
Size
173.5 MB
Last updated
5 days ago
docker pull ivenos/avet