Sunday, December 5, 2010

Digitizing Old Home Video

A couple years back, I decided to digitize my family's old VHS tapes - partially because VHS players are becoming less common and partially because analog media like VHS tapes degrades over time. So really, I didn't just want to have the video in a more accessible format, I also wanted an "archive-quality" copy of my family's old videos. In my mind, that had three implications:

  1. Regardless of what form the video ultimately ended up in, I wanted to have a copy of the raw, unedited video in format that I thought would be as enduring as possible. In my mind, the obvious choice was to capture the video to AVI files containing DV streams. AVI has probably been the most enduring video container and it will probably be around forever. And DV encoding is standard, well-supported and lossless. It's also worth noting that there is an actual .dv file format that contains a DV stream but support for it seems a little spotty compared to DV AVI's.
  2. I want to keep a copy of the raw, unedited video capture but that doesn't mean I'm willing to let all my editing work go to waste. When I started the project, my first thought was to capture and edit the movies entirely in Apple iMovie. But I was worried about tying myself to any commercial product that periodically has big releases, which might break compatibility. If I do all this work in iMovie, am I still going to be able to open the project in 10 years? Who knows. So, what I chose to do instead was edit on Windows, using AviSynth for processing and ffmpeg for encoding. I don't think that's a combination of software that I'll have any trouble running any time soon. And since they are all open source, I can include the software with my project.
  3. The final thing I wanted to be able to do was target a new format without repeating most of the work I had done. In programs like iMovie and Adobe Premiere, your project has a resolution associated with it and you are locked in. If I make a DVD one day and I want to make a 1080p BluRay the next, the two formats have different resolutions but up until the video is resized, all of the cleanup work I do on the original video will be the same. By using AviSynth, I was able to write one script to clean the raw video and another script to resize the output from the first script and get it ready for the encoder.

Capturing the Video

First things first. Before you can clean up your home videos, you have to get them onto your computer. There are a lot of devices out there that do this and the quality varies greatly. There are some devices out there that encode the video and send it to your computer as an MPEG stream. But MPEG codecs are lossy and the compression is usually pretty poor on that type of inexpensive digitizer.

A step in the right direction is to get a digitizer that allows you to capture DV video, which is lossless. I have a Pinnacle AV/DV but like a lot of consumer-grade digitizers, it can have some major problems with the audio synchronization. This happened because the digitizer uses "unlocked" audio. Theoretically, unlocked audio should never drift more than 1/3 of a frame (11.12 ms for NTSC video) but in reality, it can and does. For more information, see "unlocked audio: real life" at http://www.videouniversity.com/articles/dv-formats-everything-you-need-to-know. For me, capturing two hours at a time using the Pinnacle AV/DV and Studio 9, the audio would drift up to 2 or 3 seconds.

So, what you really want is a digitizer with locked audio, which tends to be more on the professional side. I went with a Canopus ADVC110. It cost more than the Pinnacle AV/DV, but unlike the AV/DV, the ADVC110 isn't a waste of money. : )

Editing

Once you have your videos captures, it's time to start cleaning them up. Below is an expanded view of the process used to encode the video as an MP4 file for use on a media player - a PlayStation 3 in this case.

And below is the code from all of the scripts, along with some explanations and tips.

Vid.avs

AviSource("Vid.avi")
Trim(55,217003,false)

ConvertToRGB32()
LoadVirtualDubPlugin("Smart.vdf", "SmartDeint", 1)
SmartDeint(0,3,8,100,0,0,0,0,1,2,1)

Crop(20,2,-20,-8) # Crop(left,top,-right,-bottom)

# This is a good place to adjust levels, saturation, etc.
  • The AviSource() function does exactly what it sounds like - it loads the file that you will be working with. Other functions like DirectShowSource() are useful when dealing with other formats.
  • When editing home video, use Trim() to cut off the part of the video between when you started recording on the computer and when you pressed play on your VCR. To find the frames I want, I open the original AVI in VirtualDub. Once I find the frame, I press Control-G to open the "jump to frame" dialog, where I can copy the frame number and paste it into my script.
  • ConvertToRGB32() - Each pixel in a video has a color value but there are a lot of different ways of describing and storing color. By default, the output from AviSource() uses the YV12 colorspace but the RGB colorspace seems to be the one most widely used by filters.
  • The next step is deinterlacing. Because of the way deinterlacing works, it's generally advisable to deinterlace as soon as possible, before making changes like cropping or color correction. Since deinterlacing is such a complex topic, I think I will discuss that more in-depth in another post. For now, I will just explain that this script uses my favorite deinterlacer, Donald Graft's Smart Deinterlacer. The problem is that it is a VirtualDub filter instead of a AviSynth filter. Luckily, AviSynth gives you way to use those filters using LoadVirtualDubPlugin(). You tell AviSynth where the VirtualDub filter is, give it a name and tell AviSynth to start a frame earlier since the filter looks at the previous frame when deinterlacing. Now you can call the filter by name. But what are all those parameters? Well, since VirtualDub is a GUI tool, it's not like the parameters for its filters are likely to be documented. So instead, load the AVS file you are working on in VirtualDub, add and setup the deinterlacing filter, then choose "Save Processing Settings" from the File menu. Open the file you saved with a text editor and your filter's parameters will be listed out for you. Use those values in your AviSynth script. This is a technique that I learned from the AviSynth website.
  • Now that you have a nice progressive video source, you can crop out the black border that might appear around the edges. This is called underscan, where the camcorder basically didn't make full use of each line when it was writing to the tape. Televisions compensate for this by overscanning, which essentially pushes the black border off the screen. The interesting thing about the Crop() function is that it behaves differently depending on whether its last two parameters are positive or negative. Negative values are treated as the number of pixels you want to trim off the right and bottom edges. To find out how much you want to crop, load the video in VirtualDub and play with its crop feature. It's also been my experience that the colors at the very edge of the image might be a little discolored and/or shift around as the video is played back. I find that distracting so I usually crop a little extra off.
  • After you have your video cropped down to a nice size, you can make any changes you need. Denoising and Levels() would be a good place to start but to my surprise, I didn't find it necessary for the video I was editing. If I had, I would have found the ideal levels using VirtualDub's levels tool, which will show you a histogram for not only the current frame, but the video as a whole.

Vid Native.avs

AviSource("Vid.avs")
LanczosResize(640,480)
ConvertToYV12()
  • AviSource() allows you to load not only AVI files but other AviSynth scripts. AviSynth starts another frame server for the AVS file and its output becomes our input.
  • AviSynth provides a lot of different algorithms for resizing but all you need to know is that the Lancroz algorithm is good for most purposes and AviSynth gives it to you as an easy-to-use function, LancrozResize().
  • The final step in this example is to convert back to the YV12 colorspace. This script is going to be loaded directly by ffmpeg and encoded with the H.264/x264 codec, which requires YV12 instead of RGB32, which we've been working in.

Vid.bat

ffmpeg.exe -i "Vid Native.avs" -vcodec libx264 -crf 20 -acodec libfaac -ab 160k -threads 0 "Vid.mp4"
This batch file calls ffmpeg to encode the video. Since there are a fair number of parameters and I will never remember them, I put them in a .bat file. The parameters are:
  • i: The input file. Even though ffmpeg runs on a variety of platforms and AviSynth 2.x is Windows-only, Windows ffmpeg builds are capable of using AVS files.
  • vcodec: I used x264 as the video codec. It's an open source H.264 implementation and H.264 video in an MP4 file seems to be fairly well supported by media players including Apple devices and PlayStation 3.
  • crf: From what I've found online, this is the recommended way of doing quality-based variable bitrate (VBR) encoding. Lower numbers mean high quality and bigger files. You can also do VBR encoding by specifying an average bitrate. I've used average bitrate encoding before for encoding DivX videos to completely fill a CD-R. However, there is always some variability in file size and my only restriction nowadays is that the files be smaller than 4GB so I can put them on FAT-formatted external drives.
  • acodec: I used the libfaac codec. It works well and pairing AAC audio with H.264 video seems pretty standard.
  • ab: Sets the audio bitrate to 160 kb/s. This might be overkill considering most AAC music you buy online is 128 kb/s but the extra 32 kb/s seems pretty trivial compared to the overall bitrate of the combined audio and video.
  • threads: I'm not sure if this is actually useful or if this is just misinformation that has been circulating around the internet but supposedly setting the number of threads to zero will cause ffmpeg to choose an appropriate value based on your computer and how many cores are present. Why this wouldn't be the default, I don't know.
  • The last parameter is the output file name. Ffmpeg picks the container type (MPEG-4) automatically based on the filename's extension.

So, there you have it - one batch file that sets off a chain reaction to convert a 28 GB DV file into a 4 GB MP4 file. But of course a media player is just one device you might want to watch your videos on...

DVD Authoring

Suppose you wanted to create a DVD instead. I've been a little underwhelmed by the quality of every DVD I've made from home video, but I will say that I got better results from DVD Flick than I did from commercial tools like Pinnacle Studio. DVD Flick is a free, open source DVD authoring program based on ffmpeg. Like ffmpeg on Windows, it also allows you to use AVS files for an input.

Authoring To Other Formats

Now suppose that you want to put your video on some other format and the authoring software you need doesn't support AviSynth scripts. What do you do? I would load the script in VirtualDub and save the output to an AVI that the authoring software can import. The only problem is that you will want to use lossless codecs for that intermediate file, which will make that intermediate file very large. And since all that data will be written to disk instead of passing directly to the authoring tool, the process will be much slower... not to mention that you will have to wait until VirtualDub finishes writing that intermediate file before you can load it up in your authoring software. It's an option though.