[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