Problem Background

I’d like to standardize the color palette of vobsub subtitles of all video files in my collection. Further, I’d like to have consistent colors between mplayer and vlc.

Solution

First, mplayer cannot play colored vobsub subtitles. This immediately narrows down our color palette to white text with a black outline/shadow.

A typical vobsub file pair would consist of an index file (subtitle.idx) and the vobsub file (subtitle.sub). The index file is a plain text file with a preamble section and a list of time stamps and matching subtitles indicating when the subtitles should be shown. It’s the preamble section we are interested in.

A typical preamble section might look like this:

# VobSub index file, v7 (do not modify this line!)
size: 720x480
# The original palette of the DVD
palette: 000000, 828282, 828282, 828282, 828282, 828282, 828282, ffffff, 828282, bababa, 828282, 828282, 828282, 828282, 828282, 828282
# Custom colors (transp idxs and the four colors)
custom colors: OFF, tridx: 1000, colors: 000000, bababa, 828282, 000000

The original DVD color palette is of no use to us. We can throw those lines out. The way to control the color of the subtitles on-screen is through the custom colors. Now, our preamble looks like this:

# VobSub index file, v7 (do not modify this line!)
size: 720x480
custom colors: OFF, tridx: 1000, colors: 000000, bababa, 828282, 000000

First, set the custom colors to ON to activate this line. There are four colors that you can prescribe for a vobsub subtitle stream. The first is the background color of the subtitle. The second is the color of the subtitle text. The third and fourth represent the inner, thick outline/shadow and outer, thin outline/shadow around the subtitle font respectively.

The tridx parameter allows one to set one of more of these fields to be transparent. Typically, the background to the subtitle font is set to be fully transparent(1). The rest are opaque(0). This is then followed by a set of four rgb colors for each of the aspects of the subtitle mentioned earlier. I’ve chosen to go with this:

custom colors: ON, tridx: 1000, colors: 000000, ffffff, 000000, 000000

In the above example, the background and outlines are set to be black (000000). The font color is set to be white (ffffff). The background is set to be fully transparent, but the font and its outlines are set to be opaque (tridx: 1000). This produces clear, legible subtitles that follow the same color scheme regardless of whether the file is played in mplayer or vlc.

Now, merge this back into the matroska file or select this file as the subtitle while playing your video file. Enjoy your nice looking subtitles!

Problem Background

Matroska files are a great format to build a video collection in. One of the most interesting aspects of the format is the ease of adding, removing, extracting or replacing streams (video, audio, subtitle) in the file. An example would be if you found a better subtitle file for one of the movies and would like it to replace the current one in the file. So, it’d be useful to be able to split a file into its individual tracks and reconstitute it back.

Solution

Use mkvinfo, mkvextract and mkvmerge. All are part of the mkvtoolnix package.

Identification

For this example, I have a matroska file called test.mkv. It consists of a video stream encoded using x264 at 29.97 fps. It contains one Japanese audio track and one english subtitle track in the vobsub format. I have recently come across a new subtitle track in the srt format that I’d like to use instead of the original subtitles.

Although I already know the make up of my mkv file, I’ll pretend I don’t. I’ll start by identifying the tracks in the matroska file.

MKVFILE=test.mkv
mkvinfo $MKVFILE | grep Track

Your output will look similar to this:

| + Track number: 1
| + Track UID: 631176983
| + Track type: video
| + Track number: 2
| + Track UID: 1164692611
| + Track type: audio
| + Track number: 3
| + Track UID: 1596684428
| + Track type: subtitles

This shows the makeup of the matroska file. It contains three tracks. The first is the video track, the second is the audio track and the third is the subtitle track.

Extraction

The tracks can be extracted one at a time or all at once. While extracting the video track, be sure to note down the the fps (frames per second) and the display dimension (which can be different from the actual dimensions of the video stream). To get the fps, use mkvinfo:

mkvinfo $MKVFILE | grep duration

This might produce an output similar to this:

| + Default duration: 33.367ms (29.970 fps for a video track)
| + Default duration: 32.000ms (31.250 fps for a video track)

Since the video track is the first track, the fps value to note is 29.970. Similarly, to get the display dimensions,

mkvinfo $MKVFILE | grep Display

And this might yield something like this:

| + Display width: 835
| + Display height: 480

Now, extract the tracks. I’ll extract all three tracks at once and dump them in appropriately named files:

mkvextract tracks $MKVFILE 1:video.264 2:audio.ac3 3:subtitle

This will create a raw video stream (video.264), a raw audio stream (audio.ac3) and a subtitle set with an index file (subtitle.idx) and a vobsub file (subtitle.sub). The subtitle files are useless to me.

Reconstruction

Now, I’ll reconstruct the video and audio streams along with my new subtitle file called newsubs.srt. To reconstitute this set back together, use the mkvmerge command like this:

TITLE="My Title"
AUDIOLANGUAGE=jpn
SUBLANGUAGE=eng
OUTFILE=output.mkv
mkvmerge --title "$TITLE" -o $OUTFILE --default-duration 0:29.970fps --display-dimensions 0:835x480 video.264 --language 0:$AUDIOLANGUAGE audio.ac3 --language 0:$SUBLANGUAGE newsubs.srt

The fps value can also be given in terms of fractions (29.970 = 30000/1001). Note that you need to specify a three-letter code to designate the language of the audio and subtitle stream. These language codes can be easily got from the mkvmerge command like this:

mkvmerge --list-languages

You can pipe this to a grep command to search for the language code for the specific language you’re using. For instance,

mkvmerge --list-languages | grep "Japanese"

GUI alternative

As an alternative to all this CLI work, you could just launch the mkvinfo and mkvmerge GUI programs and drag and drop the streams to create the new file. You’d then fill in the fps and display dimensions in the appropriate text fields and pick the languages from the drop-down menus. I feel that GUIs tend to slow you down, so I prefer the CLI route.