Use ffmpeg to add multiple subtitles separately to a video - ffmpeg

I am trying to add multiple languages of subtitles to a video using ffmpeg. I succeeded in adding 1 language, but can't seem to add a second one.
I use this simple script to add english subtitles to my video.
ffmpeg -i %1 -i subs_eng.srt -map 0 -vcodec copy -acodec copy -scodec subrip -metadata:s:s:0 language=English "%~n1"_eng.mkv
In addition, I run another script to add the Dutch subtitles.
ffmpeg -i %1 -i subs_nl.srt -map 0? -vcodec copy -acodec copy -scodec subrip -metadata:s:s:1 language=Dutch "%~n1"_nl.mkv
But whenever I add the second language, it doesn't seem to do anything. The command terminal shows that ffmpeg is processing the video, but there is only 1 subtitle language available in vlc media player (the first one).
I really want to be able to add it in 2 takes rather than in the same script, as I don't have both languages for all of my video's.

Without -map for the subtitle stream, ffmpeg will select only one subtitle stream from among all inputs.
ffmpeg -i %1 -i subs_nl.srt -map 0 -map 1 -vcodec copy -acodec copy -c:s:0 copy -c:s:1 subrip -metadata:s:s:1 language=Dutch "%~n1"_nl.mkv
I set codec mode for the existing subtitle stream to copy and subrip for only the new one. This assumes you muxed exactly one subtitle stream earlier.

It is easier in fact. You can add subtitles to a file without removing old ones by
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. If you add more input subtitle tracks, you need to supply -map 2, -map 3 and so on. See the very conscious and simple Map documentation.
Now more tricky is if you want to properly label these subtitles. You can add
-metadata:s:s:0 language=heb -metadata:s:s:0 handler_name=Hebrew -metadata:s:s:0 title=Hebrew
-metadata:s:s:1 language=eng -metadata:s:s:1 handler_name=English -metadata:s:s:1 title=English
But you need to know the final mapping which will depend on original files ab/presence of subtitles.
Credits to eladkarako.

Related

Hardcoding subtitles from DVD or VOB file with ffmpeg

I have some DVDs that I would like to encode so that I can play them on a Chromecast, with subtitles. It seems that Chromecast only supports text-based subtitle formats, while DVD subtitles are in a bitmap format, so I need to hardcode the subtitles onto the video stream.
First I use vobcopy to create a VOB file:
vobcopy -I /dev/sr0
Next I want to use ffmpeg to encode it as a video stream in a format that is supported by the Chromecast. This is the closest I've come so far (based on the ffmpeg documentation):
ffmpeg -analyzeduration 100M -probesize 100M -i in.vob \
-filter_complex "[0:v:0][0:s:0]overlay[vid]" -map "[vid]" \
-map 0:3 -codec:v libx264 -crf 20 -codec:a copy out.mkv
The -filter_complex "[0:v:0] [0:s:0]overlay[vid] parameters should overlay the first subtitle stream on the first video stream (-map 0:3 is for the audio). This partially works, but the subtitles are only shown for a fraction of a second (I'm guessing one frame).
How can I make the subtitles display for the correct duration?
I'm using ffmpeg 4.4.1 on Linux, but I've also tried the latest snapshot version, and tried gstreamer and vlc (but didn't get far).
The only solution I found that worked perfectly was a tedious multi-stage process.
Copy the DVD with vobcopy
vobcopy -I /dev/sr0
Extract the subtitles in vobsub format using mencoder. This command will write subs.idx and subs.sub. The idx file can be edited if necessary to tweak the appearance of the subtitles.
mencoder *.vob -nosound -ovc frameno -o /dev/null \
-vobsuboutindex 0 -sid 0 -vobsubout subs
Copy the audio and video from the VOB into an mkv file. ffprobe can be used to identify the relevant video and audio stream numbers.
ffmpeg -fflags genpts -i *vob -map 0:1 -map 0:3 \
-codec:v copy -codec:a copy copied_av.mkv
Merge the subtitles with the audio/video stream.
mkvmerge -o merged.mkv copied_av.mkv subs.sub subs.idx
Then ffmpeg will work reliably with the mkv file to write hardcoded subtitles to the video stream.
ffmpeg -i merged.mkv -filter_complex "[0:v:0][0:s:0]overlay[vid]" \
-map [vid] -map 0:1 -codec:v libx264 -codec:a copy hardcoded.mkv

ffmpeg/bash - mapping code for several output

I use ffmpeg on a RaspberryPi3 (99% sure it is a pre-compiled version which I got from https://johnvansickle.com/ffmpeg/ as other methods to install failed for me)
Starting point:
Several multimedia files, each with a single video track, one or more audio tracks, and sometimes plenty subtitles.
My objective:
A bash script taking 3 inputs: respectively track number for video (mostly 0), track number for audio (mostly 1 but can be 2 or..) and track number for subtitles (mostly 2 but can also be 3 or 7 or..).
As output, I want:
a) a new video file with selected tracks (video, single audio which is re-encoded, single subtitle)
b) a new separate srt file with only selected subtitle.
My code was already working for a) but can't seem to get it to work for b)
See below. It does generate the video I want with right selection of tracks.
But its fails to write the right srt file: seems to always default to first subtitle track ?
Is my mapping incorrect, or is this a bash coding issue ?
The file is called Remux.sh, and called with arguments so for example:
./Remux.sh 0 1 5
#!/bin/bash
PATH="/mnt/USB1/Series/_Remux/"
for fullfile in "$PATH"*.mkv
do
newname="${fullfile%}_converted.mkv"
srtname="${fullfile%}_converted.srt"
/usr/local/bin/ffmpeg -i "${fullfile}" -map 0:$1 -map 0:$2 -map 0:$3 \
-c:v copy -c:s copy -c:a ac3 -b:a 640k "${newname}" \
-c:s copy "$srtname"
done
Edit: many thanks #llogan for the pointer ! it's now working with the mapping addition :)
Each output must have its own -map options. Otherwise, an output with no -map options will use the default stream selection behavior which chooses only 1 stream per stream type.
#!/bin/bash
PATH="/mnt/USB1/Series/_Remux/"
for fullfile in "$PATH"*.mkv
do
newname="${fullfile%}_converted.mkv"
srtname="${fullfile%}_converted.srt"
/usr/local/bin/ffmpeg -i "${fullfile}" -map 0:$1 -map 0:$2 -map 0:$3 \
-c:v copy -c:s copy -c:a ac3 -b:a 640k "${newname}" \
-map 0:$3 -c:s copy "$srtname"
done
You may find it helpful to use stream specifiers in your -map options, such as -map 0:s:1 for subtitle stream #2 (index starts at 0). See FFmpeg Wiki: Map.
Recommend you paste your script into shellcheck.net for additional Bash suggestions.

Merge (video and audio) with (background music) with ffmpeg

I have video file input.mp4 that contain video and two audio source - speaking, and music.mp3 that contain background music. Now, I want to merge them.
Here is the command that i use:
ffmpeg -i input.mp4 -i music.mp3 -c:v copy -filter_complex "[0:a]aformat = fltp:44100:stereo,apad[0a];[1] aformat=fltp:44100:stereo,volume=1.5[1a];[0a] [1a] amerge[a]" -map 0:v -map "[a]" -ac 2 -y -shortest output.mp4
input file
https://d9f35555a8b3e9044c8d-95c21efaab8093d23d4124e599a618ee.ssl.cf5.rackcdn.com/mub_audio/1e03dc67-5079-46d5-b692-d69bbb6ee3e3.mp4
audio file
https://d9f35555a8b3e9044c8d-95c21efaab8093d23d4124e599a618ee.ssl.cf5.rackcdn.com/mub_audio/a6cab88a63eb11eb8d8b901b0efa6f1d.mp3
you can check the output file here
https://d9f35555a8b3e9044c8d-95c21efaab8093d23d4124e599a618ee.ssl.cf5.rackcdn.com/mub_audio/6e6e79ef-a732-49d9-8317-4ba97c05a352.mp4
The input file has a pause after every sentence. But in output, that pause duration doesn't have any background music.
What can be the reason? what is the solution for it?

Is there a way to encode multiple audio and sub streams managed by language

so, i have a folder full of .mkv's, i have a one liner which can convert them all to mp4's
these new files now have multiple audio and subtitle streams, one eng, one ger one jap and for subtitles the same, is there an way with which i can easily specify use f.e. German audio and burn eng subtitles.
My One Liner:
for /R %f IN (*.mkv) DO ffmpeg -i "%f" -c copy "%~nf.mp4"
How would i have to modify this one Liner for it to work.
I found out that with -vf subtitles=foo.ass i can hardcode subtitles, but how do i select one audio and or subtitle stream from included with the file?
and how would i select f.e. a audio atream but none subtitle stream
I found out, that with
-map 0:V -map 0:a:m:language:ger -map 0:s
I can get a file with which only has the german audio files, but now i need the same for the subtitles, to only include the english ones
I can get a file with which only has the german audio files, but now i need the same for the subtitles, to only include the english ones
ffmpeg -i input.mkv -map 0:v -map 0:a:m:language:ger -map 0:s:m:language:eng -c copy output.mkv
For burning subtitles you must provide the file name, and optionally a stream index. You can use ffprobe to determine the subtitle index of the desired subtitle language:
ffprobe -v error -select_streams s -show_entries tags=language -of default=nw=1:nk=1 input.mkv
Example output:
ger
eng
So ger is index #0 and eng is index #1.
Final ffmpeg command:
ffmpeg -i input.mkv -filter_complex "subtitles=f=input.mkv:si=1[v]" -map "[v]" -map 0:a:m:language:ger -c:a copy -movflags +faststart output.mp4

How to add a Subtitle as default to a video already containing a subtitle stream using ffmpeg? [duplicate]

By adding an .ass subtitles track to an mkv video with ffmpeg, it isn't set as default track, so on playback you have to manually turn on subtitles. Is it possible to set the default flag for the subtitles track?
ffmpeg command used:
ffmpeg -i video.mp4 -i subtitles.ass -c:v libx264 -preset veryslow \
-pix_fmt yuv420p10le -c:a copy -c:s copy output.mkv
Note that I want to keep .ass subtitle format, not convert the subtitles to mov_text like suggested in this similar question:
How to set default streams with ffmpeg
There is the possibility to set the default flag afterwards with mkvpropedit like this:
mkvpropedit output.mkv --edit track:s1 --set flag-default=1
But is it possible to do this directly with ffmpeg?
I think as per this patch this is now possible. At least for me it works with:
ffmpeg -i in.mp4 -i in.srt -c copy -disposition:s:0 default out.mkv
Note s in -disposition:s:0 in this case stands for subtitle and not stream. To select the second steam by index use -disposition:1.
You can use 'forced' instead of default to force vlc to play it
ffmpeg -f mp4 -i outfile.mp4 -f srt -i VTS_07_0.EnglishV2.srt -c:v copy -c:a copy -metadata:s:a:0 language=Japanese -c:s mov_text -metadata:s:s:0 language=English -disposition:s:s:0 forced mix.mp4

Resources