How to Add Subtitles to Video with FFmpeg
Complete guide to adding subtitles with FFmpeg — hardcode SRT/ASS, softcode subtitle tracks, style customization, multi-language support, and subtitle extraction.

Subtitles make videos accessible, searchable, and watchable in noisy environments. FFmpeg can hardcode subtitles directly into the video, embed them as selectable tracks, extract existing subtitles, and convert between subtitle formats. This guide covers all of it with practical commands.
Hardcode vs Softcode: Which to Choose?
Before adding subtitles, you need to decide how:
| Method | Description | Pros | Cons |
|---|---|---|---|
| Hardcode (burn-in) | Subtitles rendered into the video pixels | Always visible, universal playback | Can't turn off, requires re-encoding |
| Softcode (embedded) | Subtitles stored as a separate track | Toggleable, multiple languages | Player must support it |
Rule of thumb: Hardcode for social media and maximum compatibility. Softcode for long-form content and multi-language support. If you're building a UGC platform that processes user-uploaded videos, softcode is usually the better choice.
Hardcode SRT Subtitles
SRT is the most common subtitle format. To burn it into the video:
# Hardcode SRT subtitles
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" output.mp4
That's it. FFmpeg uses the subtitles filter (powered by libass) to render the SRT file directly onto each frame.
Customize SRT Appearance
The default SRT rendering is plain. You can style it with the force_style option:
# Custom font, size, and color
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontName=Arial,FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1'" output.mp4
Common style parameters:
| Parameter | Example | Description |
|---|---|---|
| FontName | Arial | Font face |
| FontSize | 24 | Size in points |
| PrimaryColour | &H00FFFFFF | Text color (ABGR hex) |
| OutlineColour | &H00000000 | Outline color |
| BackColour | &H80000000 | Shadow/background color |
| Outline | 2 | Outline thickness |
| Shadow | 1 | Shadow depth |
| Bold | 1 | Bold text (0 or 1) |
| Alignment | 2 | Position (2=bottom center) |
| MarginV | 30 | Vertical margin from edge |
Note: Colors use ABGR format (Alpha, Blue, Green, Red), not the usual RGB.
# Yellow text with black outline, larger font, positioned higher
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=28,PrimaryColour=&H0000FFFF,OutlineColour=&H00000000,Outline=3,MarginV=40'" output.mp4
Handle Special Characters in File Paths
If your subtitle file path contains special characters, escape them:
# Escape colons in Windows paths or special characters
ffmpeg -i input.mp4 -vf "subtitles='my\ subtitles.srt'" output.mp4
# Or use the subtitle file index approach
ffmpeg -i input.mp4 -i subs.srt -filter_complex "[0:v][1:s]overlay" output.mp4
Hardcode ASS/SSA Styled Subtitles
ASS (Advanced SubStation Alpha) subtitles support rich styling — fonts, colors, animations, and positioning per line. When hardcoding ASS files, all styling is preserved:
# Hardcode ASS subtitles (all styling preserved)
ffmpeg -i input.mp4 -vf "ass=subs.ass" output.mp4
Create a Simple ASS File
Here's an example ASS file structure:
[Script Info]
Title: My Subtitles
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,48,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,2,1,2,10,10,40,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,Hello, this is a subtitle.
Dialogue: 0,0:00:05.00,0:00:08.00,Default,,0,0,0,,This line has {\b1}bold{\b0} text.
Convert SRT to ASS with Custom Styling
# Convert SRT to ASS (then you can edit the ASS styles)
ffmpeg -i subs.srt subs.ass
After converting, edit the [V4+ Styles] section in the ASS file to customize appearance. You may also want to convert your video to a different format before adding subtitles, depending on your target platform.
Softcode Subtitles (Embedded Tracks)
Softcoded subtitles are stored as a separate stream inside the container. The viewer can toggle them on/off.
Add SRT as a Subtitle Track
# Add SRT subtitle track to MP4
ffmpeg -i input.mp4 -i subs.srt -c copy -c:s mov_text output.mp4
# Add SRT subtitle track to MKV (MKV supports SRT natively)
ffmpeg -i input.mp4 -i subs.srt -c copy -c:s srt output.mkv
Important: MP4 containers require mov_text codec for subtitles. MKV supports SRT, ASS, and many other formats natively.
Add Multiple Subtitle Languages
# Add English and Spanish subtitle tracks to MKV
ffmpeg -i input.mp4 -i english.srt -i spanish.srt \
-map 0:v -map 0:a -map 1 -map 2 \
-c copy -c:s srt \
-metadata:s:s:0 language=eng -metadata:s:s:0 title="English" \
-metadata:s:s:1 language=spa -metadata:s:s:1 title="Spanish" \
output.mkv
For MP4:
# Add multiple subtitle tracks to MP4
ffmpeg -i input.mp4 -i english.srt -i chinese.srt \
-map 0:v -map 0:a -map 1 -map 2 \
-c copy -c:s mov_text \
-metadata:s:s:0 language=eng -metadata:s:s:0 title="English" \
-metadata:s:s:1 language=chi -metadata:s:s:1 title="Chinese" \
output.mp4
Set Default Subtitle Track
# Mark the first subtitle track as default
ffmpeg -i input.mp4 -i subs.srt \
-map 0 -map 1 \
-c copy -c:s mov_text \
-disposition:s:0 default \
output.mp4
Add ASS Subtitles as Soft Track
# Add ASS to MKV (preserves all styling for compatible players)
ffmpeg -i input.mp4 -i subs.ass -map 0 -map 1 -c copy -c:s ass output.mkv
Extract Subtitles from Video
Extract to SRT
# Extract the first subtitle track to SRT
ffmpeg -i input.mkv -map 0:s:0 output.srt
# Extract the second subtitle track
ffmpeg -i input.mkv -map 0:s:1 output.srt
Extract to ASS
# Extract subtitle track as ASS (preserves styling)
ffmpeg -i input.mkv -map 0:s:0 output.ass
List Available Subtitle Tracks
# Show all streams including subtitles
ffprobe -v error -show_entries stream=index,codec_name,codec_type -of csv input.mkv
# More detailed subtitle info
ffprobe -v error -select_streams s -show_entries stream=index,codec_name:stream_tags=language,title -of json input.mkv
Extract All Subtitle Tracks
#!/bin/bash
# Extract all subtitle tracks from a video
INPUT="input.mkv"
# Count subtitle streams
COUNT=$(ffprobe -v error -select_streams s -show_entries stream=index -of csv=p=0 "$INPUT" | wc -l)
for i in $(seq 0 $((COUNT - 1))); do
# Get language tag
LANG=$(ffprobe -v error -select_streams s:$i -show_entries stream_tags=language -of csv=p=0 "$INPUT")
LANG=${LANG:-"track$i"}
ffmpeg -i "$INPUT" -map 0:s:$i "${INPUT%.*}_${LANG}.srt"
done
Convert Subtitle Formats
FFmpeg can convert between subtitle formats:
# SRT to ASS
ffmpeg -i subs.srt subs.ass
# ASS to SRT (loses styling)
ffmpeg -i subs.ass subs.srt
# SRT to WebVTT
ffmpeg -i subs.srt subs.vtt
# SUB/IDX (DVD) to SRT (requires OCR — FFmpeg can't do this directly)
# Use a tool like SubtitleEdit or Tesseract for bitmap-to-text conversion
| From | To | Preserves Styling? |
|---|---|---|
| SRT → ASS | Yes | Adds default styling |
| ASS → SRT | No | Loses all styling |
| SRT → VTT | Partial | Basic formatting kept |
| VTT → SRT | Partial | Loses VTT-specific features |
Subtitle Timing Adjustments
Delay or Advance Subtitles
# Delay subtitles by 2.5 seconds
ffmpeg -i input.mp4 -itsoffset 2.5 -i subs.srt -map 0 -map 1 -c copy -c:s mov_text output.mp4
For hardcoded subtitles, use the setpts filter or adjust the SRT file directly (if you need to trim or merge video segments, timing adjustments become especially important):
# Shift hardcoded subtitles (using subtitle filter with offset)
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" -ss 00:00:02.500 output.mp4
Hardcode Subtitles with Resolution Control
When hardcoding, subtitle rendering quality depends on the video resolution. For best results, match the original resolution:
# Hardcode subtitles while keeping original resolution
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" -c:v libx264 -crf 23 -c:a copy output.mp4
# Hardcode and scale to 1080p
ffmpeg -i input.mp4 -vf "subtitles=subs.srt,scale=-2:1080" -c:v libx264 -crf 23 -c:a copy output.mp4
Subtitle Font Rendering for Different Resolutions
# For 720p video — smaller font
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=20'" -c:v libx264 -crf 23 output.mp4
# For 4K video — larger font
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=48,Outline=3'" -c:v libx264 -crf 23 output.mp4
Batch Subtitle Operations
Hardcode Subtitles for All Videos
# Assumes each video has a matching .srt file
for f in *.mp4; do
srt="${f%.mp4}.srt"
if [ -f "$srt" ]; then
ffmpeg -i "$f" -vf "subtitles=$srt" -c:v libx264 -crf 23 -c:a copy "subtitled_${f}"
fi
done
Softcode Subtitles for All Videos
# Add matching SRT files as subtitle tracks
for f in *.mp4; do
srt="${f%.mp4}.srt"
if [ -f "$srt" ]; then
ffmpeg -i "$f" -i "$srt" -c copy -c:s mov_text "soft_${f}"
fi
done
Troubleshooting
"No such filter: subtitles" — FFmpeg was compiled without libass support. Install a full build:
# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt install ffmpeg libass-dev
Subtitles don't appear — Check the subtitle file encoding. FFmpeg expects UTF-8:
# Convert subtitle encoding to UTF-8
iconv -f ISO-8859-1 -t UTF-8 subs_latin.srt > subs_utf8.srt
Wrong subtitle position or size — The video resolution doesn't match the subtitle's assumed resolution. Use force_style to override:
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=24,MarginV=30'" output.mp4
Special characters display as boxes — The font doesn't support the characters. Specify a font that supports your language:
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontName=Noto Sans CJK SC'" output.mp4
MP4 output fails with "unknown codec" for subtitles — MP4 only supports mov_text. Use MKV for other subtitle formats, or hardcode instead.
Cloud Alternative with FFHub
Hardcoding subtitles requires re-encoding, which is CPU-intensive — a 1-hour video can take 20+ minutes on a standard machine. FFHub runs FFmpeg in the cloud via API, so you can offload subtitle burning to fast cloud servers while your machine stays free.
This is especially useful when you need to generate multiple subtitle versions (different languages, different styles) from the same source video.
Summary
- Hardcode (
-vf "subtitles=subs.srt") for guaranteed visibility on all platforms - Softcode (
-c:s mov_textfor MP4,-c:s srtfor MKV) for toggleable tracks - Use
force_styleto customize SRT subtitle appearance - Use ASS format when you need rich styling and animations
- Use
-mapto add multiple subtitle languages - Use
ffprobeto inspect existing subtitle tracks before extraction - Always ensure subtitle files are UTF-8 encoded
Related Articles
- How to Trim and Merge Videos with FFmpeg - Cut, split, and concatenate video clips while keeping subtitle timing in sync
- How to Convert Video Format with FFmpeg - Switch between MP4, MKV, and WebM containers that each handle subtitles differently
- How to Extract Audio from Video with FFmpeg - Pull audio tracks from video for transcription and subtitle generation