From 4f2f78fe1899d029dfb8f7f9491de3ad5efaa715 Mon Sep 17 00:00:00 2001 From: Jessey Fransen Date: Sun, 27 Sep 2026 22:06:47 +0200 Subject: [PATCH 1/4] fix(video): enable NVDEC in Linux release builds --- .github/workflows/release.yml | 13 +++++++++++- CHANGELOG.md | 4 ++++ docs/video-wallpaper.md | 3 +++ scripts/benchmark-video.sh | 23 +++++++++++++++------ wallr-core/examples/video_probe.rs | 19 +++++++++++++++++ wallr-core/src/video/decoder.rs | 33 ++++++++++++++++++------------ 6 files changed, 75 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad04e82..64c4560 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,7 +78,7 @@ jobs: run: | mkdir -p "${{ github.workspace }}/.vcpkg-cache" /opt/vcpkg/vcpkg install \ - 'ffmpeg[core,avcodec,avformat,swscale,swresample]:x64-linux-static' \ + 'ffmpeg[core,avcodec,avformat,swscale,swresample,nvcodec]:x64-linux-static' \ --overlay-triplets="$GITHUB_WORKSPACE/.github/vcpkg-triplets" - name: Install stable Rust @@ -110,6 +110,17 @@ jobs: PKG_CONFIG_ALL_STATIC: "1" run: cargo build --release --bin wallr --features static-ffmpeg + - name: Verify static FFmpeg hardware support + env: + MEDIA_PREFIX: /opt/vcpkg/installed/x64-linux-static + PKG_CONFIG_PATH: /opt/vcpkg/installed/x64-linux-static/lib/pkgconfig + PKG_CONFIG_ALL_STATIC: "1" + run: | + set -euo pipefail + capabilities=$(cargo run -q -p wallr-core --example video_probe --release --features static-ffmpeg -- --capabilities) + printf '%s\n' "$capabilities" + grep -qx 'cuda: enabled' <<<"$capabilities" + - name: Verify binary is self-contained run: | set -euo pipefail diff --git a/CHANGELOG.md b/CHANGELOG.md index 31107c9..f284b85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ and versions follow [Semantic Versioning](https://semver.org/). ### Fixed +- Linux release binaries now include FFmpeg NVCodec support, so configured + NVDEC playback no longer silently falls back to CPU decoding. +- Explicit hardware decode failures are reported as software fallback rather + than ordinary software decoding. - Fixed false hardware-decoding reports when a decoder opened but produced no hardware frames. - Fixed decoder shutdown caused by transient queue pressure. diff --git a/docs/video-wallpaper.md b/docs/video-wallpaper.md index 5ac70d8..cbe2803 100644 --- a/docs/video-wallpaper.md +++ b/docs/video-wallpaper.md @@ -20,6 +20,9 @@ MP4, WebM, MKV, MOV, and AVI. Anything FFmpeg can demux is a candidate; the FFmp - Frames are decoded into a small bounded latest-frame queue and presented on PTS timing. Temporary queue pressure drops stale frames instead of terminating the decoder or allowing memory growth. - `wallpaper.loop_video` (default `true`) restarts the stream when it ends, producing a continuous loop. - `wallr ipc info` distinguishes hardware negotiation, active hardware frames, software decoding, software fallback, and decoder failure. A backend is not reported as active until a hardware frame has actually been received. +- Official Linux release binaries include FFmpeg NVCodec support; NVDEC still + loads the installed NVIDIA driver at runtime and falls back cleanly when it + is unavailable. ## Playback control diff --git a/scripts/benchmark-video.sh b/scripts/benchmark-video.sh index be2ac7c..125ec12 100755 --- a/scripts/benchmark-video.sh +++ b/scripts/benchmark-video.sh @@ -13,19 +13,29 @@ mkdir -p "$(dirname "$report")" run_probe() { local backend=$1 - cargo run -q -p wallr-core --example video_probe --release -- "$input" "$backend" 2>&1 + TIMEFORMAT='wall_seconds=%R user_seconds=%U system_seconds=%S' + { time cargo run -q -p wallr-core --example video_probe --release -- "$input" "$backend"; } 2>&1 } software=$(run_probe software) vaapi=$(run_probe vaapi || true) +nvdec=$(run_probe nvdec || true) frames=$(awk '/^decoded / {print $2; exit}' <<<"$software") +vaapi_frames=$(awk '/^decoded / {print $2; exit}' <<<"$vaapi") +nvdec_frames=$(awk '/^decoded / {print $2; exit}' <<<"$nvdec") software_rate=$(sed -n 's/^decoded .* (\([0-9.]*\) fps,.*/\1/p' <<<"$software" | head -n1) software_state=$(sed -n 's/^active backend: \([^ ]*\) (state:.*/\1/p' <<<"$software" | head -n1) vaapi_rate=$(sed -n 's/^decoded .* (\([0-9.]*\) fps,.*/\1/p' <<<"$vaapi" | head -n1) vaapi_state=$(sed -n 's/^active backend: \([^ ]*\) (state:.*/\1/p' <<<"$vaapi" | head -n1) +nvdec_rate=$(sed -n 's/^decoded .* (\([0-9.]*\) fps,.*/\1/p' <<<"$nvdec" | head -n1) +nvdec_state=$(sed -n 's/^active backend: \([^ ]*\) (state:.*/\1/p' <<<"$nvdec" | head -n1) software_drops=$(awk -F'dropped frames: ' '/^active backend:/ {gsub(/\).*/, "", $2); print $2; exit}' <<<"$software") vaapi_drops=$(awk -F'dropped frames: ' '/^active backend:/ {gsub(/\).*/, "", $2); print $2; exit}' <<<"$vaapi") +nvdec_drops=$(awk -F'dropped frames: ' '/^active backend:/ {gsub(/\).*/, "", $2); print $2; exit}' <<<"$nvdec") +software_cpu=$(awk -F'[ =]' '/^wall_seconds=/ {printf "%.3f", $4 + $6; exit}' <<<"$software") +vaapi_cpu=$(awk -F'[ =]' '/^wall_seconds=/ {printf "%.3f", $4 + $6; exit}' <<<"$vaapi") +nvdec_cpu=$(awk -F'[ =]' '/^wall_seconds=/ {printf "%.3f", $4 + $6; exit}' <<<"$nvdec") { echo "# Wallr video decoder benchmark" @@ -33,14 +43,15 @@ vaapi_drops=$(awk -F'dropped frames: ' '/^active backend:/ {gsub(/\).*/, "", $2) echo "Machine-specific decoder measurements. This report does not measure compositor presentation or GPU render time." echo printf -- '- Date (UTC): %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S')" - printf -- '- Host: %s\n' "$(hostname)" + printf -- '- Host: %s\n' "$(uname -n)" printf -- '- Input: `%s`\n' "$input" printf -- '- Wallr version: %s\n' "$(cargo run -q -p wallr -- --version 2>/dev/null)" echo - echo "| Backend | Frames | Throughput (fps) | Decoder state | Dropped frames |" - echo "|:--|--:|--:|:--|--:|" - printf '| Software | %s | %s | %s | %s |\n' "${frames:-unavailable}" "${software_rate:-unavailable}" "${software_state:-unavailable}" "${software_drops:-unavailable}" - printf '| VAAPI | %s | %s | %s | %s |\n' "${frames:-unavailable}" "${vaapi_rate:-unavailable}" "${vaapi_state:-unavailable}" "${vaapi_drops:-unavailable}" + echo "| Backend | Frames | Throughput (fps) | CPU time (s) | Decoder state | Dropped frames |" + echo "|:--|--:|--:|--:|:--|--:|" + printf '| Software | %s | %s | %s | %s | %s |\n' "${frames:-unavailable}" "${software_rate:-unavailable}" "${software_cpu:-unavailable}" "${software_state:-unavailable}" "${software_drops:-unavailable}" + printf '| VAAPI | %s | %s | %s | %s | %s |\n' "${vaapi_frames:-unavailable}" "${vaapi_rate:-unavailable}" "${vaapi_cpu:-unavailable}" "${vaapi_state:-unavailable}" "${vaapi_drops:-unavailable}" + printf '| NVDEC | %s | %s | %s | %s | %s |\n' "${nvdec_frames:-unavailable}" "${nvdec_rate:-unavailable}" "${nvdec_cpu:-unavailable}" "${nvdec_state:-unavailable}" "${nvdec_drops:-unavailable}" } >"$report" echo "Report saved to: $report" diff --git a/wallr-core/examples/video_probe.rs b/wallr-core/examples/video_probe.rs index 6ced2d0..b1b643e 100644 --- a/wallr-core/examples/video_probe.rs +++ b/wallr-core/examples/video_probe.rs @@ -11,6 +11,10 @@ use wallr_core::video::{HwAccel, VideoDecoder}; fn main() { let mut args = std::env::args().skip(1); let path = args.next().expect("usage: video_probe