diff -urN mythtv-0.13.vanilla/libs/libmythtv/NuppelVideoPlayer.cpp mythtv-0.13/libs/libmythtv/NuppelVideoPlayer.cpp
--- mythtv-0.13.vanilla/libs/libmythtv/NuppelVideoPlayer.cpp	2003-12-06 00:01:45.000000000 +0000
+++ mythtv-0.13/libs/libmythtv/NuppelVideoPlayer.cpp	2004-01-02 18:54:48.000000000 +0000
@@ -11,6 +11,9 @@
 #include <qsqldatabase.h>
 #include <qmap.h>
 
+#include <linux/rtc.h>
+#include <sys/ioctl.h>
+
 #include <iostream>
 using namespace std;
 
@@ -155,6 +158,10 @@
 
     limitKeyRepeat = false;
 
+    warplbuff = NULL;
+    warprbuff = NULL;
+    warpbuffsize = 0;
+
 }
 
 NuppelVideoPlayer::~NuppelVideoPlayer(void)
@@ -876,6 +883,248 @@
     }
 }
 
+#define MAXWARPDIFF 0.0005 // Max amount the warpfactor can change in 1 frame
+#define WARPMULTIPLIER 1000000000 // How much do we multiply the warp by when 
+                                  //storing it in an integer
+#define WARPAVLEN (video_frame_rate * 600) // How long to average the warp over
+#define RTCRATE 1024 // RTC frequency if we have no vsync
+#define WARPCLIP    0.1 // How much we allow the warp to deviate from 1 (normal speed)
+#define MAXDIVERGE  20  // Maximum number of frames of A/V divergence allowed
+                        // before dropping or extending video frames to compensate
+
+float NuppelVideoPlayer::WarpFactor(void) 
+{
+    // Calculate a new warp factor
+    float   divergence;
+    float   rate;
+    float   newwarp = 1;
+    float   warpdiff;
+
+    // Number of frames the audio is out by
+    divergence = (float)avsync_avg / (float)frame_interval;
+    // Number of frames divergence is changing by per frame
+    rate = (float)(avsync_avg - avsync_oldavg) / (float)frame_interval;
+    avsync_oldavg = avsync_avg;
+    newwarp = warpfactor_avg * (1 + ((divergence + rate) / 125));
+
+    // Clip the amount changed so we don't get big frequency variations
+    warpdiff = newwarp / warpfactor;
+    if (warpdiff > (1 + MAXWARPDIFF)) 
+    {
+        newwarp = warpfactor * (1 + MAXWARPDIFF);
+    } 
+    else if (warpdiff < (1 - MAXWARPDIFF)) 
+    {
+        newwarp = warpfactor * (1 - MAXWARPDIFF);
+    }
+    
+    warpfactor = newwarp;
+
+    // Clip final warp factor
+    if (warpfactor < (1 - WARPCLIP)) 
+        warpfactor = 1 - WARPCLIP;
+    else if (warpfactor > (1 + (WARPCLIP * 2))) 
+        warpfactor = 1 + (WARPCLIP * 2);
+
+    // Keep a 10 minute average
+    warpfactor_avg = (warpfactor + (warpfactor_avg * (WARPAVLEN - 1))) / 
+                      WARPAVLEN;
+
+//    cerr << "Divergence: " << divergence << "  Rate: " << rate 
+//         << "  Warpfactor: " << warpfactor << "  warpfactor_avg: " 
+//         << warpfactor_avg << endl;
+    return divergence;
+}
+
+void NuppelVideoPlayer::InitVTAVSync(void) 
+{
+    QString timing_type = "next trigger";
+
+    //warpfactor_avg = 1;
+    warpfactor_avg = gContext->GetNumSetting("WarpFactor", 0);
+    if (warpfactor_avg) 
+        warpfactor_avg /= WARPMULTIPLIER;
+    else 
+        warpfactor_avg = 1;
+    // Reset the warpfactor if it's obviously bogus
+    if (warpfactor_avg < (1 - WARPCLIP)) 
+        warpfactor_avg = 1;
+    if (warpfactor_avg > (1 + (WARPCLIP * 2))) 
+        warpfactor_avg = 1;
+
+    warpfactor = warpfactor_avg;
+    avsync_oldavg = 0;
+    rtcfd = -1;
+    if (!disablevideo) 
+    {
+        int ret = vsync_init();
+
+        refreshrate = videoOutput->GetRefreshRate();
+        if (refreshrate <= 0) 
+            refreshrate = frame_interval;
+
+        if (ret > 0) 
+        {
+            hasvsync = true;
+
+            if ( ret == 1 ) timing_type = "nVidia polling";
+            vsynctol = refreshrate / 4; 
+            // How far out can the vsync be for us to use it?
+        } 
+        else 
+        {
+            rtcfd = open("/dev/rtc", O_RDONLY);
+            if (rtcfd >= 0) 
+            {
+                if ((ioctl(rtcfd, RTC_IRQP_SET, RTCRATE) < 0) || 
+                    (ioctl(rtcfd, RTC_PIE_ON, 0) < 0)) 
+                {
+                    close(rtcfd);
+                    rtcfd = -1;
+                } 
+                else 
+                {
+                    vsynctol = 1000000 / RTCRATE; 
+                    // How far out can the interrupt be for us to use it?
+                    timing_type = "/dev/rtc";
+                }
+            }
+        }
+
+        nice(-19);
+
+        QString msg = QString("Video timing method: %1").arg(timing_type);
+        VERBOSE(VB_PLAYBACK, msg);
+        msg = QString("Refresh rate: %1, frame interval: %2") 
+                     .arg(refreshrate).arg(frame_interval);
+        VERBOSE(VB_PLAYBACK, msg);
+    }
+}
+
+void NuppelVideoPlayer::VTAVSync(void)
+{
+    float           diverge;
+    unsigned long   rtcdata;
+    
+    VideoFrame *buffer = videoOutput->GetLastShownFrame();
+    if (!buffer) 
+    {
+        cerr << "No video buffer, error error\n";
+        return;
+    }
+
+    if (normal_speed)
+        diverge = WarpFactor();
+    else
+        diverge = 0;
+    delay = UpdateDelay(&nexttrigger);
+    // If video is way ahead of audio, drop some frames until we're close again.
+    // If we're close to a vsync then we'll display a frame to keep the picture updated.
+    if ((diverge < -MAXDIVERGE) && (delay < vsynctol)) cerr << "A/V diverged by " << diverge << " frames, dropping frame to keep audio in sync" << endl;
+    else if (disablevideo) 
+    {
+        delay = UpdateDelay(&nexttrigger);
+        if (delay > 0) 
+            usleep(delay);
+    } 
+    else 
+    {
+        // if we get here, we're actually going to do video output
+        delay = UpdateDelay(&nexttrigger);
+        if (buffer) 
+            videoOutput->PrepareFrame(buffer);
+
+        if ((hasvsync) || (rtcfd >= 0)) 
+        {
+            delay = UpdateDelay(&nexttrigger);
+            if (delay < (0 - vsynctol)) 
+                cerr << "Late frame!  Delay: " << delay << endl;
+
+            if (hasvsync) 
+                vsync_wait_for_retrace();
+            else 
+                read(rtcfd,&rtcdata,sizeof(rtcdata));
+
+            delay = UpdateDelay(&nexttrigger);
+            while (delay > vsynctol) 
+            {
+                vsync_wait_for_retrace();
+                delay = UpdateDelay(&nexttrigger);
+            }
+        } 
+        else 
+        {    
+            // No vsync _or_ RTC, fall back to usleep() (yuck)
+            delay = UpdateDelay(&nexttrigger);
+            usleep(delay);
+        }
+        
+        // Reset the frame timer to current time since we're trusting the 
+        // video timing
+        gettimeofday(&nexttrigger, NULL);
+        
+        // Display the frame
+        videoOutput->Show();
+    }
+    
+    if (output_jmeter && output_jmeter->RecordCycleTime()) 
+        cout << "avsync_avg: " << avsync_avg / 1000 
+             << ", warpfactor: " << warpfactor 
+             << ", warpfactor_avg: " << warpfactor_avg << endl;
+
+    // Schedule next frame
+    nexttrigger.tv_usec += frame_interval;
+    if (diverge > MAXDIVERGE) {
+        // Audio is way ahead of the video - cut the frame rate
+        // until it's almost in sync
+        cerr << "A/V diverged by " << diverge << " frames, extending frame to keep audio in sync" << endl;
+        nexttrigger.tv_usec += (frame_interval * ((int)diverge / MAXDIVERGE));
+    };
+    NormalizeTimeval(&nexttrigger);
+    
+    if (audioOutput && normal_speed) 
+    {
+        // ms, same scale as timecodes
+        lastaudiotime = audioOutput->GetAudiotime();
+        if (lastaudiotime != 0) { // lastaudiotime = 0 after a seek
+            // The time at the start of this frame (ie, now) is given by 
+            // last->timecode
+            avsync_delay = (buffer->timecode - lastaudiotime) * 1000; // uSecs
+            avsync_avg = (avsync_delay + (avsync_avg * 3)) / 4;
+        } 
+        else 
+        {
+            avsync_avg = 0;
+            avsync_oldavg = 0;
+        }
+    }
+}
+
+void NuppelVideoPlayer::ShutdownVTAVSync(void) 
+{
+    if (hasvsync) 
+        vsync_shutdown();
+    if (hasvgasync) 
+        vgasync_cleanup();
+    gContext->SaveSetting("WarpFactor", (int)(warpfactor_avg * WARPMULTIPLIER));
+
+    if (warplbuff) {
+        free(warplbuff);
+        warplbuff = NULL;
+    }
+    if (warprbuff) {
+        free(warprbuff);
+        warprbuff = NULL;
+    }
+    warpbuffsize = 0;
+
+    if (rtcfd >= 0)
+    {
+        close(rtcfd);
+        rtcfd = -1;
+    }
+}
+
 void NuppelVideoPlayer::InitExAVSync(void)
 {
     QString timing_type = "next trigger";
@@ -1191,6 +1440,7 @@
 
     reducejitter = gContext->GetNumSetting("ReduceJitter", 0);
     experimentalsync = gContext->GetNumSetting("ExperimentalAVSync", 0);
+    usevideotimebase = gContext->GetNumSetting("UseVideoTimebase", 0);
 
     if ((print_verbose_messages & VB_PLAYBACK) != 0)
         output_jmeter = new Jitterometer("video_output", 100);
@@ -1201,7 +1451,9 @@
 
     gettimeofday(&nexttrigger, NULL);
 
-    if (experimentalsync)
+    if (usevideotimebase)
+        InitVTAVSync();
+    else if (experimentalsync)
         InitExAVSync();
 
     while (!eof && !killvideo)
@@ -1262,7 +1514,9 @@
 
         videoOutput->ProcessFrame(frame, osd, videoFilters, pipplayer);
 
-        if (experimentalsync)
+        if (usevideotimebase)
+            VTAVSync();
+        else if (experimentalsync)
             ExAVSync();
         else 
             OldAVSync();
@@ -1277,7 +1531,9 @@
     delete videoOutput;
     videoOutput = NULL;
 
-    if (experimentalsync)
+    if (usevideotimebase)
+        ShutdownVTAVSync();
+    else if (experimentalsync)
         ShutdownExAVSync();
 }
 
@@ -1608,8 +1864,56 @@
 void NuppelVideoPlayer::AddAudioData(char *buffer, int len, long long timecode)
 {
     if (audioOutput)
-        audioOutput->AddSamples(buffer, len / (audio_channels * audio_bits / 8),
-                                timecode);
+    {
+        if (usevideotimebase)
+        {
+            int samples;
+            short int * newbuffer;
+            float incount = 0;
+            int outcount;
+            int samplesize;
+            int newsamples;
+            int newlen;
+
+            samplesize = audio_channels * audio_bits / 8;
+            samples = len / samplesize;
+            newsamples = (int)(samples / warpfactor);
+            newlen = newsamples * samplesize;
+
+            // We use the left warp buffer to store the new data.
+            // If it isn't big enough it is resized.
+            if ((warpbuffsize < newlen) || (! warplbuff))
+            {
+                if (warprbuff) {
+                    // Make sure this isn't allocated since we're only
+                    // resizing 1 buffer.
+                    free(warprbuff);
+                    warprbuff = NULL;
+                };
+                newbuffer = (short int *) realloc(warplbuff,newlen);
+                if (! newbuffer) {
+                    cerr << "Urk!  Couldn't allocate warped audio buffer!" << endl;
+                    return;
+                };
+                warplbuff = newbuffer;
+                warpbuffsize = newlen;
+            } else newbuffer = warplbuff;
+            
+            for (incount = 0, outcount = 0; 
+                 (incount < samples) && (outcount < newsamples); 
+                 outcount++, incount += warpfactor) 
+            {
+                memcpy(((char *)newbuffer) + (outcount * samplesize), 
+                       buffer + (((int)incount) * samplesize), samplesize);
+            }
+            samples = outcount;
+            audioOutput->AddSamples((char *)newbuffer, samples, timecode);
+        }
+        else
+            audioOutput->AddSamples(buffer, len / 
+                                    (audio_channels * audio_bits / 8),
+                                    timecode);
+    }
 }
 
 void NuppelVideoPlayer::AddAudioData(short int *lbuffer, short int *rbuffer,
@@ -1618,6 +1922,52 @@
     if (audioOutput)
     {
         char *buffers[] = {(char *)lbuffer, (char *)rbuffer};
+        if (usevideotimebase)
+        {
+            short int *newlbuffer;
+            short int *newrbuffer;
+            float incount = 0;
+            int outcount;
+            int newlen;
+            int newsamples;
+
+            newsamples = (int)(samples / warpfactor);
+            newlen = newsamples * sizeof(short int);
+
+            // We resize the buffers if they aren't big enough
+            if ((warpbuffsize < newlen) || (! warplbuff) || (! warprbuff))
+            {
+                newlbuffer = (short int *) realloc(warplbuff,newlen);
+                if (! newlbuffer) {
+                    cerr << "Urk!  Couldn't allocate left warped audio buffer!" << endl;
+                    return;
+                };
+                warplbuff = newlbuffer;
+                newrbuffer = (short int *) realloc(warprbuff,newlen);
+                if (! newrbuffer) {
+                    cerr << "Urk!  Couldn't allocate right warped audio buffer!" << endl;
+                    return;
+                };
+                warprbuff = newrbuffer;
+                warpbuffsize = newlen;
+            } else {
+                newlbuffer = warplbuff;
+                newrbuffer = warprbuff;
+            };
+
+            buffers[0] = (char *)newlbuffer;
+            buffers[1] = (char *)newlbuffer;
+
+            for (incount = 0, outcount = 0; 
+                 (incount < samples) && (outcount < newsamples); 
+                 outcount++, incount += warpfactor) 
+            {
+                newlbuffer[outcount] = lbuffer[(int)incount];
+                newrbuffer[outcount] = rbuffer[(int)incount];
+            }
+
+            samples = outcount;
+        }
         audioOutput->AddSamples(buffers, samples, timecode);
     }
 }
diff -urN mythtv-0.13.vanilla/libs/libmythtv/NuppelVideoPlayer.h mythtv-0.13/libs/libmythtv/NuppelVideoPlayer.h
--- mythtv-0.13.vanilla/libs/libmythtv/NuppelVideoPlayer.h	2003-11-26 21:43:02.000000000 +0000
+++ mythtv-0.13/libs/libmythtv/NuppelVideoPlayer.h	2004-01-02 18:55:21.000000000 +0000
@@ -150,6 +150,7 @@
 
     void DrawSlice(VideoFrame *frame, int x, int y, int w, int h);
 
+    float WarpFactor(void);
     void AddAudioData(char *buffer, int len, long long timecode);
     void AddAudioData(short int *lbuffer, short int *rbuffer, int samples,
                       long long timecode);
@@ -384,10 +385,18 @@
     int delay;
     int avsync_delay;
     int avsync_avg;
+    int avsync_oldavg;
     int refreshrate;
     int frame_interval; // always adjusted for play_speed
     float play_speed;  
     bool normal_speed;
+    float warpfactor;
+    float warpfactor_avg;
+    int rtcfd;
+    int vsynctol;
+    short int * warplbuff;
+    short int * warprbuff;
+    int warpbuffsize;
  
     bool delay_clipping;
     struct timeval nexttrigger, now;
@@ -398,6 +407,9 @@
 
     Jitterometer *output_jmeter;
 
+    void InitVTAVSync(void);
+    void VTAVSync(void);
+    void ShutdownVTAVSync(void);
     void InitExAVSync(void);
     void OldAVSync(void);
     void ExAVSync(void);
@@ -405,6 +417,7 @@
 
     bool reducejitter;
     bool experimentalsync;
+    bool usevideotimebase;
 
     bool limitKeyRepeat;
 
diff -urN mythtv-0.13.vanilla/programs/mythfrontend/globalsettings.cpp mythtv-0.13/programs/mythfrontend/globalsettings.cpp
--- mythtv-0.13.vanilla/programs/mythfrontend/globalsettings.cpp	2003-12-10 21:35:46.000000000 +0000
+++ mythtv-0.13/programs/mythfrontend/globalsettings.cpp	2004-01-02 18:44:52.000000000 +0000
@@ -597,6 +597,17 @@
     };
 };
 
+class UseVideoTimebase: public CheckBoxSetting, public GlobalSetting {
+public:
+    UseVideoTimebase():
+        GlobalSetting("UseVideoTimebase") {
+        setLabel(QObject::tr("Use video as timebase"));
+        setValue(false);
+        setHelpText(QObject::tr("Use the video as the timebase and warp "
+                    "the audio to keep it in sync."));
+    };
+};
+
 class DefaultCCMode: public CheckBoxSetting, public GlobalSetting {
 public:
     DefaultCCMode():
@@ -1403,6 +1414,7 @@
     general->addChild(new CustomFilters());
     general->addChild(new ReduceJitter());
     general->addChild(new ExperimentalSync());
+    general->addChild(new UseVideoTimebase());
     general->addChild(new DecodeExtraAudio());
     general->addChild(new PIPLocation());
     addChild(general);
