I decided to archive all of my skydiving footage from my digital camcorder to DVDs, all under Linux. As somebody who has very little experience with creating DVDs, editing video, and using tools like Photoshop/Gimp, it was a challenge on any platform. Perhaps it would have been easier on Windows, but I'm pretty sure it would have required stealing or buying extra software. It didn't cost me anything (software wise) on Linux.

My first attempt was using Fedora Core 4 and after troubles I reinstalled with Fedora Core 5. I had trouble capturing video from my video camera via Firewire (IEEE1394). In retrospect I believe the video was being captured but my players were not displaying it because my video card did not support XVideo... apparently an original Matrox Millenium from 1996 is not the best video editing hardware, even if it did have the daughtercard to upgrade it to 2MB of RAM. So I went out and bought a GeForce 6600 GX and also moved to Fedora Core 5. You can use the command 'xvinfo' to see if your video card supports XVideo... if you don't get a bunch of lines of output then you should get a new video card.

I have plenty of disk space so I did a full install of Fedora Core 5 and then added FreshRPMS to my Yum repositories and installed pretty much everything available there (most notably Kino and Cinelerra and their dependencies). I believe the only thing I had to manually install was TooLame to create my empty sound file.

I'm pretty much documenting this so that I don't forget how I did it... hopefully it helps somebody else as well.

Capturing Video

So, first I captured video from my camera via Firewire. I connected the camera, turned it on to Play mode, and started Kino. Under Preferences I set it to auto-split files (splits every time you turned the camera on/off) and set file type to "DV AVI Type 2". I recorded the entire 90-min tape and at some point the Kino GUI stopped working but I could see messages in the xterm I started it from showing it was still running. It did not display the video while capturing.

Update: After several Kino crashes, I switched to dvgrab directly:

dvgrab --autosplit --timestamp --format dv2 /root/video/tapeX/capture

Editing Video

So, then I used Cinelerra which is an amazing video editing suite available for free on Linux. It took me a whole evening before I could get very far with editing in here, but ultimately it lets me piece video together and insert transitions and titles and mix audio, etc. I found that I get much better quality using Cinelerra's Deinterlace video effect, which I would apply to the entire video stream. I then followed these instructions to output proper video for the DVD. I did Edit -> Select All and then File -> Render and checked only the audio box and output it as AC3 and used an .ac3 extension.

Once the audio was exported, I rendered again, this time selecting only video, and chosing MPEG Video and a .m2v extension. Click the settings next to the video checkbox and choose YUV 4:2:0 color model, DVD format preset, and I found a fixed quantization of 6-8 to give me acceptable quality. I turned on the Denoise and Progressive Frames options. Having a dual-core Athlon X2 4400+ w/ 2GB of RAM helps with this stage. I am able to render and encode 1 second of video every 1.5 seconds or so (one core ends up doing the compression and the other core ends up spending about half of its time rendering).

At this point you have to use a command-line tool to combine the video and audio:

mplex -M -f 8 -o newtest.mpg newtest.ac3 newtest.m2v

Creating a DVD

So this is how I created my actual content and stored it in MPEG2 format. I thought that was the hard part, until I tried to make a DVD with menus out of it... here are some URLs that helped me out.

Creating a DVD Menu

The menus on a DVD are really MPEG videos, even if you want a static image for your menu. The buttons change as they are highlighted and then selected, and these images are stored as subtitles in that menu video. So, to create a basic menu, you create a JPEG and turn it into an MPEG2 video file (adding audio, even if it is silence). You then create mostly-transparent PNG files for the buttons on the menu, and use spumux to mix those in as subtitles.

So, the JPEG file first. All you need is an image, which for NTSC is 720x480 and 81x72dpi. I used The GIMP for this. You can draw the background or capture a still frame from your video stream. In my case I used a still photo and in a second layer (with transparent background), I created solid-colored boxes as the button backgrounds. I then exported to JPEG as a flattened image, but I kept the original so I can change the background and keep the same buttons. You should put your buttons in nice rows/columns.

Now the hard part, for me at least. In The Gimp, create a new layer with a transparent background. On this layer, draw how each button should look when highlighted (i.e. when the use is moving around with the keypad on the menu). It could be a box that borders the underlying image, it could change the text color, or it could replace the entire undelying image with a new one. The important thing here is that it lines up with the underlying layer and all of the buttons are separated by transparent space.

Then I created a new file with a transparent background (under advanced options) and the same size/resolution, and copy and drag the layer into that new file. Then I go to Image -> Mode -> Indexed and set it to use a maximum number of 3 colors. I don't know if this is strictly necessary but it seems to work for me. I then export the new file as a PNG.

Then I created a second new layer in the original image which shows the buttons when they are actually clicked on. I then exported this as PNG as well, for a total of one background JPEG and two mostly-transparent PNG files.

Now, I needed a short bit of silent audio:

dd if=/dev/zero bs=4 count=1920 | toolame -b 128 -s 48 /dev/stdin silent.mp2

Next I needed a menu.xml file:

<subpictures> <stream> <spu force="yes" start="00:00:00.00" select="selected.png" highlight="highlighted.png" autooutline="infer" autoorder="rows"> </spu> </stream> </subpictures>

You might want to add "outlinewidth" if you want it to auto-detect your buttons but they are not solid boxes in your PNG files. In my case they are so it is not a big deal, but if it was just text or something then with the above settings, each letter may be detected as a separate button. Note that the order=rows means that the top-left button is #1, the next to its right is #2, etc, then on to the second row.

And now to create the menu MPEG:

jpeg2yuv -n 25 -I p -f 25 -j background.jpg | mpeg2enc -n n -f 8 -a 2 -o menu.m2v
mplex -f 8 -o /dev/stdout menu.m2v silent.mp2 | spumux -v 2 menu.xml > menu.mpg

Be sure to check menu.mpg to see that it is non-zero in size. Note that some of the parameters above need to be changed for an aspect ratio other than 4:3 and if you are not using NTSC.

Here is my Gimp XCF menu template that I used for my menus. I would load this, paste in the background, and then delete all of the buttons I didn't need. Then I would turn off visibility for the "selected" and "highlighted" layers and export as a JPG (does not need to be indexed). Then I would copy each of the highlighted and selected layers to a new document, index to 3 colors, and export as PNG. The JPG background image can have any number of colors, which means I could use antialiasing on the fonts and multi-colors.

Creating a motion DVD Menu

To create a menu with a moving background, I used Cinelerra to export a "m2v" and an "ac3" file. I used Cinelerra to put the title on the video so that my images would only have the buttons.

Then I used only three colors -- blue button backgrounds, black button border and text, and yellow highlighted buttons. All tree layers -- buttons, highlighted, and selected must have buttons of exactly the same size. I would index the file directly and export three png files: one with the buttons and text (I called overlay.png), one with the buttons, text, and highlighted border, and the final one with the buttons, text, and selected border (which I made the same color as the button background to cut down on the color count). This is different from the static menu above as the highlighted and selected layers don't contain just the border around the button but instead the entire button and the text and the border.

Be sure to not use antialiased fonts for this. Note that my button backgrounds are a solid color and not a pattern like on my static menu above. Here is an example GIMP file. For this we will export three PNGs and no JPG. Here is my menu.xml:

<subpictures> <stream> <spu force="yes" start="00:00:00.00" image="overlay.png" select="selected.png" highlight="highlighted.png" autooutline="infer" autoorder="rows"> </spu> </stream> </subpictures>

And here is how I generated the mpeg:

mplex -f 8 -o /dev/stdout menu.m2v menu.ac3 | spumux -v 2 menu.xml > menu.mpg

Authoring a DVD

I wanted just a main menu with multiple titles. So far I have not set up chapters or multiple menus. Here is my dvdauthor.xml file:

<dvdauthor dest="/home/kirk/dvd/testdvd/"> <vmgm> </vmgm> <titleset> <menus> <video format="ntsc"></video> <pgc entry="root"> <vob file="menu.mpg"></vob> <button>jump title 1;</button> <button>jump title 2;</button> <button>jump title 3;</button> </pgc> </menus> <titles> <pgc> <vob file="title1.mpg"></vob> <post>call vmgm menu;</post> </pgc> <pgc> <vob file="title2.mpg"></vob> <post>call vmgm menu;</post> </pgc> <pgc> <vob file="title3.mpg"></vob> <post>call vmgm menu;</post> </pgc> </titles> </titleset> </dvdauthor>

Now, all you have to do is run "dvdauthor -x dvdauthor.xml". But be sure to wipe out your destination directory before running each time! I had an issue where it kept adding to the output directory and it caused problems.

Advanced Menus

Creating the XML file for dvdauthor can be frustrating. As I worked with it my DVD menus became better and better, so here are some of my best.

One titleset, no vmgm menu

This is my favorite layout. All menus and videos in a single titleset. I think this is the cleanest way to do things if you have all the same aspect ratio. If there is no vmgm menu at all (i.e. an empty vmgm block) then the first title will just start playing. So my vmgm menu simply sets register "g1" to 1 and redirects to the first menu in the titleset. Note that you can optionally play an intro movie before jumping to the menu.

Next, each of the three titleset menus will check "g1" and jump to another menu if it is not set for the current menu. So, first you go to the first menu with g1 set to 1 which shows the menu. But if you go to menu 2, then g1 will be set to 2, and if the user hits the "menu" key during playback it will go to menu 1 but then jump to menu 2. So in this case we have the first menu which can go to either of the other two menus, and each of those can go back to the main menu. Each of these menus loop continuously which is useful if you have a movie for the menu.

<dvdauthor dest="/home/kirk/dvd/crw/out/"> <vmgm> <menus> <pgc> <pre>{g1 = 1;}</pre> <vob file="optional_intro.mpg"></vob> <button>jump titleset 1 menu;</button> </pgc> </menus> </vmgm> <titleset> <menus> <video format="ntsc"></video> <pgc entry="root"> <pre>{if (g1 ne 1) {jump menu 2;}}</pre> <vob file="menu1.mpg"></vob> <button>jump title 1 chapter 1;</button> <button>{g1 = 2; jump menu 2;}</button> <button>{g1 = 3; jump menu 3;}</button> <post>jump menu 1;</post> </pgc> <pgc> <pre>{if (g1 ne 2) {jump menu 3;}}</pre> <vob file="menu2.mpg"></vob> <button>jump title 2 chapter 1;</button> <button>{g1 = 1; jump menu 1;}</button> <post>jump menu 2;</post> </pgc> <pgc> <pre>{if (g1 ne 3) {jump menu 1;}}</pre> <vob file="menu2.mpg"></vob> <button>jump title 2 chapter 1;</button> <button>{g1 = 1; jump menu 1;}</button> <post>jump menu 3;</post> </pgc> </menus> <titles> <pgc> <vob file="title1_chapter1.mpeg"></vob> <vob file="title1_chapter2.mpeg"></vob> <post>call menu;</post> </pgc> <pgc> <vob file="title2_chapter1.mpeg"></vob> <post>call menu;</post> </pgc> <pgc> <vob file="title3_chapter1.mpeg"></vob> <post>call menu;</post> </pgc> </titles> </titleset> </dvdauthor>
Widescreen mixed with 4:3 video

This config will go straight to the titleset menu (menu.mpg) where one of the options there allows you to go back to the vmgm menu. The reason for this is that I prefer the titleset menus, but in this case I wanted a menu that jumps to widescreen and regular videos. Only a vmgm menu, it seems, can jump to other titlesets. And each titleset has to have the same aspect ratio. So, here is how it works:

  1. DVD starts with the vmgm menu, which immediately jumps to the titleset menu since register "g1" is not equal to 2.
  2. If user chooses the proper menu item, "g1" is set to "2" and vmgm menu is called, which this time will display.
  3. From the VMGM menu, one option will set "g1" to non-2 ("1" in this case) and returns the user to the titleset menu.
  4. If the user hits "menu" while watching non_widescreen.mpeg, it will bring them to that titleset menu which is not what I want since they watched it from the vmgm menu. So the titleset menu will redirect to the vmgm menu if g1 is equal to 2.

Here is the dvdauthor.xml file. Note that from the vmgm menu, "title 1" means the first title on the DVD (the only title in the first titleset in this case) and "title 2" means the second title on the DVD (the first title in the second titleset in this case).

<dvdauthor dest="/home/kirk/dvd/crw/out/"> <vmgm> <menus> <pgc> <pre>{if (g1 ne 2) {jump titleset 1 menu;};}</pre> <vob file="vmgm_menu.mpg"></vob> <button>jump title 1;</button> <button>jump title 2;</button> <button>{g1 = 1; jump titleset 1 menu;}</button> </pgc> </menus> </vmgm> <titleset> <titles> <video format="ntsc" aspect="16:9"></video> <pgc> <vob file="widescreen.mpeg"></vob> <post>call vmgm menu;</post> </pgc> </titles> </titleset> <titleset> <menus> <video format="ntsc"></video> <pgc entry="root"> <pre>{if (g1 eq 2) {jump vmgm menu;};}</pre> <vob file="menu.mpg"></vob> <button>jump title 2 chapter 1;</button> <button>jump title 3 chapter 1;</button> <button>{g1 = 2; jump vmgm menu;}</button> </pgc> </menus> <titles> <pgc> <vob file="non_widescreen.mpeg"></vob> <post>call vmgm menu;</post> </pgc> <pgc> <vob file="title2_chapter1.mpeg"></vob> <vob file="title2_chapter2.mpeg"></vob> <post>call menu;</post> </pgc> <pgc> <vob file="title3_chapter1.mpeg"></vob> <post>call menu;</post> </pgc> </titles> </titleset> </dvdauthor>

Burning a DVD

Before burning, I used xine to test it: "xine dvd:/path/to/dvd/" where /path/to/dvd/ contains the VIDEO_TS directory. To burn it, I first created an ISO image:

mkisofs -dvd-video -o dvd.iso dvd_dir

Where "dvd_dir" is the directory containing VIDEO_TS. Then to burn it:

growisofs -Z /dev/dvdwriter=dvd.iso

Although the above worked for me, I'm told this might be a safer version (Thanks Mark Alford):

growisofs -dvd-compat -speed=1 -Z /dev/dvdwriter=dvd.iso