VLC AutoUpscale is a Linux x86-64 video filter for VLC 3.x. It enlarges low-resolution video in real time with zimg (preferred) or FFmpeg swscale, then optionally sharpens luma with an unsharp-mask pass.
It is a classical resampler, not an AI upscaler. It improves presentation but cannot recover detail absent from the source.
The plugin build requires a C compiler, GNU Make, pkg-config, VLC 3 development
headers, and FFmpeg's libswscale/libavutil development files. zimg is an
optional backend: install its development package for the preferred Spline36
path, or omit it for a swscale-only build.
Debian or Ubuntu:
sudo apt update
sudo apt install build-essential pkg-config libvlccore-dev libvlc-dev libswscale-dev libavutil-dev
# Optional preferred backend:
sudo apt install libzimg-devFedora:
sudo dnf install gcc make pkgconf-pkg-config vlc-devel ffmpeg-free-devel
# Optional preferred backend:
sudo dnf install zimg-develConfirm that the mandatory pkg-config modules are visible before building:
pkg-config --exists vlc-plugin libswscale libavutilmake info reports the resolved compiler and library flags, install directory,
CPU configuration, and whether the zimg backend is enabled.
make
sudo make installmake builds the plugin and runs the ABI-layout and load-safe-ISA checks.
make install copies that artifact into the video_filter directory below
VLC's plugin root.
If VLC does not find the installed plugin, regenerate the cache from the plugin
root. Do not pass the video_filter install directory printed by make info:
plugin_root=$(pkg-config --variable=pluginsdir vlc-plugin)
sudo vlc-cache-gen "$plugin_root"VLC 3 also searches every directory listed in the VLC_PLUGIN_PATH
environment variable, so the plugin can live under $HOME instead of the
system plugin root:
make
mkdir -p ~/.local/lib/vlc/plugins/video_filter
cp build/libautoupscale_plugin.so ~/.local/lib/vlc/plugins/video_filter/
VLC_PLUGIN_PATH="$HOME/.local/lib/vlc/plugins" vlc --video-filter=autoupscale video.mkvTo make it permanent, export the variable from your shell profile
(~/.profile or ~/.bashrc):
export VLC_PLUGIN_PATH="$HOME/.local/lib/vlc/plugins"No vlc-cache-gen step is needed: VLC scans VLC_PLUGIN_PATH directories at
startup, which is negligible for a single plugin. Uninstall by deleting the
copied .so. Keep either the system-wide install or the rootless one, not
both: with two copies of the same module registered, which one VLC picks is
not defined, so an outdated copy can shadow a fresh build.
The normal make build does not need these tools. Install only the groups for
the verification targets you intend to run:
| Targets | Additional software |
|---|---|
make test, make fuzz-smoke |
GCC or Clang with ASan and UBSan runtime support |
make mutation-test |
Python 3 and a C compiler |
make stress |
GCC or Clang with ASan, UBSan, and TSan runtime support |
make fuzz |
Clang with libFuzzer support |
make check, make complexity |
Python 3 and Lizard |
make analyze |
Lizard, cppcheck, ShellCheck, actionlint, rumdl, and lychee |
make scan-build |
Clang and scan-build (clang-tools) |
make coverage |
GCC, gcov, Python 3, gzip, and standard POSIX shell tools |
make test-zimg, make fuzz-seam, make stress-zimg, make bench-zimg, make bench-pipeline, make coverage-zimg |
Mandatory plugin dependencies plus zimg development files |
make check-hardening, make check-visibility, make check-load-safe-isa, make check-multiversion-isa |
GNU binutils (readelf, nm, and objdump) |
On Debian or Ubuntu, prepare the complete local verification environment with:
sudo apt install ca-certificates curl tar clang clang-tools cppcheck shellcheck python3 python3-venv binutils gzip
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --only-binary=:all: --require-hashes \
-r .github/requirements-ci.txtThe requirements file keeps the local Lizard version synchronized with CI. Install the same checksum-verified actionlint, rumdl, and lychee binaries used by CI:
mkdir -p "$HOME/.local/bin"
curl -sSLo actionlint.tar.gz \
https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz
echo '023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757 actionlint.tar.gz' \
| sha256sum -c -
tar -xzf actionlint.tar.gz -C "$HOME/.local/bin" actionlint
curl -sSLo rumdl.tar.gz \
https://github.com/rvben/rumdl/releases/download/v0.2.34/rumdl-v0.2.34-x86_64-unknown-linux-gnu.tar.gz
echo '5fc1844544609504a5a8e9d37e1ef628a4a5938028dceda2b9c799173bf47739 rumdl.tar.gz' \
| sha256sum -c -
tar -xzf rumdl.tar.gz -C "$HOME/.local/bin" rumdl
curl -sSLo lychee.tar.gz \
https://github.com/lycheeverse/lychee/releases/download/lychee-v0.24.2/lychee-x86_64-unknown-linux-gnu.tar.gz
echo '1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a lychee.tar.gz' \
| sha256sum -c -
tar -xzf lychee.tar.gz -C "$HOME/.local/bin" \
--strip-components=1 lychee-x86_64-unknown-linux-gnu/lychee
export PATH="$HOME/.local/bin:$PATH"
rm actionlint.tar.gz rumdl.tar.gz lychee.tar.gzThe archives above are for Linux x86-64, matching this project's compatibility contract. Keep the virtual environment active and verify the toolchain with:
command -v lizard cppcheck shellcheck actionlint rumdl lychee \
scan-build gcov python3Fedora users can install the packaged tools with clang, clang-tools-extra,
cppcheck, ShellCheck, python3, python3-pip, binutils, and gzip;
install Lizard in a virtual environment and actionlint from its verified
upstream release. Use the verified rumdl and lychee releases above as well.
Enable the filter for one launch:
vlc --video-filter=autoupscale path/to/video.mp4AUTO mode skips sources at or above 720p. For a persistent setup, enable
AutoUpscale under Tools → Preferences → All → Video → Filters, or add
video-filter=autoupscale to ~/.config/vlc/vlcrc.
VLC's direct display chain can resize the filter output back to the decoded
size or hit Too high level of recursion (3), especially with hardware decode.
The recursion error is a stock VLC 3.0 converter-chain limit and cannot be
raised by an installed filter plugin. Use the transcode display path when you
need a forced-1080p result to reach the renderer:
vlc --avcodec-hw=none --autoupscale-target=2 --autoupscale-algo=3 --autoupscale-usm=20 --sout='#transcode{vcodec=h264,acodec=mp4a,vb=10000,ab=128,venc=x264{preset=ultrafast,tune=zerolatency},vfilter=autoupscale}:display' path/to/video.mp4The Nemo installer exposes this compatibility profile through
vlc-autoupscale --transcode-display, trading additional real-time
encode/decode work for reliable enlarged display dimensions. The installer
verifies the required VLC modules once at install time; re-check any time
with vlc-autoupscale --check-transcode-display.
See usage and troubleshooting for practical variants.
| Option | Range | Default | Effect |
|---|---|---|---|
--autoupscale-target |
0–6 | 0 | 0 AUTO; 1 720p; 2 1080p; 3 1440p; 4 4K; 5 5K; 6 8K. All targets have a 4× linear ratio cap. |
--autoupscale-algo |
0–3 | 3 | 0 fast bilinear; 1 bicubic; 2 Lanczos; 3 Spline36. swscale maps Spline36 to Lanczos. |
--autoupscale-skip-above |
0–8192 | 720 | In AUTO, skip sources at or above this height. 0 disables the gate. |
--autoupscale-usm |
0–200 | 20 | Luma sharpening percentage. 0 disables USM. |
--autoupscale-backend |
0–2 | 0 | 0 prefer zimg with swscale fallback; 1 zimg only; 2 swscale only. |
--autoupscale-threads |
0–64 | 0 | 0: zimg uses CPUs/2 - 2, clamped to 1–12; USM uses 8 workers through 1280×720 output pixels, 12 through 1920×1080, and 16 above that, capped by allowed CPUs. Explicit 1–64 preferences apply to both pools, limited by allowed CPUs and frame geometry. Default USM stripe rows add no worker cap. |
--autoupscale-adaptive-usm |
0–1 | 0 | Experimental USM worker search to reduce processing time per frame. Requires threads=0 and active sharpening. Tries counts up to 64 within CPU/stripe limits, retaining confirmed improvements. Trials add temporary latency and resource cost. Resolution, algorithm and zimg grid stay fixed. See adaptive measurements. |
--autoupscale-pin-threads |
0–1 | 1 | Best-effort zimg worker pinning. Disable if it regresses the deployment host. |
--autoupscale-zerocopy-dst |
0–1 | 1 | Direct zimg writes on compatible row grids. 0 forces copy-out. |
--autoupscale-zerocopy-src |
0–1 | 1 | Direct zimg reads. 0 forces copy-in and disables column tiling. A resulting grid change can create bounded resampling seams, so copy and direct modes are byte-identical only when their grid is unchanged. |
--autoupscale-content-probe |
0–1 | 1 | Emit one advisory for soft and blocky sources. It never changes output. |
--autoupscale-usm-stripe-min-rows |
0–256 | 0 | Minimum USM rows per worker. 0 selects 8. |
--autoupscale-zimg-stripe-lines |
0–128 | 0 | Advanced minimum zimg output lines per stripe. It can change the row/column grid and output seams; benchmark it with integration quality checks. 0 selects the validated default of 16. |
--autoupscale-usm-sharp-threshold |
0–20000 | 3500 | Skip USM on grainy sources above this metric. 0 disables skipping. |
AUTO chooses 1080p only with at least four available CPUs and an upscale ratio
no greater than 4×. Otherwise it chooses 720p. Explicit targets bypass
skip-above but never downscale. AUTO never gates its choice on detected RAM;
allocation failures are handled by the normal backend failure path.
make # host-tuned plugin
make MARCH=x86-64 MULTIVERSION=1 # portable x86-64 build with runtime SIMD selection
make test # unit/contract tests with ASan and UBSan
make mutation-test # curated control-plane mutants must all be killed
make fuzz-smoke # deterministic sanitizer fuzzing
make check # complexity plus tests
make analyze # complexity and static analysis
make scan-build # Clang analyzer on plugin configurations
make coverage # configured file/function coverage gates
make test-zimg # zimg integration tests
make stress # USM concurrency stress
make stress-zimg # zimg concurrency stress
make check-hardening # linked-plugin hardening checks
make check-visibility # exported-symbol check
sudo make uninstall # remove the installed pluginThe default MARCH=native MULTIVERSION=0 build is for the build host. Use
MARCH=x86-64 MULTIVERSION=1 when distributing to other Linux x86-64 systems.
MARCH controls the baseline of the plugin implementation; MULTIVERSION=1
only adds runtime-selected SSE2, AVX2, and AVX-512 variants for the USM kernel.
It does not lower the baseline of the scaler or VLC-facing implementation.
The VLC descriptor and guarded open callback are always built at the x86-64
baseline without LTO. For a v3 or v4 implementation, that callback verifies the
required CPU level before entering higher-ISA code. The default make and
explicit make plugin targets run check-load-safe-isa automatically. A
MARCH=native artifact is still host-specific; use the explicit x86-64
baseline for distribution.
The environment-preparation section above maps every target to its required software. Missing optional tools do not affect a normal plugin build.
make mutation-test changes isolated temporary copies only. Its curated
mutants cover target selection and safety caps, AUTO skip policy, backend
priority/fallback, content-probe confidence and advisory boundaries, and the
USM sharpness gate. Baseline suites must compile and pass first. Each mutant
must compile, then make its owning suite exit with status 1; a surviving
mutant, compile failure, abnormal exit, or timeout fails the target.
Supported: Linux x86-64 and VLC 3.x. make test, make check,
make fuzz-smoke, make fuzz, and MULTIVERSION=1 require GCC 11+ or
Clang 12+ for -march=x86-64-v3/v4. VLC 4, other operating systems, and other
architectures are outside the compatibility contract.
Useful diagnostics:
vlc-plugin pkg-config not found: install the VLC development package.- Plugin not listed: run
make info, reinstall, then regenerate VLC's cache. - Opaque/unsupported chroma: try
--avcodec-hw=none. - Playback cannot keep up: try
--autoupscale-algo=1,--autoupscale-usm=0, or--autoupscale-target=1.