# VMAF Optimiser Automated video library optimization to AV1 using VMAF (Video Multimethod Assessment Fusion) quality targets. Intelligently searches for optimal encoding parameters and gracefully degrades quality when needed to achieve target file size savings. ## Quick Start ### Requirements **Prerequisites:** - Python 3.8+ - FFmpeg with VMAF support (`ffmpeg -filters 2>&1 | grep libvmaf`) - ab-av1 binary (v0.10.3+) **Installation:** ```bash # Install ab-av1 via cargo cargo install ab-av1 # Or download pre-built binary wget https://github.com/alexheretic/ab-av1/releases/download/v0.10.3/ab-av1-x86_64-unknown-linux-musl chmod +x ab-av1 ``` ## Running the Optimiser ### Windows / macOS Use the PowerShell wrapper script: ```powershell # Interactive mode (shows prompts) .\run_optimisation.ps1 # Direct execution with parameters .\run_optimisation.ps1 --directory "D:\Movies" --vmaf 95 --preset 6 --workers 1 ``` **Available flags:** - `--directory ` - Root directory to scan (default: current directory) - `--vmaf ` - Target VMAF score (default: 95.0) - `--preset ` - SVT-AV1 Preset (default: 6) - `--workers ` - Concurrent files to process (default: 1) - `--samples ` - Samples for CRF search (default: 4) - `--thorough` - Use thorough mode (slower, more accurate) - `--encoder ` - ab-av1 encoder (default: svt-av1) - `--hwaccel ` - Hardware acceleration (default: none) ### Linux / WSL Use the bash wrapper script: ```bash # Interactive mode ./run_optimisation.sh # Direct execution with parameters ./run_optimisation.sh --directory /mnt/Media/Movies --vmaf 95 --workers 1 ``` **Same flags as PowerShell version:** - `--directory ` - Root directory to scan - `--vmaf ` - Target VMAF score - `--preset ` - SVT-AV1 Preset - `--workers ` - Concurrent files to process - `--samples ` - Samples for CRF search - `--thorough` - Use thorough mode - `--encoder ` - ab-av1 encoder - `--hwaccel ` - Hardware acceleration ## How It Works ### Phase 1: Video Analysis 1. Scans directory for video files (.mkv, .mp4) 2. Uses `ffprobe` to get: - Codec (h264, hevc, etc.) - Resolution (width × height) - Bitrate (calculated from size/duration) - HDR status (color transfer detection) 3. Skips if already AV1 encoded ### Phase 2: VMAF Target Search (Intelligent Fallback) The script tries VMAF targets in **descending order** (highest quality first): ``` Try VMAF 94 (Premium) ↓ Can achieve? ↓ Yes ↓ No Calculate savings Try VMAF 93 ↓ Savings ≥ 12%? ↓ Yes ↓ No Encode at VMAF 94 Calculate savings ↓ Savings ≥ 12%? ↓ Yes ↓ No Encode at VMAF 93 Find 15% (test 92, 90) ``` **Fallback Logic:** - If VMAF 94 gives ≥12% savings → **Encode at VMAF 94** - If VMAF 94 <12% but VMAF 93 ≥12% → **Encode at VMAF 93** - If both <12% → Find what VMAF gives 15%+ savings: - Tests VMAF 93, 92, 90 - Reports "FOUND 15%+ SAVINGS" with exact parameters - Logs for manual review (no encoding) ### Phase 3: CRF Search Uses `ab-av1 crf-search` with `--thorough` flag: - Takes multiple samples (20-30s segments) from video - Interpolates binary search for optimal CRF - Outputs: Best CRF, Mean VMAF, Predicted size - Uses `--samples` to control accuracy (default: 4 samples) ### Phase 4: Full Encoding (with Real-time Output) If savings threshold met: 1. Runs `ab-av1 encode` with found CRF 2. **Streams all output in real-time** (you see progress live) 3. Shows ETA, encoding speed, frame count 4. Uses `--acodec copy` to preserve audio/subtitles **Real-time output example:** ``` → Running encoding (CRF 34) Encoded 4320/125400 frames (3.4%) Encoded 8640/125400 frames (6.9%) Encoded 12960/125400 frames (10.3%) ... Encoded 125400/125400 frames (100.0%) Speed: 15.2 fps, ETA: 2s ``` ### Phase 5: Verification & Replacement 1. Probes encoded file for actual stats 2. Calculates actual savings 3. Only replaces original if new file is smaller 4. Converts .mp4 to .mkv if needed ## Configuration Key settings (edit in `optimize_library.py`): ```python TARGETS = [94.0, 93.0, 92.0, 90.0] # VMAF targets to try MIN_SAVINGS_PERCENT = 12.0 # Encode if savings ≥12% TARGET_SAVINGS_FOR_ESTIMATE = 15.0 # Estimate for this level PRESET = 6 # SVT-AV1 preset (4=best, 8=fast) EXTENSIONS = {".mkv", ".mp4", ".mov", ".avi", ".ts"} ``` ### What is CRF? **Constant Rate Factor (CRF):** Quality/bitrate trade-off - **Lower CRF** = Higher quality, larger files (e.g., CRF 20) - **Higher CRF** = Lower quality, smaller files (e.g., CRF 40) - **AV1 CRF range:** 0-63 (default for VMAF 94 is ~34-36) ### What is VMAF? **Video Multimethod Assessment Fusion:** Netflix's quality metric - **VMAF 95:** "Visually lossless" - indistinguishable from source - **VMAF 94:** Premium quality - minor artifacts - **VMAF 93:** Good quality - acceptable for most content - **VMAF 90:** Standard quality - may have noticeable artifacts - **VMAF 85:** Acceptable quality for mobile/low bandwidth ## Hardware Acceleration **Automatic hwaccel detection:** When `--hwaccel auto` is specified, the script selects appropriate hardware acceleration: | Platform | Auto Selection | Notes | |-----------|----------------|--------| | Windows | d3d11va | Direct3D Video Acceleration | | macOS | videotoolbox | VideoToolbox framework | | Linux/WSL | vaapi | Video Acceleration via VA-API | **Discrete GPU vs iGPU priority:** - **Discrete GPU (e.g., AMD RX 7900 XT) takes priority over iGPU** - FFmpeg/ab-av1 will prefer the more capable encoder - For AV1 encoding, discrete GPU is selected if present **To disable hardware acceleration:** ```powershell .\run_optimisation.ps1 --hwaccel none ``` ```bash ./run_optimisation.sh --hwaccel none ``` ## Running on Multiple Machines ### Lock File Mechanism Each video file has a corresponding lock file: ``` /opt/Optmiser/.lock/{video_filename} ``` **Process:** 1. Machine A checks for lock → None found, creates lock 2. Machine A starts encoding 3. Machine B checks for lock → Found, skips file 4. Machine A finishes, removes lock 5. Machine B can now process that file **Result:** Different machines automatically process different files! ### Multi-Machine Setup **Machine 1 (Linux Server - Intel i9-12900H):** ```bash cd /opt/Optmiser git pull origin main ./run_optimisation.sh /mnt/Media/movies --vmaf 95 ``` **Machine 2 (Windows PC - AMD RX 7900 XT):** ```powershell cd C:\Optmiser git pull origin main .\run_optimisation.ps1 D:\Media\movies --vmaf 95 --hwaccel auto ``` **Machine 3 (Another Linux PC):** ```bash cd /opt/Optmiser git pull origin main ./run_optimisation.sh /home/user/Media/tv --vmaf 95 ``` All three can run simultaneously - lock files prevent duplicates! ## Logging System All logs stored in `/opt/Optmiser/logs/` directory: | File | Purpose | |------|---------| | `tv_movies.jsonl` | Successful TV & Movie encodes | | `content.jsonl` | Successful Content folder encodes | | `low_savings_skips.jsonl` | Files with <12% savings + 15% estimates | | `failed_searches.jsonl` | Files that couldn't hit any VMAF target | | `failed_encodes.jsonl` | Encoding errors | ### Log Entry Format **Successful encode:** ```json { "file": "/path/to/file.mkv", "status": "success", "vmaf": 94.0, "crf": 34.0, "before": { "codec": "h264", "bitrate": 8500, "size": 2684354560, "duration": 1379.44 }, "after": { "codec": "av1", "bitrate": 6400, "size": 2013265920, "duration": 1379.44 }, "duration": 145.2, "savings": 25.0, "timestamp": "2025-12-31T12:00:00.000Z" } ``` **Low savings with 15% estimate:** ```json { "file": "/path/to/file.mkv", "vmaf_94": 94.0, "savings_94": 7.0, "vmaf_93": 93.0, "savings_93": 18.0, "target_for_15_percent": { "target_vmaf": 93, "crf": 37, "savings": 18.0, "quality_drop": 1, "found": true }, "recommendations": "logged_for_review", "timestamp": "2025-12-31T12:00:00.000Z" } ``` ### Viewing Logs ```bash # Watch logs in real-time tail -f /opt/Optmiser/logs/tv_movies.jsonl | jq '.' # Check files logged for review (both 94 and 93 <12%) cat /opt/Optmiser/logs/low_savings_skips.jsonl | jq '.[] | select(.recommendations=="logged_for_review")' # Statistics jq -r '.status' /opt/Optmiser/logs/tv_movies.jsonl | sort | uniq -c # Find what CRF/VMAF combinations are being used most jq -r '[.vmaf, .crf] | @tsv' /opt/Optmiser/logs/tv_movies.jsonl | sort | uniq -c ``` ## Troubleshooting ### Issue: "0k bitrate" display **Cause:** VBR (Variable Bitrate) files show 0 in ffprobe's format bitrate field. **Solution:** Calculate from `(size × 8) / duration` ### Issue: Multiple machines encoding same file **Cause:** No coordination between machines. **Solution:** Lock files in `/opt/Optmiser/.lock/{video_filename}` ### Issue: Encode fails with "unexpected argument" **Cause:** Using wrong flags for ab-av1 commands. **Solution:** Script now validates ab-av1 support at runtime and warns gracefully. ### Issue: Out of Memory **Solution:** Reduce workers or increase swap: ```bash # Increase swap (if needed) sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # Use more conservative settings ./run_optimisation.sh --workers 1 --vmaf 93 ``` ## Best Practices ### For Servers (Intel i9-12900H) 1. **Use 50% CPU mode** if running other services (Plex, Jellyfin): ```bash ./run_optimisation.sh --workers 1 --cpu-limit 50 ``` 2. **Run during off-peak hours** to minimize impact on users 3. **Monitor CPU temperature**: ```bash watch -n 2 'sensors | grep "Package id"' ``` 4. **Use higher preset for faster encodes** (preset 7-8): ```bash ./run_optimisation.sh --preset 8 --vmaf 93 ``` ### For Windows PC (AMD RX 7900 XT) 1. **Enable hardware acceleration** for massive speedup: ```powershell .\run_optimisation.ps1 --hwaccel auto ``` 2. **Test small sample first** to verify settings: ```powershell .\run_optimisation.ps1 --directory "D:\Media\sample" --thorough --vmaf 95 ``` 3. **Monitor GPU usage**: ```powershell # Task Manager or radeontop (if available) ``` 4. **Consider quality compensation:** GPU encoding may need slightly lower VMAF target (e.g., VMAF 92) to match CPU quality. ### For WSL 1. **Access Windows drives via /mnt/c/:** ```bash ls /mnt/c/Media/movies ``` 2. **Increase memory limits** if encoding 4K content: ```bash # Edit ~/.wslconfig [wsl2] memory=16GB ``` ## Customization ### Changing VMAF Targets Edit `optimize_library.py`: ```python # More aggressive (smaller files, lower quality) TARGETS = [92.0, 90.0, 88.0] # Conservative (larger files, higher quality) TARGETS = [95.0, 94.0, 93.0] ``` ### Changing Savings Threshold ```python # More aggressive (encode more) MIN_SAVINGS_PERCENT = 8.0 # Less aggressive (encode fewer) MIN_SAVINGS_PERCENT = 15.0 ``` ### Changing Encoder Preset ```python # Faster encodes (larger files, lower quality) PRESET = 8 # Better quality (slower encodes, smaller files) PRESET = 4 ``` ### Changing Estimate Target ```python # Target higher savings for estimates TARGET_SAVINGS_FOR_ESTIMATE = 20.0 ``` ## File Structure ``` /opt/Optmiser/ ├── optimize_library.py # Main encoding engine ├── run_optimisation.sh # Linux/Server wrapper ├── run_optimisation.ps1 # Windows wrapper ├── bin/ │ └── ab-av1 # ab-av1 binary ├── tmp/ # Temporary encoding files ├── logs/ # Log files (JSONL format) │ ├── tv_movies.jsonl │ ├── content.jsonl │ ├── low_savings_skips.jsonl │ ├── failed_searches.jsonl │ └── failed_encodes.jsonl ├── .lock/ # Multi-machine coordination (created at runtime) ├── README.md # This file ├── SETUP.md # Setup instructions └── AGENTS.md # Technical documentation ``` ## Platform Support Matrix | Platform | Status | Notes | |-----------|--------|-------| | Linux (Intel CPU) | ✅ Supported | Software encoding, multi-worker capable | | Windows (AMD GPU) | ✅ Supported | Hardware acceleration via d3d11va (auto-detects) | | Windows (Intel CPU) | ✅ Supported | Software encoding | | macOS (Apple Silicon) | ✅ Supported | Hardware via videotoolbox (auto-detects) | | WSL (Ubuntu/Debian) | ✅ Supported | Linux compatibility layer | | WSL (Windows drives) | ✅ Supported | Access via /mnt/c/ | ## Git Workflow ### Initial Setup ```bash cd /opt/Optmiser git init git remote add origin https://gitea.theflagroup.com/bnair/VMAFOptimiser.git git branch -M main git add . git commit -m "Initial commit: VMAF optimisation pipeline" git push -u origin main ``` ### Daily Updates ```bash cd /opt/Optmiser git pull origin main # Run optimisation ./run_optimisation.sh /media tv_movies # Review changes git diff ``` ### Committing Changes ```bash cd /opt/Optmiser git status # Add changed files git add optimize_library.py run_optimisation.sh run_optimisation.ps1 # Commit with message git commit -m "feat: add Windows and Linux wrapper scripts" # Push git push ``` ### View History ```bash cd /opt/Optmiser git log --oneline git log --graph --all ``` ## FAQ **Q: Can I run this on multiple machines at once?** A: Yes! Each machine will process different files due to lock file mechanism. **Q: Should I use Windows or WSL?** A: WSL is recommended for Linux compatibility. Use Windows native if you need direct hardware access or performance. **Q: Will hardware encoding work better than CPU?** A: For AMD RX 7900 XT, hardware AV1 encoding is ~3-10x faster than CPU. However, GPU encoding may need slightly lower VMAF targets to match quality. **Q: What VMAF target should I use?** A: Start with VMAF 94 or 95. Drop to 92-90 if you need more savings. **Q: How do I know which files are being processed?** A: Check `.lock/` directory: `ls -la /opt/Optmiser/.lock/` **Q: Can I pause/resume?** A: Pause by stopping the script (Ctrl+C). Resume by running again - it skips processed files. **Q: What happens if encoding fails?** A: Error is logged to `failed_encodes.jsonl`. Original file is NOT modified. **Q: How much CPU does encoding use?** A: Full CPU by default. Use `--workers 1` for single-threaded, or limit with `--cpu-limit 50` for 50% (12 threads on 24-core). --- **Last Updated:** December 31, 2025 **Version:** 2.0 with Windows and Linux Wrapper Scripts >>>>>>> 91418fa898de4a73e144a9fb9202b2315e922ab9