Updated 2013-04-09 03:06:55 by AMG

AMG: Here's a little program I wrote to help me correct audio/video synchronization problems using VirtualDub [1].

Most videos I record have some linear timing error between the audio and video, such that the audio is both delayed and slightly slow compared to the video. Over the course of long videos, this can add up to several minutes of synchronization error.

To correct, I use this program (or the equations therein) to compute the corrective audio skew (usually negative) and time stretch ratio (usually slightly less than one).

To determine the skew and stretch, this program requires manual correlation of two events between the audio and video tracks. In the video I look for a door slam or gunshot or audible menu manipulation, something that should have an immediate audio report, and I enter its video frame number. (VirtualDub makes it easy to single-step forwards and backwards.) In the audio I find the video frame number associated with the start of the event's corresponding audio, and I enter that as well. (This is best done using the Audio Display option, which only works for uncompressed audio, or use the DirectShow plugin [2].) And last, I enter the video frame rate.

For best results, these two events should be as far apart as possible, so pick something near the beginning and the end of the video. Also, it's much easier to correlate when the event happens at time with little to no background music or noise, so that you can quickly see the event impulse in the audio display. If you are recording video games, I recommend doing some menu manipulation to establish calibration marks at the beginning and end of the video, even if you cut them out of the final product.

When all these numbers are entered, the skew and stretch values are displayed, ready to be copied and pasted into VirtualDub. There are convenient Copy buttons for this purpose.

The skew value goes into Audio Interleaving Skew Correction Delay. The stretch value goes into Audio Filter Time Stretch. The latter is hard to get to: select Audio Full Processing and Audio Advanced Filtering; then select Audio Filters; then use Add to add Input, Time Stretch, and Output; then use Configure on Time Stretch.

Tip: VirtualDub 1.10.3 keeps the Skew setting even when changing video files, so you will have to remember to reset it to zero. Otherwise, if you open a freshly corrected audio file and play it back to test, the audio will be out of sync due to it applying the Skew twice.

To anybody coming to this page without knowledge of Tcl: you will need to get a Tcl interpreter to run this program. At the moment I suggest grabbing the KBS 0.4.4 build of Tcl/Tk 8.6, available here [3]. Then either drag'n'drop the script file (copied and pasted from below, save it to avsync.tcl) onto the interpreter executable you downloaded, or set up a permanent file association from *.tcl to your interpreter which you probably want to save off in C:\Program Files (x86)\Tcl . Or you can get ActiveTcl.
# A/V Sync Calculator for use with VirtualDub (http://virtualdub.org/)
# Andy Goth <[email protected]>
# http://wiki.tcl.tk/37897

package require Tcl 8.5
package require Tk

# Default frame rate.
set rate 29.97

# Configure window.
wm title . "A/V Sync Calculator"
wm resizable . 0 0
raise .

# Create widgets.
ttk::label .sample1-label -text "Sample 1"
ttk::label .sample2-label -text "Sample 2"
ttk::label .video-label -text Video
ttk::label .audio-label -text Audio
foreach param {video1 audio1 video2 audio2} {
    ttk::entry .$param -validate all -validatecommand {regexp {^\d*$} %P}\
            -width 8 -textvariable $param
ttk::separator .sep1
ttk::label .rate-label -text "Frame Rate"
ttk::entry .rate -validate all -validatecommand {regexp {^\d*\.?\d*$} %P}\
        -width 8 -textvariable rate
ttk::label .rate-unit -text fps
ttk::separator .sep2
ttk::label .skew-label -text Skew
ttk::entry .skew -state readonly -width 10 -textvariable skew
ttk::button .skew-copy -text Copy
ttk::label .stretch-label -text Stretch
ttk::entry .stretch -state readonly -width 10 -textvariable stretch
ttk::button .stretch-copy -text Copy

# Create traces.
foreach param {rate video1 audio1 video2 audio2} {
    trace add variable $param write {apply {{name elem operation} {
        global rate video1 audio1 video2 audio2 skew stretch

        # Initialize skew and stretch to empty string.
        set skew ""
        set stretch ""

        # Use a catch block in case of missing or invalid input.
        catch {
            # Ensure decimal interpretation even with leading zeroes.
            foreach {in out} {video1 v1 audio1 a1 video2 v2 audio2 a2} {
                scan [set $in] %d $out

            # Compute skew and stretch values.
            set stretch [expr {double($v2 - $v1) / ($a2 - $a1)}]
            set skew [expr {int(1000 * ($v1 / $stretch - $a1) / $rate)}]

# Program copy buttons.
foreach param {skew stretch} {
    .$param-copy configure -command [list apply {{param} {
        clipboard clear
        clipboard append -- [set ::$param]
    }} $param]

# Configure widget geometry.
grid x .video-label .audio-label -sticky ew
grid .sample1-label .video1 .audio1 -sticky ew
grid .sample2-label .video2 .audio2 -sticky ew
grid .sep1 - - -sticky ew
grid .rate-label .rate .rate-unit -sticky ew
grid .sep2 - - -sticky ew
grid .skew-label .skew .skew-copy -sticky ew
grid .stretch-label .stretch .stretch-copy -sticky ew
grid columnconfigure . 1 -weight 1
grid columnconfigure . 2 -weight 1

# vim: set sts=4 sw=4 tw=80 et ft=tcl: