With n_threads > 1, the CAMBI heatmaps written with heatmaps_path can lose their first pictures. In some runs, part or all of the first pictures of a heatmap file are zeros instead of the values a single-threaded run writes, so the output differs from run to run. The CAMBI scores are not affected.
Tested on Linux (FFmpeg 8.1 + libvmaf 3.2.1) and macOS (Homebrew FFmpeg 9.0.2 + libvmaf 3.2.1, which reports itself as 3.2.0).
Steps to reproduce
Two 1280x720 clips of 50 frames: a reference and a blurred copy.
ffmpeg -v error -y -f lavfi -i "testsrc2=s=1280x720:r=25:d=2" -pix_fmt yuv420p -c:v ffv1 ref.mkv
ffmpeg -v error -y -i ref.mkv -vf "gblur=sigma=1" -c:v ffv1 dist.mkv
Claude prepared the following script to check the bug quickly. It computes CAMBI with heatmaps 6 times with n_threads=1 and 6 times with n_threads=4. For each run, it prints how many bytes are not zero in each of the first five pictures of the scale 0 heatmap (p0 to p4). A lost picture is all zeros, so its count drops (to 0 if it is lost entirely).
frames=50
for threads in 1 4; do
for run in 1 2 3 4 5 6; do
out="heatmaps_t${threads}_r${run}"
rm -rf "$out"
ffmpeg -v error -nostdin -i dist.mkv -i ref.mkv \
-lavfi "[0:v][1:v]libvmaf=feature=name=cambi\\\\:heatmaps_path=${out}:n_threads=${threads}:log_fmt=json:log_path=${out}.json" \
-f null -
f=$(ls "$out"/cambi_heatmap_scale_0_*)
pic=$(( $(wc -c < "$f") / frames ))
line="n_threads=$threads run=$run"
for i in 0 1 2 3 4; do
nz=$(dd if="$f" bs="$pic" skip="$i" count=1 2>/dev/null | LC_ALL=C tr -d '\0' | wc -c | tr -d ' ')
line="$line p$i=$nz"
done
echo "$line"
done
done
Expected
Every run gives the same counts of non-zero bytes as the n_threads=1 runs.
Actual
Currently, the script prints:
n_threads=1 run=1 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=1 run=2 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=1 run=3 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=1 run=4 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=1 run=5 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=1 run=6 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=1 p0=0 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=2 p0=4392 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=3 p0=0 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=4 p0=4132 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=5 p0=303 p1=5260 p2=5447 p3=6197 p4=6009
n_threads=4 run=6 p0=4858 p1=5260 p2=5447 p3=6197 p4=6009
The 6 single-threaded runs are identical. With n_threads=4, picture 0 is entirely zero in runs 1 and 3 and partly zero in runs 2, 4 and 5. Pictures 1 to 4 match the single-threaded runs. Which runs are affected changes from one attempt to the next, since it depends on thread timing.
With
n_threads > 1, the CAMBI heatmaps written withheatmaps_pathcan lose their first pictures. In some runs, part or all of the first pictures of a heatmap file are zeros instead of the values a single-threaded run writes, so the output differs from run to run. The CAMBI scores are not affected.Tested on Linux (FFmpeg 8.1 + libvmaf 3.2.1) and macOS (Homebrew FFmpeg 9.0.2 + libvmaf 3.2.1, which reports itself as 3.2.0).
Steps to reproduce
Two 1280x720 clips of 50 frames: a reference and a blurred copy.
Claude prepared the following script to check the bug quickly. It computes CAMBI with heatmaps 6 times with
n_threads=1and 6 times withn_threads=4. For each run, it prints how many bytes are not zero in each of the first five pictures of the scale 0 heatmap (p0top4). A lost picture is all zeros, so its count drops (to 0 if it is lost entirely).Expected
Every run gives the same counts of non-zero bytes as the
n_threads=1runs.Actual
Currently, the script prints:
The 6 single-threaded runs are identical. With
n_threads=4, picture 0 is entirely zero in runs 1 and 3 and partly zero in runs 2, 4 and 5. Pictures 1 to 4 match the single-threaded runs. Which runs are affected changes from one attempt to the next, since it depends on thread timing.