Automation Pipeline3 min read

ffmpeg gotchas that cost me hours

Four ffmpeg and toolchain traps from building a video pipeline: overlay has no alpha, drawtext breaks on non-ASCII font paths, a silently broken system binary, and why a static ffmpeg wins on a recent Python.

#ffmpeg#python#video#debugging#automation
Placeholder visual for ffmpeg gotchas
Placeholder — real capture pending.

None of these are in the error message you'd hope for. Each one looked like my bug until I found the ffmpeg-shaped edge underneath.

1. overlay has no alpha= option

I wanted a layer to fade in over the background. The obvious guess — an alpha on the overlay filter — doesn't exist. overlay composites; it doesn't own the opacity of its input.

The fix is to fade the source before you overlay it, using fade with the alpha flag:

# WRONG: overlay has no alpha=
# [bg][fg]overlay=alpha=...
 
# RIGHT: fade the source's alpha, then overlay
[fg]format=yuva420p,fade=t=in:st=0:d=0.4:alpha=1[fgf];
[bg][fgf]overlay

I lost an hour assuming a symmetry the filter graph doesn't have.

2. drawtext chokes on non-ASCII / spaced font paths

For multilingual captions I pointed drawtext at the CJK system fonts. It failed to render — the font paths have spaces and non-ASCII characters, and drawtext's fontfile= parsing can't handle them.

Fix: copy the font to a plain ASCII-named file and point drawtext at that copy.

cp "/System/.../Hiragino Sans W4.ttc" ./fonts/hiragino_w4.ttc
# then: drawtext=fontfile=./fonts/hiragino_w4.ttc:...

After that, Japanese and Korean render cleanly instead of as tofu boxes.

3. The system ffmpeg was silently broken

Renders were failing in a way that pointed nowhere useful. The cause turned out to be a shared-library soname drift — the system's ffmpeg had been left linking against a library version that no longer matched. Nothing in my pipeline was wrong; the binary itself was.

4. On a recent Python, bet on a static ffmpeg

The reflex fix for #3 is "reinstall ffmpeg," but the usual static-build mirror was DNS-blocked on my network, and on a very recent Python (3.14) the media-related wheels don't always have prebuilt binaries — so pip would try to compile and fail. I sidestepped all of it by pulling a known-good static binary through the imageio-ffmpeg package, and pointing the pipeline at that.

import imageio_ffmpeg
FFMPEG = imageio_ffmpeg.get_ffmpeg_exe()  # a pinned static binary

The honest part

Three of these four looked like application bugs and were actually environment or filter-graph facts. The lesson I keep relearning with ffmpeg: when a render misbehaves, separate your graph from the binary and its inputs before you debug the graph. Pin a static binary, normalize font paths to ASCII, and read the filter docs for the specific option you're assuming exists — because half the time it doesn't.

Related