Update: I should have checked the plex support site to start with - this is already documented on this support article. However - I did get to learn a fair bit more about how ffmpeg command line works :)
To make it easier to use the films I buy easier for the kids - I use Plex.
But given that we're a two-language house - I often want to keep multiple audio and subtitle tracks around - so I don't want to burn them into the video track.
To do this - I've been keeping the files in the Matrovska/MKV format.
Some clients need the server to transcode on the fly (for example if you want subtitles and the client needs them just in the video stream).
Now - some of these video files are using a VC-1 codec. Transcoding VC-1 on the fly is not a low CPU task for the server so I'd like to convert them to an easier format (in this case h264).
But I want to keep the MKV format and keep the audio and subtitles untouched.
So - there are various programs out there that can do parts of this - but how to batch it with ffmpeg? I have a few to go through- it would be nice to find a command line tool that can be run. FFmpeg looks like the best candidate here (in fact half or more of the GUIs that do this sort of stuff use it behind the scenes).
So - it means I guess I have to try to figure out the command line for FFmpeg.
The example file I have has the following track list:
Getting the track info
ffmpeg -i file.mkv
This gives a fair bit of info - including the tracks (streams):
Stream #0:0(eng): Video: vc1 (Advanced) (WVC1 / 0x31435657), yuv420p(bt709, progressive),
1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc
Stream #0:1(eng): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s (default)
Metadata:
title : English 3/2+1
Stream #0:2(nor): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s
Metadata:
title : Norwegian 3/2+1
Stream #0:3(eng): Subtitle: hdmv_pgs_subtitle (default)
Metadata:
title : English
Stream #0:4(nor): Subtitle: hdmv_pgs_subtitle
Metadata:
title : Norwegian
Attempt 1 - tell it to convert the video stream
So - the first thing I tried was can I tell it to convert any video stream and hope that the rest just works?
ffmpeg -i file.mkv -c:v libx264 out.mkv
The answer here is - no - it doesn't do what you assume and to go back and read the manual :)
You can see this from the stream mapping it builds up:
Stream mapping:
Stream #0:0 -> #0:0 (vc1 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (ac3 (native) -> vorbis (libvorbis))
OK - so stream 0 is getting converted - but the audio is getting converted too. And there is no subtitle in the resulting file.
Attempt 2 - tell it to copy audio and subtitle unchanged
The next step was to tell it to convert the video file, and to copy the audio and subtitles.
ffmpeg -i file.mkv -c:v libx264 -c:a copy -c:s copy -n out.mkv
This does make some progress - it now gives the following stream mapping:
Stream mapping:
Stream #0:0 -> #0:0 (vc1 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (copy)
Stream #0:3 -> #0:2 (copy)
So - I got an h264 video stream and the English audio and the English subtitles.
So the next bit is to get more than one of each output file.
Attempt 3 - tell it to process more than one of each type of stream
Reading the manual again led me to the map command. This time I will tell it exactly what streams to include.
ffmpeg -i file.mkv \
-map 0:0 -map 0:1 -map 0:2 -map 0:3 -map 0:4 \
-c:v libx264 -c:a:0 copy -c:a:1 copy -c:s:0 copy -c:s:1 copy \
out.mkv
Here we ask it to include all of 0:0 through 0:4.
Then we say
The stream map looks like this now:
Stream mapping:
Stream #0:0 -> #0:0 (vc1 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (copy)
Stream #0:2 -> #0:2 (copy)
Stream #0:3 -> #0:3 (copy)
Stream #0:4 -> #0:4 (copy)
Attempt 4 - simplification
Can we make the command simpler? We want to do the same thing for all audio and subtitle streams.
ffmpeg -i file.mkv \
-map 0:0 -map 0:1 -map 0:2 -map 0:3 -map 0:4 \
-c:v libx264 -c:a -c:s copy \
out.mkv
Testing this gave the same stream map and the same output file - so it looks like we can say -c:s copy and get all subtitles in the map just copied.
But - in an example - it appears that there is a further simplification available:
ffmpeg -i file.mkv \
-map 0 \
-c copy -c:v libx264 \
out.mkv
with the comment
And yes - this does the right thing:
Stream mapping:
Stream #0:0 -> #0:0 (vc1 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (copy)
Stream #0:2 -> #0:2 (copy)
Stream #0:3 -> #0:3 (copy)
Stream #0:4 -> #0:4 (copy)
We can also compare the information to the original:
Stream #0:0(eng): Video: h264 (High), yuv420p(progressive),
1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc
Metadata:
ENCODER : Lavc58.22.101 libx264
DURATION : 00:21:26.077000000
Stream #0:1(eng): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s (default)
Metadata:
title : English 3/2+1
DURATION : 00:21:27.040000000
Stream #0:2(nor): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s
Metadata:
title : Norwegian 3/2+1
DURATION : 00:21:27.040000000
Stream #0:3(eng): Subtitle: hdmv_pgs_subtitle (default)
Metadata:
title : English
DURATION : 00:21:23.866000000
Stream #0:4(nor): Subtitle: hdmv_pgs_subtitle
Metadata:
title : Norwegian
DURATION : 00:21:23.866000000
Here we can see that it has changed the video codec and also added some metadata (ENCODER and DURATION).
So we now have the streams in place - what about quality?
Attempt 5 - bitrate
If we run:
ffmpeg -i file.mkv
we can see the bitrate:
Duration: 00:21:27.01, start: 0.000000, bitrate: 10896 kb/s
But if we run the command on the converted file:
ffmpeg -i out.mkv
it now looks like:
Duration: 00:21:27.04, start: 0.000000, bitrate: 3070 kb/s
So - reading the FFmpeg page on h264 encoding - the line Use this mode if you want to keep the best quality and don't care about the file size. seems very appealing.
Let's try a crf of 17 (near the edge of the range that the page calls sane and visually lossless or nearly so) and we'll go with a profile of slower
This gives us the new command:
ffmpeg -i file.mkv \
-map 0 \
-c copy -c:v libx264 -preset slower -crf 17 \
out.mkv
This runs a little slower :)
The original file is 30834 frames and was running the conversion at about 40fps on my laptop - taking about 12-13 minutes. This conversion is running at about 10-15fps and that gives an estimate of around 30-45 minutes. The longer the source file the more of a difference this will make.
But - this gives the following results:
File | Size | Bitrate |
---|---|---|
file.mkv | 1752999086 | 10896 kb/s |
no_preset.mkv | 494040188 | 3070 kb/s |
slower_preset.mkv | 827987122 | 5146 kb/s |
Subjectively - the result is pretty good - as they said on the encoding page for crf 17 - visually lossless or nearly so - for my eyesight on my displays - this seems to be the case - YMMV :)