Skip to content

Subtitle formats overview

current

Subtitle formats fall into three families: plain or lightly styled text (SRT, WebVTT), fully styled script text (ASS/SSA), and images (PGS from Blu-ray, VobSub from DVD). Which one you want depends on where the video will be played, and converting between them usually loses something.

FormatWhat it isUse it forCarried in
SRTTimed text with <i>, <b>, <u>, <font> tagsAnything that just needs dialogue; the lowest common denominator.srt, MKV (S_TEXT/UTF8), MP4 as mov_text
ASS/SSAStyled script: fonts, colours, positioning, animationAnime fansubs, typeset signs and songs, karaoke.ass, MKV (S_TEXT/ASS)
WebVTTSRT-like text with cue positioning and CSS stylingWeb video (<track>), HLS.vtt, WebM, MKV
PGS (.sup)Blu-ray bitmapsKeeping Blu-ray subtitles exactly as authoredMKV (S_HDMV/PGS), M2TS
VobSub (.idx + .sub)DVD bitmapsKeeping DVD subtitlesMKV (S_VOBSUB), VOB
TTML / IMSCXML timed textBroadcast and streaming-service deliverablesSeparate files, DASH/CMAF segments
CEA-608/708, DVBBroadcast caption systemsTV broadcast and recordingsEmbedded in video or MPEG-TS

Text formats stay small, searchable, and resizable by the player. Image formats look exactly like the original but cannot be edited, restyled, or searched without OCR.

ContainerText subtitlesImage subtitles
MatroskaSRT, ASS/SSA, WebVTTPGS, VobSub
MP4mov_text only (SRT-level styling)Not portable
WebMWebVTTNone

If a player or device cannot show your subtitle format, the fallback is burning it in (see below).

Terminal window
ffmpeg -i subs.srt subs.ass
ffmpeg -i subs.srt subs.vtt
ffmpeg -i subs.vtt subs.srt
ffmpeg -i subs.ass subs.srt
ffmpeg -i input.mkv -map 0:s:0 subs.srt

Going from a richer format to a poorer one loses information, and FFmpeg does not clean up after itself. ASS to SRT turns each style into <font size="…" color="…"> wrappers (with the ASS script’s sizes, meaningless to an SRT player), drops \pos and leaves tags like {\an8} in the text; ASS to WebVTT keeps italics and bold but drops styles and positioning. Always check the result, and keep the original. Converting image subtitles to text needs OCR (Subtitle Edit, for example); FFmpeg cannot do it.

Terminal window
ffmpeg -i video.mkv -i subs.srt -map 0 -map 1 -c copy -metadata:s:s:0 language=eng output.mkv
mkvmerge -o output.mkv video.mkv --language 0:eng --track-name 0:"English" subs.srt
ffmpeg -i video.mkv -i subs.srt -map 0 -map 1 -c copy -c:s mov_text output.mp4

The --language 0:eng option applies to the file that follows it (here subs.srt, track 0). To keep fonts for ASS subtitles, attach them to the MKV; see SubStation Alpha.

Hard-coding the subtitles re-encodes the video, so it is a last resort for devices that cannot render soft subtitles:

Terminal window
ffmpeg -i input.mkv -vf subtitles=input.mkv -c:v libx264 -crf 18 -c:a copy output.mp4
ffmpeg -i input.mkv -vf "subtitles=subs.srt:force_style='FontSize=24,Outline=2'" -c:v libx264 -crf 18 -c:a copy output.mp4
ffmpeg -i input.mkv -vf ass=subs.ass -c:v libx264 -crf 18 -c:a copy output.mp4

subtitles takes any text format FFmpeg can read and picks the first subtitle stream of the file (:si=1 for the second); when the file is an MKV it also loads the attached fonts. ass only reads .ass files. Both need an FFmpeg built with libass.

Image subtitles (PGS, VobSub) are burned in with the overlay filter instead:

Terminal window
ffmpeg -i input.mkv -filter_complex "[0:v][0:s:0]overlay=shortest=1" -c:v libx264 -crf 18 -c:a copy output.mp4

shortest=1 ends the output with the video. Without it, the last subtitle event (which has no end time) can stretch the output far past the end of the video, and an MP4 mux fails. If you are also downscaling, put the scale after overlay so the subtitles shrink with the picture.

SRT has no style of its own, so FFmpeg converts it with a default ASS style. Override it with force_style using ASS style names (FontName, FontSize, Outline, MarginV). FontSize is measured against a 288-line script that libass scales to the video, so it is not a pixel size: 24 is a font size of 1/12 of the picture height at any resolution.

Old SRT files are often in a legacy codepage rather than UTF-8, which shows up as garbled accents. Tell FFmpeg what the file is with -sub_charenc CP1252 -i subs.srt (or charenc= on the filter), or convert with iconv -f CP1252 -t UTF-8.