2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
8 #include "VideoReferenceClock.h"
10 #include "ServiceBroker.h"
11 #include "settings/Settings.h"
12 #include "settings/SettingsComponent.h"
13 #include "utils/MathUtils.h"
14 #include "utils/TimeUtils.h"
15 #include "utils/log.h"
16 #include "windowing/GraphicContext.h"
17 #include "windowing/VideoSync.h"
18 #include "windowing/WinSystem.h"
22 CVideoReferenceClock::CVideoReferenceClock() : CThread("RefClock")
24 m_SystemFrequency
= CurrentHostFrequency();
26 m_TotalMissedVblanks
= 0;
31 m_CurrTimeFract
= 0.0;
35 m_vsyncStopEvent
.Reset();
40 CVideoReferenceClock::~CVideoReferenceClock()
43 m_vsyncStopEvent
.Set();
47 void CVideoReferenceClock::Start()
49 if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK
) && !IsRunning())
53 void CVideoReferenceClock::UpdateClock(int NrVBlanks
, uint64_t time
)
55 std::unique_lock
<CCriticalSection
> lock(m_CritSection
);
58 UpdateClockInternal(NrVBlanks
, true);
61 void CVideoReferenceClock::Process()
63 bool SetupSuccess
= false;
68 m_pVideoSync
= CServiceBroker::GetWinSystem()->GetVideoSync(this);
72 SetupSuccess
= m_pVideoSync
->Setup();
76 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
77 Now
= CurrentHostCounter();
79 m_LastIntTime
= m_CurrTime
;
80 m_CurrTimeFract
= 0.0;
82 m_TotalMissedVblanks
= 0;
87 m_UseVblank
= true; //tell other threads we're using vblank as clock
88 m_VblankTime
= Now
; //initialize the timestamp of the last vblank
91 // we might got signalled while we did not wait
92 if (!m_vsyncStopEvent
.Signaled())
95 m_pVideoSync
->Run(m_vsyncStopEvent
);
96 m_vsyncStopEvent
.Reset();
102 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Setup failed, falling back to CurrentHostCounter()");
106 m_UseVblank
= false; //we're back to using the systemclock
109 //clean up the vblank clock
112 m_pVideoSync
->Cleanup();
113 m_pVideoSync
.reset();
121 //this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update
122 void CVideoReferenceClock::UpdateClockInternal(int NrVBlanks
, bool CheckMissed
)
124 if (CheckMissed
) //set to true from the vblank run function, set to false from Wait and GetTime
126 if (NrVBlanks
< m_MissedVblanks
) //if this is true the vblank detection in the run function is wrong
129 "CVideoReferenceClock: detected {} vblanks, missed {}, refreshrate might have changed",
130 NrVBlanks
, m_MissedVblanks
);
132 NrVBlanks
-= m_MissedVblanks
; //subtract the vblanks we missed
137 m_MissedVblanks
+= NrVBlanks
; //tell the vblank clock how many vblanks it missed
138 m_TotalMissedVblanks
+= NrVBlanks
; //for the codec information screen
139 m_VblankTime
+= m_SystemFrequency
* static_cast<int64_t>(NrVBlanks
) / MathUtils::round_int(m_RefreshRate
); //set the vblank time forward
142 if (NrVBlanks
> 0) //update the clock with the adjusted frequency if we have any vblanks
144 double increment
= UpdateInterval() * NrVBlanks
;
145 double integer
= floor(increment
);
146 m_CurrTime
+= static_cast<int64_t>(integer
+ 0.5); //make sure it gets correctly converted to int
148 //accumulate what we lost due to rounding in m_CurrTimeFract, then add the integer part of that to m_CurrTime
149 m_CurrTimeFract
+= increment
- integer
;
150 integer
= floor(m_CurrTimeFract
);
151 m_CurrTime
+= static_cast<int64_t>(integer
+ 0.5);
152 m_CurrTimeFract
-= integer
;
156 double CVideoReferenceClock::UpdateInterval() const
158 return m_ClockSpeed
/ m_RefreshRate
* static_cast<double>(m_SystemFrequency
);
161 //called from dvdclock to get the time
162 int64_t CVideoReferenceClock::GetTime(bool interpolated
/* = true*/)
164 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
166 //when using vblank, get the time from that, otherwise use the systemclock
172 Now
= CurrentHostCounter(); //get current system time
173 NextVblank
= TimeOfNextVblank(); //get time when the next vblank should happen
175 while(Now
>= NextVblank
) //keep looping until the next vblank is in the future
177 UpdateClockInternal(1, false); //update clock when next vblank should have happened already
178 NextVblank
= TimeOfNextVblank(); //get time when the next vblank should happen
183 //interpolate from the last time the clock was updated
184 double elapsed
= static_cast<double>(Now
- m_VblankTime
) * m_ClockSpeed
;
185 //don't interpolate more than 2 vblank periods
186 elapsed
= std::min(elapsed
, UpdateInterval() * 2.0);
188 //make sure the clock doesn't go backwards
189 int64_t intTime
= m_CurrTime
+ static_cast<int64_t>(elapsed
);
190 if (intTime
> m_LastIntTime
)
191 m_LastIntTime
= intTime
;
193 return m_LastIntTime
;
202 return CurrentHostCounter();
206 void CVideoReferenceClock::SetSpeed(double Speed
)
208 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
209 //VideoPlayer can change the speed to fit the rereshrate
212 if (Speed
!= m_ClockSpeed
)
214 m_ClockSpeed
= Speed
;
215 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Clock speed {:0.2f} %", m_ClockSpeed
* 100.0);
220 double CVideoReferenceClock::GetSpeed()
222 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
224 //VideoPlayer needs to know the speed for the resampler
231 void CVideoReferenceClock::UpdateRefreshrate()
233 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
234 m_RefreshRate
= static_cast<double>(m_pVideoSync
->GetFps());
237 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Detected refreshrate: {:.3f} hertz", m_RefreshRate
);
240 //VideoPlayer needs to know the refreshrate for matching the fps of the video playing to it
241 double CVideoReferenceClock::GetRefreshRate(double* interval
/*= NULL*/)
243 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
248 *interval
= m_ClockSpeed
/ m_RefreshRate
;
250 return m_RefreshRate
;
256 #define MAXVBLANKDELAY 13LL
257 //guess when the next vblank should happen,
258 //based on the refreshrate and when the previous one happened
259 //increase that by 30% to allow for errors
260 int64_t CVideoReferenceClock::TimeOfNextVblank() const
262 return m_VblankTime
+ (m_SystemFrequency
/ MathUtils::round_int(m_RefreshRate
) * MAXVBLANKDELAY
/ 10LL);
265 //for the codec information screen
266 bool CVideoReferenceClock::GetClockInfo(int& MissedVblanks
, double& ClockSpeed
, double& RefreshRate
) const
268 std::unique_lock
<CCriticalSection
> SingleLock(m_CritSection
);
272 MissedVblanks
= m_TotalMissedVblanks
;
273 ClockSpeed
= m_ClockSpeed
;
274 RefreshRate
= m_RefreshRate
;