DEV Community

Javid Jamae
Javid Jamae

Posted on • Originally published at ffmpeg-micro.com

FFmpeg Subtitles Filter: How to Burn SRT Subtitles Into Video

Originally published at ffmpeg-micro.com

FFmpeg's subtitles filter burns SRT or ASS captions directly into the video pixels. Unlike soft subtitles (which viewers can toggle off), burned-in subtitles are permanent. They show up everywhere: social media players that strip subtitle tracks, messaging apps, and platforms that don't support external caption files.

The basic command is one line, but the filter's syntax for styling, positioning, and encoding has a few traps that waste hours if you don't know about them.

The Basic Command

ffmpeg -i input.mp4 -vf "subtitles=captions.srt" -c:v libx264 -crf 23 -c:a copy output.mp4
Enter fullscreen mode Exit fullscreen mode

FFmpeg reads captions.srt, renders each subtitle at its timestamp, and encodes the result into a new MP4. The -c:a copy flag passes audio through untouched so you don't re-encode it for no reason.

Two things to know up front:

  • The subtitles filter requires FFmpeg compiled with libass. If you get No such filter: 'subtitles', your build doesn't have it. Most package managers include it by default (brew install ffmpeg on macOS, apt install ffmpeg on Ubuntu), but minimal Docker images often skip it.
  • The filter reads the SRT file at filter-graph init time. If the file path has spaces or colons, you need to escape them (more on that below).

Styling with force_style

The default appearance (white text, thin black outline) works, but you'll almost always want to customize it. The force_style parameter accepts ASS override tags:

ffmpeg -i input.mp4 \
  -vf "subtitles=captions.srt:force_style='FontSize=28,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1'" \
  -c:v libx264 -crf 23 -c:a copy output.mp4
Enter fullscreen mode Exit fullscreen mode

force_style Parameter Reference

Parameter What it does Example value
FontSize Text size in pixels 28
FontName Font family Arial
PrimaryColour Text fill color (ASS hex: &HAABBGGRR) &H00FFFFFF (white)
OutlineColour Outline color &H00000000 (black)
BackColour Shadow/background color &H80000000 (50% black)
Outline Outline thickness in pixels 2
Shadow Shadow distance in pixels 1
BorderStyle 1 = outline + shadow, 4 = opaque box 4
Alignment Position on screen (numpad layout) 2 (bottom center)
MarginV Vertical margin from the edge 30
Bold Bold text (1 = on, 0 = off) 1

The color format is tricky. ASS uses &HAABBGGRR, not the #RRGGBB you're used to from CSS. White is &H00FFFFFF. Yellow is &H0000FFFF. Red is &H000000FF. The AA prefix is transparency (00 = fully opaque, FF = fully transparent).

Character Encoding and Escaping

Three escaping rules that catch people off guard:

Colons in file paths. On Windows, paths like C:\Users\me\captions.srt break the filter because : is a parameter separator. Escape them with backslashes.

Single quotes inside force_style. The force_style value is wrapped in single quotes. If your font name contains an apostrophe, escape it with a backslash.

UTF-8 encoding. The SRT file must be UTF-8. If you get garbled characters or blank subtitles, check with file captions.srt. Convert if needed:

iconv -f UTF-16 -t UTF-8 captions-utf16.srt > captions.srt
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls

"No such filter: subtitles" means your FFmpeg build doesn't include libass. Reinstall with subtitle support: brew install ffmpeg (macOS), sudo apt install ffmpeg libass-dev (Ubuntu).

Timing is off after trimming. If you use -ss (seek) to trim the video, subtitle timestamps don't shift automatically. Put -ss before -i as an input option.

Output file is bigger than the input. Burning subtitles forces a full re-encode. Use -crf 23 or lower for quality parity. Don't use -c:v copy because the filter can't modify copied frames.

Text renders at wrong size after scaling. Put subtitles AFTER the scale filter: -vf "scale=1280:720,subtitles=captions.srt". Chain order matters.

FAQ

Does the subtitles filter work with WebM and MKV output?

Yes. The filter burns text into video frames, so it works with any output container.

Can I use the subtitles filter with a URL instead of a local file?

No. The subtitles filter only reads local files. Download your SRT first with curl or wget before passing it to the filter.

How do I burn subtitles without re-encoding?

You can't. The filter modifies video frames, which requires a full decode-and-encode cycle. To minimize quality loss, match the original codec and use a CRF value equal to or lower than the source.

Read the full guide with more examples on ffmpeg-micro.com.

Top comments (0)