Skip to content

Instantly share code, notes, and snippets.

@spirillen
Last active March 30, 2025 15:34
Show Gist options
  • Save spirillen/af307651c4261383a6d651038a82565d to your computer and use it in GitHub Desktop.
Save spirillen/af307651c4261383a6d651038a82565d to your computer and use it in GitHub Desktop.
Use FFmpeg to add subtitles to video

Use FFmpeg to add subtitles to video

MP4:

ffmpeg -i input.mp4 -f srt -i input.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy \
    -c:a copy -c:s mov_text output.mp4

MKV:

ffmpeg -i input.mp4 -f srt -i input.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy \
    -c:a copy -c:s srt output.mkv

To understand the -map option you'll first need to examine your source file by running the video trough ffprobe or ffmpeg

ffprobe video.mp4

OR

ffmpeg -i video.mp4

Then you'll see some lines named "Stream", each stream represent a video, sound and/or subtitle. Streams starting with 0: are video or sound tracks (streams) and streams starting with a number higher than 0 is subtitle (overlays)

To add several subtitles to the same video requires you to add a -c:s mov_text or -c:s srt for each subtitle submitted

If you like to add a title to each soundtrack or subtitle you can use the

-metadata:s: and -metadata:s:s

Example

For English soundtracks you'll use English for language

-metadata:s:0 language=English

For subtitle you have to use the extra s

-metadata:s:s:1 language=English \
-metadata:s:s:2 language=Dansk
@wonbinbk
Copy link

Streams starting with 0: are video or sound tracks (streams) and streams starting with a number higher than 0 is subtitle (overlays)

Is this correct? If so, does the order of inputs specified by the "-i" switch matter?

@spirillen
Copy link
Author

This is all correct

and yes the order of the input sources matters, meaning it is first the video and audio streams, then the subtitle streams

@Neurognostic
Copy link

Thanks for sharing the example!

This is what I got to work with several languages:

ffmpeg -i ./"$name".mp4 \
	-f srt -i ./"$name".en.srt \
	-f srt -i ./"$name".es.srt \
	-f srt -i ./"$name".fr.srt \
	-f srt -i ./"$name".sv.srt \
	-f srt -i ./"$name".fi.srt \
	-f srt -i ./"$name".cr.srt \
	-f srt -i ./"$name".ar.srt \
	-map 0:0 -map 0:1 -map 1:0 \
	-map 2:0 -map 3:0 -map 4:0 \
	-map 5:0 -map 6:0 -map 7:0 \
	-c:v copy -c:a copy -c:s mov_text \
	-metadata:s:s:0 language=eng \
	-metadata:s:s:1 language=spa \
	-metadata:s:s:2 language=fra \
	-metadata:s:s:3 language=swe \
	-metadata:s:s:4 language=fin \
	-metadata:s:s:5 language=cre \
	-metadata:s:s:6 language=ara \
	-metadata title="$title" \
	"$title".subs.mp4

I used the ISO 639-2 three character language code for the subtitle language metadata which seems to be recognized by both mediainfo and vlc as their locale names.

I had a problem with one of my subtitle files being encoded with WINDOWS-1252 rather than the expected UTF-8. It would produce an error like this when encoding:

[srt @ 0x559e9f111dc0] Invalid UTF-8 in decoded subtitles text; maybe missing -sub_charenc option
Error while decoding stream #0:8: Invalid data found when processing input

To fix it you can detect the encoding with chardet or uchardet and then use iconv to convert to the expected UTF-8:

iconv --verbose -f WINDOWS-1252 -t UTF-8 -o ./"$name".fixed.en.srt ./"$name".en.srt

@salivity
Copy link

I created an online tool which used this method to add subtitles to a video container, https://davidclews.com/article/280.html you can see the code in the browsers console

@spirillen
Copy link
Author

I created an online tool which used this method to add subtitles to a video container, https://davidclews.com/article/280.html you can see the code in the browsers console

You should mention the rather small limitation, but good initiative.

This tool has a max filesize limit of 2.00 GiB or 2.15 GB

@GerZah
Copy link

GerZah commented Jan 17, 2025

Thank you for this comprehensive documentation, much obliged!

… You've got a typo: It shouldn't say ffmprobe -i video.mp4 but ffprobe -i video.mp4.

@spirillen
Copy link
Author

Thanks @GerZah it's fixed, and glad you like my tiny guide on the topic,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment