13

I'm trying to re-encode video streams from a Matroska file to save space, while keeping all the subtitles as-is, using ffmpeg. I want to write a generic command that works without me having to specify exact stream numbers. Now I can't figure out how to let ffmpeg pick its default video stream and default audio stream and then all subtitles.

The current input file I'm working with has these streams, but other files will have different streams.

 [lavf] stream 0: video (mpeg2video), -vid 0 [lavf] stream 1: audio (ac3), -aid 0, -alang eng, Surround 5.1 [lavf] stream 2: audio (ac3), -aid 1, -alang fre, Surround 5.1 [lavf] stream 3: audio (ac3), -aid 2, -alang ita, Surround 5.1 [lavf] stream 4: audio (ac3), -aid 3, -alang spa, Surround 5.1 [lavf] stream 5: audio (ac3), -aid 4, -alang eng, Stereo [lavf] stream 6: subtitle (dvdsub), -sid 0, -slang eng [lavf] stream 7: subtitle (dvdsub), -sid 1, -slang fre [lavf] stream 8: subtitle (dvdsub), -sid 2, -slang ita [lavf] stream 9: subtitle (dvdsub), -sid 3, -slang spa [lavf] stream 10: subtitle (dvdsub), -sid 4, -slang ara [lavf] stream 11: subtitle (dvdsub), -sid 5, -slang dan [lavf] stream 12: subtitle (dvdsub), -sid 6, -slang dut [lavf] stream 13: subtitle (dvdsub), -sid 7, -slang fin [lavf] stream 14: subtitle (dvdsub), -sid 8, -slang ice [lavf] stream 15: subtitle (dvdsub), -sid 9, -slang nor [lavf] stream 16: subtitle (dvdsub), -sid 10, -slang por [lavf] stream 17: subtitle (dvdsub), -sid 11, -slang swe [lavf] stream 18: subtitle (dvdsub), -sid 12, -slang fre [lavf] stream 19: subtitle (dvdsub), -sid 13, -slang ita [lavf] stream 20: subtitle (dvdsub), -sid 14, -slang spa 

Commands I have tried:

ffmpeg -i IN.mkv -c:v libx264 -threads 4 -speed 1 -f matroska OUT.mkv

Result: One video stream, one audio stream, no subtitle streams.

ffmpeg -i IN.mkv -c:v libx264 -threads 4 -speed 1 -f matroska -c:s copy OUT.mkv

Result: One video stream, one audio stream, one subtitle stream.

ffmpeg -i IN.mkv -c:v libx264 -threads 4 -speed 1 -f matroska -map 0 OUT.mkv

Result: All video, all audio, all subtitles.

ffmpeg -i IN.mkv -c:v libx264 -threads 4 -speed 1 -f matroska -c:s copy -map 0:s OUT.mkv

Result: No video, no audio, all subtitles.

As far as I can tell from the manual, -c:s copy is supposed to copy all the streams, not just the default one, but it won't. Perhaps it's a bug?

To clarify, what I'm after is the result: one video, one audio and all subtitles.

2 Answers 2

30

The stream selection default behavior only selects one stream per type of stream, so inputs with multiple audio streams will create an output with one audio stream. To disable this behavior and manually choose desired streams use the -map option.

These examples use -c copy to stream copy (re-mux) from the input to to the output. No re-encoding occurs.

Stream copy all streams

ffmpeg -i input -map 0 -c copy output 

1st video stream, 2nd audio stream, all subtitles

ffmpeg -i input -map 0:v:0 -map 0:a:1 -map 0:s -c copy output 

3rd video stream, all audio streams, no subtitles

This example uses negative mapping to exclude the subtitles.

ffmpeg -i input -map 0:v:2 -map 0:a -map -0:s -c copy output 

Choosing streams from multiple inputs

All video from input 0, all audio from input 1:

ffmpeg -i input0 -i input1 -map 0:v -map 1:a -c copy output 
0
-2
 #!/bin/bash # Extract old format video tracks from each MKV file in the given directories and transcode them to e.g. x265 # extend for your purposes ;-) I've run this script for over a year. echo MKV:migrate echo "| no args : scan directory tree for candidates" echo "| dir : scan that directory" echo "| . -c : convert the video track in those candidates to H.265 (x265)" echo "| . -c -r : convert and remux into new resulting video" echo "\ . -c -r -k : and keep the intermediate video files (not by default)" echo " " date echo Reading all directories for MKV files, and verify whether they have Mpeg-4p2, a H264 or XVid or DivX or a H263 track in them. echo If so, it will be extracted with mkvextract and converted to H265 with ffmpeg. echo If remuxed, the original will be named to .old.mkv, and the new one will get the original name with suffix .x265.mkv. echo With remuxing with mkvmerge, the new video track will be integrated, whilst the original old format track will not. echo "(See: ffmpeg bla bla bla -d \!0 trackno if track 0 was the old videoformat, it will be skipped by -d)" echo . vidxt="mp4" vidco="libx265" H264=" " H264="(H.264)|H264|(MPEG-4p10)|" CRF="25" VIDRATE=" 1500k " #If 4 cores, you have 400% available. Limit your process to 65% e.g. CPULIM=240 #SCALE=" -vf scale=1280:720 " #RATE=" -r 23.976216 " # If no directory is given, work in local dir if [ "$1" == "" ]; then DIR="." doconvert="" doremux="" keep="" else DIR="$1" doconvert=$2 doremux=$3 keep=$4 fi # I ran this script first to get rid of really old formats... if [ "$H264" == " " ]; then echo "Not scanning for H.264 videotracks." else echo "Scanning also for H.264 videotracks: "$H264 fi # Get all the MKV files in this dir and its subdirs find "$DIR" -type f -name '*.mkv' | sort | while read filename do # Find out which tracks contain the subtitles # echo Checking: $filename cand=0 mkvmerge -i "$filename" | grep ' video ' | while read subline do candidate=`echo $subline | egrep -o $H264"(avc1)|(XVID)|(DX50)|MPEG-4p2"` #echo $filename.$subline if [ "$candidate" != "" ]; then # Grep the number of the video/audio track tracknumber=`echo $subline | egrep -o "[0-9]{1,2}" | head -1` #echo " EVAL : " $candidate " trk:" $tracknumber " cand: " $cand if [ $cand != 1 ]; then echo "|" echo $filename " : " $subline " : " $tracknumber MYDAT=$(ls -l --full-time "${filename}" |gawk -F ' ' '{ print $6" "$7 }' | gawk -F. '{ print $1 }') echo " | Original date: "$MYDAT cand=1 else echo " /--Next track" fi echo " |" `date` echo " | Candidate: [$candidate]" # Get base name for subtitle basefilename=${filename%.*} echo " \-- Video: "$subline" ($basefilename)" if [ "$doconvert" == "-c" ]; then # Extract the track to a .tmp file echo " -- extracting $tracknumber:$basefilename.vid.tmp (avg 2 min) " `mkvextract tracks "$filename" $tracknumber:"$basefilename.vid.tmp" >>/dev/null 2>&1` `chmod g+rw "$basefilename.vid.tmp"` # DO THE FFMPEG CONVERSION echo " -- "`date` echo " -- FFMpeg conversion of $basefilename.vid.tmp via $vidco to $vidxt (avg 2 hour)" # echo ` ffmpeg -i "$basefilename.vid.tmp" $RATE -c:v:0 $vidco -crf $CRF -b:v:0 $VIDRATE $SCALE "$basefilename.$tracknumber.$vidxt"` >/dev/null 2>&1 `cpulimit -f --limit=$CPULIM -- ffmpeg -i "$basefilename.vid.tmp" $RATE -c:v:0 $vidco -crf $CRF -b:v:0 $VIDRATE $SCALE "$basefilename.$tracknumber.$vidxt" >/dev/null 2>&1` if [ "$keep" == "" ]; then `rm "$basefilename.vid.tmp" >/dev/null 2>&1` fi if [ "$doremux" == "-r" ]; then echo " -- "`date` `mv "$filename" "$basefilename".old.mkv >/dev/null 2>&1` echo " -- Muxing $filename without track $tracknumber but with $vidxt. (avg 4 min) " mkvmerge -o "$basefilename.x265.mkv" -d \!$tracknumber "$basefilename.old.mkv" "$basefilename.$tracknumber.$vidxt" >>/dev/null 2>&1 if [ "$keep" == "" ]; then `rm "$basefilename.$tracknumber.$vidxt" >/dev/null 2>&1` fi touch --date="$MYDAT" "$basefilename.x265.mkv" >>/dev/null 2>&1 fi fi fi done done 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.