Libvpx: Ghosts S01e11

Welcome to Ghosts S01E11: Libvpx . Let’s exorcise it. It started with a routine archival job. We were transcoding a film scan (ProRes 4444 → WebM) for a client’s interactive museum installation. The command was standard:

Pause the video. Look at frame 1042. Fine. Advance one frame. A faint, semi-transparent outline of frame 1042 remains superimposed over frame 1043. Another frame forward? The ghost fades. But it shouldn't be there at all. ghosts s01e11 libvpx

The blocks don’t look like corruption. They look like... memories. Shadows of a previous frame refusing to leave. You’ve just encoded a 10-bit source with Libvpx (VP9), and somehow, the ghost of frame 1,042 is haunting frame 1,043. Welcome to Ghosts S01E11: Libvpx

ffmpeg -i input.mov -c:v libvpx-vp9 -crf 18 -b:v 0 -f webm - | md5sum # Run again. Compare hashes. Force Libvpx to use a simpler reference structure. This is the nuclear option that usually fixes the ghost, but hurts compression efficiency. We were transcoding a film scan (ProRes 4444

# Check your version ffmpeg -version | grep libvpx # If it's 1.11 or 1.12, upgrade. If you can’t upgrade (e.g., in a production container), tune around the bug by reducing the temporal dependency depth.

ffmpeg -i input.mov -c:v libvpx-vp9 \ -tile-columns 2 -row-mt 1 \ -lag-in-frames 16 \ # Reduce from default 25 -auto-alt-ref 1 \ # Keep on, but be careful -arnr-maxframes 3 \ # Reduce temporal filtering -cpu-used 2 \ output.webm Two-pass encoding often masks the bug because the first pass forces the encoder to re-evaluate scene boundaries more strictly.

ffmpeg -i master.mov -c:v libvpx-vp9 -pix_fmt yuv420p10le -crf 18 -b:v 0 output.webm The first pass looked incredible. Grain was preserved. Banding was minimal. But during playback on a high-refresh-rate display, we noticed it: .