commit 76824fb52cc307efc68674104e3711df100ff71b
Author: Mark Spieth <mspieth@digivation.com.au>
Date:   Tue Apr 27 07:51:51 2010 +1000

    smoother vsync with predictive frame skipping

diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index 42ad109..575bc50 100644
--- a/mythtv/libs/libmythtv/mythplayer.cpp
+++ b/mythtv/libs/libmythtv/mythplayer.cpp
@@ -236,6 +236,7 @@ MythPlayer::MythPlayer(bool muted)
       // Audio and video synchronization stuff
       videosync(NULL),              avsync_delay(0),
       avsync_adjustment(0),         avsync_avg(0),
+      avsync_predictor(0),          avsync_predictor_enabled(false),
       refreshrate(0),
       lastsync(false),              repeat_delay(0),
       // Time Code stuff
@@ -869,7 +870,7 @@ void MythPlayer::SetVideoParams(int width, int height, double fps,
         video_frame_rate = fps;
         float temp_speed = (play_speed == 0.0f) ?
             audio.GetStretchFactor() : play_speed;
-        frame_interval = (int)(1000000.0f / video_frame_rate / temp_speed);
+        SetFrameInterval(kScan_Progressive, 1.0 / (video_frame_rate * temp_speed));
     }
 
     if (videoOutput)
@@ -1598,6 +1599,31 @@ int MythPlayer::NextCaptionTrack(int mode)
     return NextCaptionTrack(nextmode);
 }
 
+void MythPlayer::SetFrameInterval(FrameScanType scan, double frame_period)
+{
+    frame_interval = (int)(1000000.0f * frame_period + 0.5f);
+    if (!avsync_predictor_enabled)
+        avsync_predictor = 0;
+    avsync_predictor_enabled = false;
+
+    VERBOSE(VB_PLAYBACK, LOC + QString("SetFrameInterval ps:%1 scan:%2")
+            .arg(play_speed).arg(scan)
+           );
+    if (play_speed < 1 || play_speed > 2 || refreshrate <= 0)
+        return;
+
+    avsync_predictor_enabled = ((frame_interval-(frame_interval/200)) < refreshrate);
+}
+
+void MythPlayer::ResetAVSync(void)
+{
+    avsync_avg = 0;
+    if (!avsync_predictor_enabled || avsync_predictor >= refreshrate)
+        avsync_predictor = 0;
+    prevtc = 0;
+    VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V sync reset");
+}
+
 void MythPlayer::InitAVSync(void)
 {
     videosync->Start();
@@ -1620,17 +1646,32 @@ void MythPlayer::InitAVSync(void)
                        .arg(refreshrate).arg(frame_interval);
         VERBOSE(VB_PLAYBACK, LOC + msg);
 
+        SetFrameInterval(m_scan, 1.0 / (video_frame_rate * play_speed));
+
         // try to get preferential scheduling, but ignore if we fail to.
         myth_nice(-19);
     }
 }
 
+int64_t MythPlayer::AVSyncGetAudiotime(void)
+{
+    int64_t currentaudiotime = 0;
+    if (normal_speed)
+    {
+        currentaudiotime = audio.GetAudioTime();
+    }
+    return currentaudiotime;
+}
+
 #define MAXDIVERGE  3.0f
 #define DIVERGELIMIT 30.0f
 void MythPlayer::AVSync(bool limit_delay)
 {
     float diverge = 0.0f;
     int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval;
+    int vsync_delay_clock = 0;
+    int64_t currentaudiotime = 0;
+
     // attempt to reduce fps for standalone PIP
     if (player_ctx->IsPIP() && framesPlayed % 2)
     {
@@ -1665,15 +1706,39 @@ void MythPlayer::AVSync(bool limit_delay)
     if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
         ps = kScan_Progressive;
 
+    bool dropframe = false;
+    QString dbg;
+
+    if (avsync_predictor_enabled) // && !prebuffering)
+    {
+        avsync_predictor += frame_interval;
+        if (avsync_predictor >= refreshrate)
+        {
+            int refreshperiodsinframe = avsync_predictor/refreshrate;
+            avsync_predictor -= refreshrate * refreshperiodsinframe;
+        }
+        else
+        {
+            dropframe = true;
+            dbg = "A/V predict drop frame, ";
+        }
+    }
+
     if (diverge < -MAXDIVERGE)
     {
+        dropframe = true;
         // If video is way behind of audio, adjust for it...
-        QString dbg = QString("Video is %1 frames behind audio (too slow), ")
+        dbg = QString("Video is %1 frames behind audio (too slow), ")
             .arg(-diverge);
+    }
 
+    if (dropframe)
+    {
         // Reset A/V Sync
         lastsync = true;
 
+        currentaudiotime = AVSyncGetAudiotime();
+
         if (buffer && !using_null_videoout &&
             videoOutput->hasHWAcceleration() &&
            !videoOutput->IsSyncLocked())
@@ -1698,10 +1763,11 @@ void MythPlayer::AVSync(bool limit_delay)
         if (buffer)
             videoOutput->PrepareFrame(buffer, ps, osd);
 
-        VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, QString("AVSync waitforframe %1 %2")
+        VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + QString("AVSync waitforframe %1 %2")
                 .arg(avsync_adjustment).arg(m_double_framerate));
-        videosync->WaitForFrame(frameDelay + avsync_adjustment + repeat_delay);
-        VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, "AVSync show");
+        vsync_delay_clock = videosync->WaitForFrame(frameDelay + avsync_adjustment + repeat_delay);
+        currentaudiotime = AVSyncGetAudiotime();
+        VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + "AVSync show");
         videoOutput->Show(ps);
 
         if (videoOutput->IsErrored())
@@ -1730,23 +1796,33 @@ void MythPlayer::AVSync(bool limit_delay)
                 videoOutput->PrepareFrame(buffer, ps, osd);
 
             // Display the second field
-            videosync->WaitForFrame(frameDelay + avsync_adjustment);
+            //videosync->AdvanceTrigger();
+            vsync_delay_clock = videosync->WaitForFrame(frameDelay + avsync_adjustment);
             videoOutput->Show(ps);
         }
 
         repeat_delay = frame_interval * buffer->repeat_pict * 0.5;
 
         if (repeat_delay)
-            VERBOSE(VB_TIMESTAMP, QString("A/V repeat_pict, adding %1 repeat "
+            VERBOSE(VB_TIMESTAMP, LOC + QString("A/V repeat_pict, adding %1 repeat "
                     "delay").arg(repeat_delay));
     }
     else
     {
-        videosync->WaitForFrame(frameDelay);
+        vsync_delay_clock = videosync->WaitForFrame(frameDelay);
+        currentaudiotime = AVSyncGetAudiotime();
     }
 
     if (output_jmeter)
-        output_jmeter->RecordCycleTime();
+    {
+        if (output_jmeter->RecordCycleTime())
+        {
+            VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V avsync_delay: %1, "
+                    "avsync_avg: %2")
+                    .arg(avsync_delay / 1000).arg(avsync_avg / 1000)
+                    );
+        }
+    }
 
     avsync_adjustment = 0;
 
@@ -1754,7 +1830,8 @@ void MythPlayer::AVSync(bool limit_delay)
     {
         // If audio is way behind of video, adjust for it...
         // by cutting the frame rate in half for the length of this frame
-        avsync_adjustment = refreshrate;
+        //avsync_adjustment = refreshrate;
+        avsync_adjustment = frame_interval;
         lastsync = true;
         VERBOSE(VB_PLAYBACK, LOC +
                 QString("Video is %1 frames ahead of audio,\n"
@@ -1764,53 +1841,70 @@ void MythPlayer::AVSync(bool limit_delay)
     if (audio.HasAudioOut() && normal_speed)
     {
         int64_t currentaudiotime = audio.GetAudioTime();
-        VERBOSE(VB_TIMESTAMP, QString(
+        VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString(
                     "A/V timecodes audio %1 video %2 frameinterval %3 "
-                    "avdel %4 avg %5 tcoffset %6")
+                    "avdel %4 avg %5 tcoffset %6"
+                    " avp %7 avpen %8"
+                    " avdc %9"
+                    )
                 .arg(currentaudiotime)
                 .arg(buffer->timecode)
                 .arg(frame_interval)
-                .arg(buffer->timecode - currentaudiotime)
+                .arg(buffer->timecode - currentaudiotime - (int)(vsync_delay_clock*audio.GetStretchFactor()+500)/1000)
                 .arg(avsync_avg)
                 .arg(tc_wrap[TC_AUDIO])
+                .arg(avsync_predictor)
+                .arg(avsync_predictor_enabled)
+                .arg(vsync_delay_clock)
                  );
         if (currentaudiotime != 0 && buffer->timecode != 0)
         { // currentaudiotime == 0 after a seek
             // The time at the start of this frame (ie, now) is given by
             // last->timecode
-            int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000);
-            prevtc = buffer->timecode;
-            //cerr << delta << " ";
-
-            // If the timecode is off by a frame (dropped frame) wait to sync
-            if (delta > (int) frame_interval / 1200 &&
-                delta < (int) frame_interval / 1000 * 3 &&
-                prevrp == 0)
+            if (prevtc != 0)
             {
-                // wait an extra frame interval
-                avsync_adjustment += frame_interval;
+                int delta = (int)((buffer->timecode - prevtc)/play_speed) - (frame_interval / 1000);
+                // If the timecode is off by a frame (dropped frame) wait to sync
+                if (delta > (int) frame_interval / 1200 &&
+                    delta < (int) frame_interval / 1000 * 3 &&
+                    prevrp == 0)
+                {
+                    // wait an extra frame interval
+                    VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V delay %1").arg(delta));
+                    avsync_adjustment += frame_interval;
+                    //videosync->AdvanceTrigger();
+                    //if (m_double_framerate)
+                    //    videosync->AdvanceTrigger();
+                }
             }
+            prevtc = buffer->timecode;
             prevrp = buffer->repeat_pict;
 
-            avsync_delay = (buffer->timecode - currentaudiotime) * 1000;//usec
+            avsync_delay = (buffer->timecode - currentaudiotime) * 1000 - (int)(vsync_delay_clock*audio.GetStretchFactor());  //usec
             // prevents major jitter when pts resets during dvd title
             if (avsync_delay > 2000000 && limit_delay)
                 avsync_delay = 90000;
             avsync_avg = (avsync_delay + (avsync_avg * 3)) / 4;
 
+            int avsync_used = avsync_avg;
+            if (labs(avsync_used) > labs(avsync_delay))
+                avsync_used = avsync_delay;
+
             /* If the audio time codes and video diverge, shift
                the video by one interlaced field (1/2 frame) */
             if (!lastsync)
             {
-                if (avsync_avg > frame_interval * 3 / 2)
+                if (avsync_used > refreshrate)
                 {
                     avsync_adjustment += refreshrate;
-                    lastsync = true;
+                    //lastsync = true;
+                    VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high extend");
                 }
-                else if (avsync_avg < 0 - frame_interval * 3 / 2)
+                else if (avsync_used < 0 - refreshrate)
                 {
                     avsync_adjustment -= refreshrate;
-                    lastsync = true;
+                    //lastsync = true;
+                    VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + "A/V avg high skip");
                 }
             }
             else
@@ -1818,9 +1912,13 @@ void MythPlayer::AVSync(bool limit_delay)
         }
         else
         {
-            avsync_avg = 0;
+            ResetAVSync();
         }
     }
+    else
+    {
+        VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString("A/V no sync proc ns:%1").arg(normal_speed));
+    }
 }
 
 void MythPlayer::DisplayPauseFrame(void)
@@ -3066,8 +3164,7 @@ void MythPlayer::ChangeSpeed(void)
     }
 
     float temp_speed = (play_speed == 0.0) ? audio.GetStretchFactor() : play_speed;
-    frame_interval = (int) (1000000.0f * ffrew_skip / video_frame_rate /
-                            temp_speed);
+    SetFrameInterval(m_scan, ffrew_skip / (video_frame_rate * temp_speed));
 
     VERBOSE(VB_PLAYBACK, LOC + "Play speed: " +
             QString("rate: %1 speed: %2 skip: %3 => new interval %4")
@@ -3350,6 +3447,7 @@ void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
     commBreakMap.SetTracker(framesPlayed);
     commBreakMap.ResetLastSkip();
     needNewPauseFrame = true;
+    ResetAVSync();
 }
 
 void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget,
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
index e58bb90..602b8a6 100644
--- a/mythtv/libs/libmythtv/mythplayer.h
+++ b/mythtv/libs/libmythtv/mythplayer.h
@@ -488,6 +488,9 @@ class MPUBLIC MythPlayer
     void  WrapTimecode(int64_t &timecode, TCTypes tc_type);
     void  InitAVSync(void);
     virtual void AVSync(bool limit_delay = false);
+    void  ResetAVSync(void);
+    int64_t AVSyncGetAudiotime(void);
+    void  SetFrameInterval(FrameScanType scan, double speed);
     void  FallbackDeint(void);
     void  CheckExtraAudioDecode(void);
 
@@ -671,6 +674,8 @@ class MPUBLIC MythPlayer
     int        avsync_delay;
     int        avsync_adjustment;
     int        avsync_avg;
+    int        avsync_predictor;
+    bool       avsync_predictor_enabled;
     int        refreshrate;
     bool       lastsync;
     bool       decode_extra_audio;
