Skip to content

Commit

Permalink
Add vsync_delay_clock method to compensate for hardware vsync phase i…
Browse files Browse the repository at this point in the history
…ssues.

This also deprecates the use of KeepPhase in favor of handling this in AVSync.

Patch by Mark Spieth.

Closes #7964.
  • Loading branch information
tralph committed Jan 8, 2011
1 parent 956b4f2 commit 3b10231
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 27 deletions.
20 changes: 13 additions & 7 deletions mythtv/libs/libmythtv/mythplayer.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1708,6 +1708,7 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)


float diverge = 0.0f; float diverge = 0.0f;
int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval; int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval;
int vsync_delay_clock = 0;
int64_t currentaudiotime = 0; int64_t currentaudiotime = 0;


// attempt to reduce fps for standalone PIP // attempt to reduce fps for standalone PIP
Expand Down Expand Up @@ -1798,7 +1799,8 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
osdLock.unlock(); osdLock.unlock();
VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + QString("AVSync waitforframe %1 %2") VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + QString("AVSync waitforframe %1 %2")
.arg(avsync_adjustment).arg(m_double_framerate)); .arg(avsync_adjustment).arg(m_double_framerate));
videosync->WaitForFrame(frameDelay + avsync_adjustment + repeat_delay); vsync_delay_clock = videosync->WaitForFrame
(frameDelay + avsync_adjustment + repeat_delay);
currentaudiotime = AVSyncGetAudiotime(); currentaudiotime = AVSyncGetAudiotime();
VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + "AVSync show"); VERBOSE(VB_PLAYBACK|VB_TIMESTAMP, LOC + "AVSync show");
videoOutput->Show(ps); videoOutput->Show(ps);
Expand Down Expand Up @@ -1828,7 +1830,7 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
videoOutput->PrepareFrame(buffer, ps, osd); videoOutput->PrepareFrame(buffer, ps, osd);
osdLock.unlock(); osdLock.unlock();
// Display the second field // Display the second field
videosync->WaitForFrame(frameDelay + avsync_adjustment); vsync_delay_clock = videosync->WaitForFrame(frameDelay + avsync_adjustment);
videoOutput->Show(ps); videoOutput->Show(ps);
} }


Expand All @@ -1840,7 +1842,7 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
} }
else else
{ {
videosync->WaitForFrame(frameDelay); vsync_delay_clock = videosync->WaitForFrame(frameDelay);
currentaudiotime = AVSyncGetAudiotime(); currentaudiotime = AVSyncGetAudiotime();
} }


Expand Down Expand Up @@ -1869,19 +1871,22 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)


if (audio.HasAudioOut() && normal_speed) if (audio.HasAudioOut() && normal_speed)
{ {
// must be sampled here due to Show delays
int64_t currentaudiotime = audio.GetAudioTime(); int64_t currentaudiotime = audio.GetAudioTime();
VERBOSE(VB_TIMESTAMP, LOC + QString( VERBOSE(VB_PLAYBACK+VB_TIMESTAMP, LOC + QString(
"A/V timecodes audio %1 video %2 frameinterval %3 " "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") "avp %7 avpen %8 avdc %9")
.arg(currentaudiotime) .arg(currentaudiotime)
.arg(timecode) .arg(timecode)
.arg(frame_interval) .arg(frame_interval)
.arg(timecode - currentaudiotime) .arg(timecode - currentaudiotime -
(int)(vsync_delay_clock*audio.GetStretchFactor()+500)/1000)
.arg(avsync_avg) .arg(avsync_avg)
.arg(tc_wrap[TC_AUDIO]) .arg(tc_wrap[TC_AUDIO])
.arg(avsync_predictor) .arg(avsync_predictor)
.arg(avsync_predictor_enabled) .arg(avsync_predictor_enabled)
.arg(vsync_delay_clock)
); );
if (currentaudiotime != 0 && timecode != 0) if (currentaudiotime != 0 && timecode != 0)
{ // currentaudiotime == 0 after a seek { // currentaudiotime == 0 after a seek
Expand All @@ -1903,7 +1908,8 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
prevtc = timecode; prevtc = timecode;
prevrp = repeat_pict; prevrp = repeat_pict;


avsync_delay = (timecode - currentaudiotime) * 1000;//usec avsync_delay = (timecode - currentaudiotime) * 1000 -
(int)(vsync_delay_clock*audio.GetStretchFactor()); //usec
// prevents major jitter when pts resets during dvd title // prevents major jitter when pts resets during dvd title
if (avsync_delay > 2000000 && limit_delay) if (avsync_delay > 2000000 && limit_delay)
avsync_delay = 90000; avsync_delay = 90000;
Expand Down
31 changes: 18 additions & 13 deletions mythtv/libs/libmythtv/vsync.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ int VideoSync::CalcDelay()
m_nexttrigger = now + ret_val; m_nexttrigger = now + ret_val;
} }


if (ret_val < -m_frame_interval) if (ret_val < -m_frame_interval && m_frame_interval >= m_refresh_interval)
{ {
ret_val = -m_frame_interval; ret_val = -m_frame_interval;


Expand All @@ -196,6 +196,7 @@ int VideoSync::CalcDelay()
* delay > 0 which would cause continous rapid fire stuttering. * delay > 0 which would cause continous rapid fire stuttering.
* This method is only useful for those sync methods where WaitForFrame * This method is only useful for those sync methods where WaitForFrame
* targets hardware retrace rather than targeting nexttrigger. * targets hardware retrace rather than targeting nexttrigger.
* NOTE: deprecated in favor of handling phase issues in mythplayer's AVSync.
*/ */
void VideoSync::KeepPhase() void VideoSync::KeepPhase()
{ {
Expand Down Expand Up @@ -293,7 +294,7 @@ void DRMVideoSync::Start(void)
VideoSync::Start(); VideoSync::Start();
} }


void DRMVideoSync::WaitForFrame(int sync_delay) int DRMVideoSync::WaitForFrame(int sync_delay)
{ {
// Offset for externally-provided A/V sync delay // Offset for externally-provided A/V sync delay
m_nexttrigger += sync_delay; m_nexttrigger += sync_delay;
Expand All @@ -316,7 +317,7 @@ void DRMVideoSync::WaitForFrame(int sync_delay)
if (m_delay > 0) if (m_delay > 0)
{ {
// Wait for any remaining retrace intervals in one pass. // Wait for any remaining retrace intervals in one pass.
int n = m_delay / m_refresh_interval + 1; int n = (m_delay + m_refresh_interval - 1) / m_refresh_interval;


drm_wait_vblank_t blank; drm_wait_vblank_t blank;
blank.request.type = DRM_VBLANK_RELATIVE; blank.request.type = DRM_VBLANK_RELATIVE;
Expand All @@ -327,7 +328,7 @@ void DRMVideoSync::WaitForFrame(int sync_delay)
//cerr << " Delay " << m_delay << endl; //cerr << " Delay " << m_delay << endl;
} }


KeepPhase(); return m_delay;
} }
#endif /* !_WIN32 */ #endif /* !_WIN32 */


Expand Down Expand Up @@ -397,7 +398,7 @@ void OpenGLVideoSync::Start(void)
#endif /* USING_OPENGL_VSYNC */ #endif /* USING_OPENGL_VSYNC */
} }


void OpenGLVideoSync::WaitForFrame(int sync_delay) int OpenGLVideoSync::WaitForFrame(int sync_delay)
{ {
(void) sync_delay; (void) sync_delay;
#ifdef USING_OPENGL_VSYNC #ifdef USING_OPENGL_VSYNC
Expand All @@ -409,11 +410,11 @@ void OpenGLVideoSync::WaitForFrame(int sync_delay)
m_delay = CalcDelay(); m_delay = CalcDelay();
if (m_delay > 0) if (m_delay > 0)
usleep(m_delay); usleep(m_delay);
return; return 0;
} }


if (!m_context) if (!m_context)
return; return 0;


unsigned int frameNum = m_context->GetVideoSyncCount(); unsigned int frameNum = m_context->GetVideoSyncCount();


Expand All @@ -427,12 +428,12 @@ void OpenGLVideoSync::WaitForFrame(int sync_delay)
// Wait for any remaining retrace intervals in one pass. // Wait for any remaining retrace intervals in one pass.
if (m_delay > 0) if (m_delay > 0)
{ {
uint n = m_delay / m_refresh_interval + 1; uint n = (m_delay + m_refresh_interval - 1) / m_refresh_interval;
m_context->WaitForVideoSync((n+1), (frameNum+n)%(n+1), &frameNum); m_context->WaitForVideoSync((n+1), (frameNum+n)%(n+1), &frameNum);
m_delay = CalcDelay(); m_delay = CalcDelay();
} }


KeepPhase(); return m_delay;
#endif /* USING_OPENGL_VSYNC */ #endif /* USING_OPENGL_VSYNC */
} }
#endif /* !_WIN32 */ #endif /* !_WIN32 */
Expand Down Expand Up @@ -479,7 +480,7 @@ bool RTCVideoSync::TryInit(void)
return true; return true;
} }


void RTCVideoSync::WaitForFrame(int sync_delay) int RTCVideoSync::WaitForFrame(int sync_delay)
{ {
m_nexttrigger += sync_delay; m_nexttrigger += sync_delay;


Expand All @@ -494,6 +495,7 @@ void RTCVideoSync::WaitForFrame(int sync_delay)
if ((val < 0) && (m_delay > 0)) if ((val < 0) && (m_delay > 0))
usleep(m_delay); usleep(m_delay);
} }
return 0;
} }
#endif /* __linux__ */ #endif /* __linux__ */


Expand All @@ -517,7 +519,7 @@ bool VDPAUVideoSync::TryInit(void)
return true; return true;
} }


void VDPAUVideoSync::WaitForFrame(int sync_delay) int VDPAUVideoSync::WaitForFrame(int sync_delay)
{ {
// Offset for externally-provided A/V sync delay // Offset for externally-provided A/V sync delay
m_nexttrigger += sync_delay; m_nexttrigger += sync_delay;
Expand All @@ -528,6 +530,7 @@ void VDPAUVideoSync::WaitForFrame(int sync_delay)


VideoOutputVDPAU *vo = (VideoOutputVDPAU *)(m_video_output); VideoOutputVDPAU *vo = (VideoOutputVDPAU *)(m_video_output);
vo->SetNextFrameDisplayTimeOffset(m_delay); vo->SetNextFrameDisplayTimeOffset(m_delay);
return 0;
} }
#endif #endif


Expand All @@ -548,7 +551,7 @@ bool BusyWaitVideoSync::TryInit(void)
return true; return true;
} }


void BusyWaitVideoSync::WaitForFrame(int sync_delay) int BusyWaitVideoSync::WaitForFrame(int sync_delay)
{ {
// Offset for externally-provided A/V sync delay // Offset for externally-provided A/V sync delay
m_nexttrigger += sync_delay; m_nexttrigger += sync_delay;
Expand Down Expand Up @@ -577,6 +580,7 @@ void BusyWaitVideoSync::WaitForFrame(int sync_delay)
if (cnt > 1) if (cnt > 1)
m_cheat -= 200; m_cheat -= 200;
} }
return 0;
} }


USleepVideoSync::USleepVideoSync(VideoOutput *vo, USleepVideoSync::USleepVideoSync(VideoOutput *vo,
Expand All @@ -594,13 +598,14 @@ bool USleepVideoSync::TryInit(void)
return true; return true;
} }


void USleepVideoSync::WaitForFrame(int sync_delay) int USleepVideoSync::WaitForFrame(int sync_delay)
{ {
// Offset for externally-provided A/V sync delay // Offset for externally-provided A/V sync delay
m_nexttrigger += sync_delay; m_nexttrigger += sync_delay;


m_delay = CalcDelay(); m_delay = CalcDelay();
if (m_delay > 0) if (m_delay > 0)
usleep(m_delay); usleep(m_delay);
return 0;
} }


15 changes: 8 additions & 7 deletions mythtv/libs/libmythtv/vsync.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class VideoSync
virtual void Start(void); virtual void Start(void);


/** \brief Waits for next a frame or field. /** \brief Waits for next a frame or field.
* Returns delay to real frame timing in usec
* *
* Start(void), WaitForFrame(void), and Stop(void) should * Start(void), WaitForFrame(void), and Stop(void) should
* always be called from same thread, to prevent bad * always be called from same thread, to prevent bad
Expand All @@ -72,7 +73,7 @@ class VideoSync
* \param sync_delay time until the desired frame or field * \param sync_delay time until the desired frame or field
* \sa CalcDelay(void), KeepPhase(void) * \sa CalcDelay(void), KeepPhase(void)
*/ */
virtual void WaitForFrame(int sync_delay) = 0; virtual int WaitForFrame(int sync_delay) = 0;


/// \brief Returns the (minimum) refresh interval of the output device. /// \brief Returns the (minimum) refresh interval of the output device.
int getRefreshInterval(void) const { return m_refresh_interval; } int getRefreshInterval(void) const { return m_refresh_interval; }
Expand Down Expand Up @@ -123,7 +124,7 @@ class DRMVideoSync : public VideoSync
QString getName(void) const { return QString("DRM"); } QString getName(void) const { return QString("DRM"); }
bool TryInit(void); bool TryInit(void);
void Start(void); void Start(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);


private: private:
int m_dri_fd; int m_dri_fd;
Expand Down Expand Up @@ -164,7 +165,7 @@ class OpenGLVideoSync : public VideoSync
QString getName(void) const { return QString("SGI OpenGL"); } QString getName(void) const { return QString("SGI OpenGL"); }
bool TryInit(void); bool TryInit(void);
void Start(void); void Start(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);


private: private:
MythRenderOpenGL *m_context; MythRenderOpenGL *m_context;
Expand Down Expand Up @@ -192,7 +193,7 @@ class RTCVideoSync : public VideoSync


QString getName(void) const { return QString("RTC"); } QString getName(void) const { return QString("RTC"); }
bool TryInit(void); bool TryInit(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);


private: private:
int m_rtcfd; int m_rtcfd;
Expand All @@ -212,7 +213,7 @@ class VDPAUVideoSync : public VideoSync


QString getName(void) const { return QString("VDPAU"); } QString getName(void) const { return QString("VDPAU"); }
bool TryInit(void); bool TryInit(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);


private: private:
}; };
Expand All @@ -239,7 +240,7 @@ class BusyWaitVideoSync : public VideoSync


QString getName(void) const { return QString("USleep with busy wait"); } QString getName(void) const { return QString("USleep with busy wait"); }
bool TryInit(void); bool TryInit(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);


private: private:
int m_cheat; int m_cheat;
Expand All @@ -266,6 +267,6 @@ class USleepVideoSync : public VideoSync


QString getName(void) const { return QString("USleep"); } QString getName(void) const { return QString("USleep"); }
bool TryInit(void); bool TryInit(void);
void WaitForFrame(int sync_delay); int WaitForFrame(int sync_delay);
}; };
#endif /* VSYNC_H_INCLUDED */ #endif /* VSYNC_H_INCLUDED */

0 comments on commit 3b10231

Please sign in to comment.