Skip to content

Instantly share code, notes, and snippets.

@kurlov
Created December 16, 2017 23:41
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save kurlov/32cbe841ea9d2b299e15297e54ae8971 to your computer and use it in GitHub Desktop.
Save kurlov/32cbe841ea9d2b299e15297e54ae8971 to your computer and use it in GitHub Desktop.
ffmpeg command to add .srt based subtitles to an .mkv file
ffmpeg -i in.mkv -f srt -i in.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s srt out.mkv
Copy link

ghost commented Jan 8, 2022

tl;dr

ffmpeg -y -loglevel "repeat+info" 
-i "file:input.mp4" 
-i "file:subtitle.iw.vtt" 
-i "file:subtitle.en.vtt" 
-c copy 
-map 0 -dn -map "-0:s" -map "-0:d"  
-map "1:0" "-metadata:s:s:0" "language=heb" "-metadata:s:s:0" "handler_name=Hebrew"  "-metadata:s:s:0" "title=Hebrew" 
-map "2:0" "-metadata:s:s:1" "language=eng" "-metadata:s:s:1" "handler_name=English" "-metadata:s:s:1" "title=English" 
"file:output.mkv"
why?. the following example embed two subtitles without encoding, Hebrew and English (in a "one go", and in that order).

if you want to embed more than one subtitle stream you must use map,
otherwise ffmpeg's automatic stream selection only takes the first one, of its kind (it does the same for audio/video streams as well).

note: the command is broken over several lines for better readability. linux users add \ at line's end, windows users add ^ at line's end, or just remove all line breaks to make it functional.

note: vtt is like srt, commonly used by youtube. you can use any of your existing subtitles 'as is' though.

  • input 1: input.mp4 (the map command maps 0 which means this file and whatever streams it contains first, using automatic assignment, it then runs a little cleanup to make sure no existing subtitles were left in it, so the next indexing addressing to the external subtitles is correct .
  • input 2: subtitle.iw.vtt this is the first of the subtitle streams, its index is zero based so .....s:0, it is getting a proper 3 letter language code from ISO-639-1, and a label (you can write anything you like in it, even name it locally, I could have used title=עברית as well. but for compatibility with some old players I've chosen title=Hebrew.
  • input 3: subtitle.en.vtt second in the subtitle-list of streams, so index is 1 - .....s:1.
  • output: output.mkv.
note about indexing: I know the indexing might seem a bit confusing, first, remember it is zero based, so 0 is first, 1 is second, 2 is third, ...

next:

-map 0, -map "1:0", -map "2:0" are the original input (-i ...) files 'as whole', with whatever streams they have in them,

the indexes in "-metadata:s:s:0", "-metadata:s:s:1" are limited with :s so that means they are used in the list of subtitles "collected" from all the inputs together, potentially including all the subtitles already embedded in the video file.

to avoid collecting any unknown amount of subtitles from the first input, -map "-0:s" is used, ignoring all subtitle-streams from the first input.

b.t.w. this how YT-DLP (youtube-dl fork) is embedding subtitles, it fixes a lot of quirks.

yt-dlp's subtitle embedding related, python code, for ffmpeg

https://github.com/yt-dlp/yt-dlp/blob/1209b6ca5b720a2cd035ff86040bfb1fea7ac6c9/yt_dlp/postprocessor/ffmpeg.py#L591-L667

https://github.com/yt-dlp/yt-dlp/blob/1209b6ca5b720a2cd035ff86040bfb1fea7ac6c9/yt_dlp/postprocessor/ffmpeg.py#L648-L659

if you already have subtitles you want to keep you can extract them with -na -nv mysubtitle.srt (you can choose your extension) and embed them properly again (with language code and such) along with the new ones. you can also avoid the deletion of data and existing subtitles, but then you need to remember to shift the index of the added subtitles acording to how many subtitles are already embedded, and it is bad scripting. it is better to extract them in what ever format you want. if you are working on some subtitles with more data, such as colors, or placements over video (.ASS) which are commonly used in translated anime, to translate letters street signs etc.. make sure to keep the same format otherwise you'll loose that data and only keep the text of the translation. you can often check what subtitle type is being used with ffmpeg or vlc or media player classic.

ffmpeg_embed_vtt_subtitles_by_muxing_without_encoding

and you can always use -c:s copy subtitle_of_unknown_format.txt instead and figure it out later using a text editor or the great subtitle workshop program.

source


another example with more languages:

ffmpeg -y -loglevel "repeat+info" 
-i "file:my_input_video.mkv" 
-i "file:subtitle.en.vtt" 
-i "file:subtitle.iw.vtt" 
-i "file:subtitle.ja.vtt" 
-i "file:subtitle.ko.vtt" 
-map 0 
-c copy 
-dn -ignore_unknown -map "-0:s" 
-map "1:0" "-metadata:s:s:0" "language=eng" "-metadata:s:s:0" "handler_name=English"  "-metadata:s:s:0" "title=English" 
-map "2:0" "-metadata:s:s:1" "language=heb" "-metadata:s:s:1" "handler_name=Hebrew"   "-metadata:s:s:1" "title=Hebrew" 
-map "3:0" "-metadata:s:s:2" "language=jpn" "-metadata:s:s:2" "handler_name=Japanese" "-metadata:s:s:2" "title=Japanese" 
-map "4:0" "-metadata:s:s:3" "language=kor" "-metadata:s:s:3" "handler_name=Korean"   "-metadata:s:s:3" "title=Korean" 
-movflags "+faststart" 
"file:my_output_video.mkv"

edit:

default subtitle to play

(optional) use -disposition to set a default subtitle,
and it does not even have to be the first one,
for example: -disposition:s:s:5 forced will make your sixth subtitle stream the default one to play.

note: you can use this to make other streams the default ones to open as well...

-disposition:s:a:3 forced for the fourth audio stream,

-disposition:s:v:0 forced for the first video stream..

@akostadinov
Copy link

akostadinov commented Feb 17, 2022

To just add subtitles to file without removing any subtitles it is very easy:

ffmpeg -i input.mkv -i input.srt -map 0 -map 1 -c copy output.mkv

-map x selects all streams from a file so all streams from both files get into the output file.

See the very conscious and simple documentation https://trac.ffmpeg.org/wiki/Map

P.S. Thanks @eladkarako for the metadata usage

@alexg0240
Copy link

I've tried all the above options without success... the only thing that happens is that there is a closed caption option listed yet no text is displayed and the file size of the video file was increased.

I'm using both mediainfo and vlc to look at the file info.

I'm running this thru a ubuntu server 22.04.1 LTS

is this supposed to add the CEA-708 metadata for Closed Captioning or is this a different type of embedded text?

@SalimF
Copy link

SalimF commented Sep 23, 2022

I've tried all the above options without success..

This code I use both on my PC and my server, works perfectly, the sub file should be .ass format (use aegisub or even ffmpeg to convert your sub to it)

ffmpeg -loglevel 16 -i input.mp4 -f ass -i sub.ass -brand isom -tag:v avc1 -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s ass -disposition:s:0 default Fname.mkv

@nxtreaming
Copy link

ffmpeg -i $1 -f ass -i $2 -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s ass $RANDOM.mkv

I confirm this code is fine.

Thanks.

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