You have just ripped a movie from a DVD and you want to store it in a compressed format with multi-language audio tracks, subtitles and high quality video.

— Written by Triangles on July 09, 2017 • updated on November 13, 2017 • ID 54 —
source = https://www.internalpointers.com/post/convert-vob-files-mkv-ffmpeg

In this guide I’ll show you how to do that by using FFmpeg (version 3.2.5 or greater) on a Linux-based operating system – I’m currently using Debian Stable, aka Jessie.

FFmpeg is able to deal with a vast amount of audio/video formats and containters. For our task I will be using Matroska Multimedia Container container (.mkv files), as it is capable of storing different audio, video and subtitle tracks together. The video stream will be encoded with H.264 codec, currently the best guy in town. Audio tracks will be encoded in mp3 format.

Let’s begin!

Step 1: unify your VOBs

VOB file are usually 1 Gb each in order to be compatible with all operating systems, as some cannot read files larger than that size. The first step then is to join them into a single, big VOB file. To do that, browse to the VIDEO_TS folder and do:

$ cat *.VOB > output.vob
That’s it.

Step 2: identify the streams

Now let’s inspect the newly created file: we want to find what kind of stuff it contains. Use FFmpeg for that, as follows:

$ ffmpeg -i output.vob
For example, you might end up with something like:

Input #0, mpeg, from 'output.vob':
Duration: 01:50:40.99, start: 0.287267, bitrate: 7581 kb/s
Stream #0:0[0x1bf]: Data: dvd_nav_packet
Stream #0:1[0x1e0]: Video: mpeg2video (Main), yuv420p(tv, top first), 720x576 [SAR 64:45 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
Stream #0:2[0x80]: Audio: ac3, 48000 Hz, 5.1(side), fltp, 384 kb/s
Stream #0:3[0x89]: Audio: dts (DTS), 48000 Hz, 5.1(side), fltp, 768 kb/s
Stream #0:4[0x82]: Audio: ac3, 48000 Hz, 5.1(side), fltp, 384 kb/s

Here FFmpeg reports that my VOB file contains five streams. Starting from the first one, Stream #0:0 contains data regarding the DVD’s menu navigation. We can get rid of it. Stream #0:1 is the actual movie. The remaining streams are audio tracks. I’ll discard Stream #0:2 as it contains the same data (English audio track) encoded in a different format.

Mind the deep-buried streams!

Normally, while looking for streams, FFmpeg parses only few seconds of the input data as most formats have a global header there that describes everything present in the file. Unfortunately VOBs have no headers and it is likely to find movies that hold additional streams further down the VOB file.

Let FFmpeg scan it thoroughly by adding two more flags: -analyzeduration (in microseconds) and -probesize (in bytes). Honestly I’m not able to tell the difference between those options: put in there some fairly large numbers and tweak them until you are satisfied. For example:

$ ffmpeg -analyzeduration 100M -probesize 100M -i output.vob
And, not surprisingly, two more streams are found:

...
Stream #0:5[0x21]: Subtitle: dvd_subtitle
Stream #0:6[0x20]: Subtitle: dvd_subtitle

Subtitles: let’s keep them!

Step 3: encoding

We are ready to pack our DVD into a beautiful .mkv file. The command looks like:

ffmpeg \
-analyzeduration 100M -probesize 100M \
-i output.vob \
-map 0:1 -map 0:3 -map 0:4 -map 0:5 -map 0:6 \
-metadata:s:a:0 language=ita -metadata:s:a:0 title="Italian stereo" \
-metadata:s:a:1 language=eng -metadata:s:a:1 title="English stereo" \
-metadata:s:s:0 language=ita -metadata:s:s:0 title="Italian" \
-metadata:s:s:1 language=eng -metadata:s:s:1 title="English" \
-codec:v libx264 -crf 21 \
-codec:a libmp3lame -qscale:a 2 \
-codec:s copy \
output.mkv

Let me dissect it:

-analyzeduration 100M -probesize 100M — keep this one so that FFmpeg is able to find hidden streams;
-i output.vob — the input file;
-map 0:1 -map 0:3 -map 0:4 -map 0:5 -map 0:6 — here I’m mapping the streams, namely I’m telling FFmpeg to keep Stream 0:1, Stream 0:3, Stream 0:4, Stream 0:5, Stream 0:6 and put them in the output file in that specific order;
-metadata[...] — this is used to give streams a title and other additional information, specifically to audio tracks (s:a:0 and s:a:1 where a stands for audio) and subtitles (s:s:0 and s:s:1 where s stands for subtitles);
-codec:v libx264 -crf 21 — defines the video codec in use and the constant rate factor (crf), namely the quality level. This method allows the encoder to keep a constant quality level, regardless the output file size: 0 is lossless, 23 is default, and 51 is worst possible. The sane range is between 18 and 28;
-codec:a libmp3lame -qscale:a 2 — defines the audio codec in use and the quality level: 0-3 will produce transparent results, 4 (default) should be close to perceptual transparency, 6-9 produces an “acceptable” quality. Using numbers from 0 to 9 means that the audio track will be encoded in variable bitrate (vbr) mode: smaller files, better quality;
-codec:s copy — s stands for subtitles: copy them as they are;
output.mkv — the output file.
Bonus point: if your machine supports it, add the flag -threads N to enable multi-threading and give the encoding a boost. Replace N with the number of your CPU cores.

Sources

Wikipedia – VOB (link)
Wikipedia – Matroska (link)
FFmpeg f.a.q. – 3.16 Why does FFmpeg not see the subtitles in my VOB file? (link)
FFmpeg wiki – FFmpeg and H.264 Encoding Guide (link)
FFmpeg wiki – FFmpeg MP3 Encoding Guide (link)