Skip to content

Instantly share code, notes, and snippets.

@kurlov
Created December 16, 2017 23:41
Show Gist options
  • 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
@abbasnaqdi
Copy link

Compatible with utf8 :
ffmpeg -i input.mkv -sub_charenc 'UTF-8' -f srt -i input.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s srt out.mkv

@RobertoMaurizzi
Copy link

With the original options for map, I got subs out of sync by around 4 secs. Removing -map completely copies over all the streams and works for most files

ffmpeg -i input.mkv -sub_charenc 'UTF-8' -f srt -i subs.en.srt -map 0 -c:v copy -c:a copy -c:s srt output.mkv

@ttyusupov
Copy link

-map 1:0 is still needed for copying subtitles into output file:

ffmpeg -i input.mkv -sub_charenc 'UTF-8' -f srt -i subs.en.srt -map 0 -map 1:0 -c:v copy -c:a copy -c:s srt output.mkv

@pscopic
Copy link

pscopic commented Mar 18, 2020

-map 1:0 is still needed for copying subtitles into output file:

ffmpeg -i input.mkv -sub_charenc 'UTF-8' -f srt -i subs.en.srt -map 0 -map 1:0 -c:v copy -c:a copy -c:s srt output.mkv

Worked for me without the -map 1:0 so long as I omit the -map 0 too.

@nimahkh
Copy link

nimahkh commented Mar 21, 2020

@apotton
Copy link

apotton commented Apr 27, 2020

I used this script I made. I find it more simple and it can simply be changed for other formats:
ffmpeg -i input.mkv -c copy -c:s srt out.mkv

Works very well, and the srt subtitle codec can be changed with other codecs like mov_text for mp4 videos

@SalimF
Copy link

SalimF commented Dec 22, 2020

The first code works well for me from first try, no need to fancy works, this is shell script version

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

e.g name your script Sub and excute it like this

Sub 'movie.mp4' 'sub.ass'
and you're done, you can change ass with srt since I use ass most of time, + I didn;t even need to add UTF8 parameter the sub were embedded perfectly

@godric-cz
Copy link

FFmpeg can figure out subtitle type and mapping on it's own, nothing fancy needed (assuming your input does not have subtitles in it already):

ffmpeg -i input.mkv -i subtitles.srt -c copy output.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