Speed Up FFmpeg VMAF with Multi-Threading

Computing Video Multi-Method Assessment Fusion (VMAF) scores can be computationally intensive, often creating bottlenecks in automated video processing pipelines. By default, the libvmaf filter in FFmpeg may not utilize all available CPU resources efficiently. This guide explains how to enable multi-threading for the VMAF calculation itself, optimize decoder thread allocation, and structure your FFmpeg commands to maximize processing throughput.

Enabling the n_threads Option in libvmaf

The primary method to enable multi-threading during VMAF calculation is passing the n_threads parameter directly to the libvmaf filter inside the -filter_complex argument.

Here is a standard multi-threaded command:

ffmpeg -i distorted.mp4 -i reference.mp4 -filter_complex \
"[0:v][1:v]libvmaf=n_threads=8:log_fmt=json:log_path=vmaf.json" \
-f null -
  • n_threads: Sets the number of execution threads allocated strictly to the VMAF algorithm.
  • log_fmt and log_path: Specify the output format and file destination for the resulting metric data.
  • -f null -: Instructs FFmpeg to discard the video output rather than encoding it to disk, saving I/O overhead.

Optimizing Input Decoder Threads

Calculating VMAF requires decoding two video streams simultaneously: the reference video and the distorted video. If the decoders cannot supply frames fast enough, the VMAF filter will stall regardless of how many threads are assigned to n_threads.

Explicitly allocate decoding threads prior to each input to prevent decoder starvation:

ffmpeg -threads 4 -i distorted.mp4 -threads 4 -i reference.mp4 -filter_complex \
"[0:v][1:v]libvmaf=n_threads=8:model=version=vmaf_v0.6.1:log_fmt=json:log_path=vmaf.json" \
-f null -

In this setup, each input utilizes 4 decoding threads, while libvmaf utilizes 8 threads to process the comparative scoring.

Optimal Thread Allocation Strategy

To find the most efficient thread allocation:

  1. Avoid Oversubscription: The total number of threads (Input 1 Decoder + Input 2 Decoder + n_threads) should not exceed your machine's total logical CPU cores. For an 8-core/16-thread CPU, a balanced configuration is -threads 4 (Input 1), -threads 4 (Input 2), and n_threads=8.
  2. Diminishing Returns: libvmaf scaling generally plateaus between 8 and 16 threads depending on the video resolution. Setting n_threads higher than 16 rarely provides proportional performance gains.

Performance Optimization Checklist

  • Pre-align Formats and Resolutions: If the reference and distorted files have different pixel formats or resolutions, FFmpeg automatically inserts scaling and conversion filters that might run on a single thread. Ensure both inputs are normalized to matching pixel formats (such as yuv420p) and identical resolutions beforehand.
  • Use Fast Frame Seeking for Sampling: If you only need an estimate across a video, use the -ss and -t flags to sample representative segments rather than analyzing the entire file.
  • Hardware Decoding: If CPU decoding forms a bottleneck, utilize hardware-accelerated decoders (e.g., -hwaccel cuda or -hwaccel qsv) to offload decoding to the GPU, reserving 100% of the host CPU cores exclusively for the n_threads calculation.