[mythtv] [PATCH] Video timebase / audio warping fixes

steve at nexusuk.org steve at nexusuk.org
Fri Jan 2 14:39:28 EST 2004


The attached patch against the current CVS version fixes some bugs in the 
new experimental VTAVSync:

- Buffers for the warped audio are nolonger allocated for each chunk of 
  audio, they are allocated once and grown if they are too small.
- The amount audio can be warped is now clipped to slightly more sane 
  limits and this is now a #define called WARPCLIP
- If audio gets more than MAXDIVERGE (20) frames out of sync then frames 
  will now be dropped or extended to try and get the sync back by 
  brute-force (since large divergences cannot be corrected within a 
  reasonable time by warping the audio).
- Vsync tollerance reduced to 1/4 of the refresh rate instead of 1/2 since 
  it could be possible to get 2 vsyncs per refresh when running in an 
  interlaced mode.
- Nolonger bothers calculating a new warpfactor or brute-force 
  correcting divergence when the video isn't playing at normal speed 
  since this was screwing up when playing the video at high speed.
- Removed some code for the /dev/rtc support that was left in there by 
  mistake and would never get executed anyway.
- Fixed the audio warping code for interleaved audio (so PCM audio now 
  works properly)

It still has a few small problems recovering sync after playing a video at
non-normal speed (this doesn't affect skipping) which I need to work on.

-- 

 - Steve                                             http://www.nexusuk.org/

     Servatis a periculum, servatis a maleficum - Whisper, Evanescence
-------------- next part --------------
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/NuppelVideoPlayer.cpp,v
retrieving revision 1.309
diff -u -r1.309 NuppelVideoPlayer.cpp
--- libs/libmythtv/NuppelVideoPlayer.cpp	1 Jan 2004 22:05:07 -0000	1.309
+++ libs/libmythtv/NuppelVideoPlayer.cpp	2 Jan 2004 18:32:26 -0000
@@ -162,6 +162,10 @@
 
     limitKeyRepeat = false;
 
+    warplbuff = NULL;
+    warprbuff = NULL;
+    warpbuffsize = 0;
+
 }
 
 NuppelVideoPlayer::~NuppelVideoPlayer(void)
@@ -896,6 +900,9 @@
                                   //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) 
 {
@@ -916,24 +923,20 @@
     warpdiff = newwarp / warpfactor;
     if (warpdiff > (1 + MAXWARPDIFF)) 
     {
-//        cerr << "Clipped.  Warpdiff: " << warpdiff << "  warp: " << newwarp;
         newwarp = warpfactor * (1 + MAXWARPDIFF);
-//        cerr << "  clipped to: " << newwarp << endl;
     } 
     else if (warpdiff < (1 - MAXWARPDIFF)) 
     {
-//        cerr << "Clipped.  Warpdiff: " << warpdiff << "  warp: " << newwarp;
         newwarp = warpfactor * (1 - MAXWARPDIFF);
-//        cerr << "  clipped to: " << newwarp << endl;
     }
     
     warpfactor = newwarp;
 
     // Clip final warp factor
-    if (warpfactor < 0.5) 
-        warpfactor = 0.5;
-    else if (warpfactor > 2) 
-        warpfactor = 2;
+    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))) / 
@@ -956,9 +959,9 @@
     else 
         warpfactor_avg = 1;
     // Reset the warpfactor if it's obviously bogus
-    if (warpfactor_avg < 0.5) 
+    if (warpfactor_avg < (1 - WARPCLIP)) 
         warpfactor_avg = 1;
-    if (warpfactor_avg > 2) 
+    if (warpfactor_avg > (1 + (WARPCLIP * 2))) 
         warpfactor_avg = 1;
 
     warpfactor = warpfactor_avg;
@@ -977,7 +980,7 @@
             hasvsync = true;
 
             if ( ret == 1 ) timing_type = "nVidia polling";
-            vsynctol = refreshrate / 2; 
+            vsynctol = refreshrate / 4; 
             // How far out can the vsync be for us to use it?
         } 
         else 
@@ -1022,10 +1025,15 @@
         return;
     }
 
-    diverge = WarpFactor();
-    /*if (diverge < -5) cerr << "Dropping frame to keep audio in sync :(" << endl;
-    else*/ 
-    if (disablevideo) 
+    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) 
@@ -1056,10 +1064,6 @@
                 delay = UpdateDelay(&nexttrigger);
             }
         } 
-        else if (rtcfd >= 0) 
-        {
-            // No vsync - use the RTC instead
-        } 
         else 
         {    
             // No vsync _or_ RTC, fall back to usleep() (yuck)
@@ -1077,12 +1081,17 @@
     
     if (output_jmeter && output_jmeter->RecordCycleTime()) 
         cout << "avsync_avg: " << avsync_avg / 1000 
-             << ", avsync_oldavg: " << avsync_oldavg / 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) 
@@ -1111,6 +1120,16 @@
         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);
@@ -1866,25 +1885,46 @@
         if (usevideotimebase)
         {
             int samples;
-            char * newbuffer;
+            short int * newbuffer;
             float incount = 0;
             int outcount;
             int samplesize;
+            int newsamples;
+            int newlen;
 
             samplesize = audio_channels * audio_bits / 8;
             samples = len / samplesize;
-            newbuffer = (char *)malloc(len * 2);
+            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 < (samples * 2)); 
+                 (incount < samples) && (outcount < newsamples); 
                  outcount++, incount += warpfactor) 
             {
-                memcpy(newbuffer + (outcount * samplesize), 
-                       buffer + ((int)incount * samplesize), samplesize);
+                memcpy(((char *)newbuffer) + (outcount * samplesize), 
+                       buffer + (((int)incount) * samplesize), samplesize);
             }
-
             samples = outcount;
-            audioOutput->AddSamples(buffer, samples, timecode);
-            free(newbuffer);
+            audioOutput->AddSamples((char *)newbuffer, samples, timecode);
         }
         else
             audioOutput->AddSamples(buffer, len / 
@@ -1905,14 +1945,38 @@
             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;
+            };
 
-            newlbuffer = (short int *)malloc(sizeof(short int) * samples * 2);
-            newrbuffer = (short int *)malloc(sizeof(short int) * samples * 2);
             buffers[0] = (char *)newlbuffer;
             buffers[1] = (char *)newlbuffer;
 
             for (incount = 0, outcount = 0; 
-                 (incount < samples) && (outcount < (samples * 2)); 
+                 (incount < samples) && (outcount < newsamples); 
                  outcount++, incount += warpfactor) 
             {
                 newlbuffer[outcount] = lbuffer[(int)incount];
@@ -1920,12 +1984,8 @@
             }
 
             samples = outcount;
-            audioOutput->AddSamples(buffers, samples, timecode);
-            free(newlbuffer);
-            free(newrbuffer);
         }
-        else
-            audioOutput->AddSamples(buffers, samples, timecode);
+        audioOutput->AddSamples(buffers, samples, timecode);
     }
 }
 
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/NuppelVideoPlayer.h,v
retrieving revision 1.130
diff -u -r1.130 NuppelVideoPlayer.h
--- libs/libmythtv/NuppelVideoPlayer.h	1 Jan 2004 22:05:07 -0000	1.130
+++ libs/libmythtv/NuppelVideoPlayer.h	2 Jan 2004 18:32:26 -0000
@@ -401,6 +401,9 @@
     float warpfactor_avg;
     int rtcfd;
     int vsynctol;
+    short int * warplbuff;
+    short int * warprbuff;
+    int warpbuffsize;
  
     bool delay_clipping;
     struct timeval nexttrigger, now;


More information about the mythtv-dev mailing list