Merge pull request #26273 from 78andyp/blurayfixes2
[xbmc.git] / xbmc / cores / VideoPlayer / VideoReferenceClock.cpp
blob236db07780455e0d5ec32fe7fd983a93aaabcd85
1 /*
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.
7 */
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"
20 #include <mutex>
22 CVideoReferenceClock::CVideoReferenceClock() : CThread("RefClock")
24 m_SystemFrequency = CurrentHostFrequency();
25 m_ClockSpeed = 1.0;
26 m_TotalMissedVblanks = 0;
27 m_UseVblank = false;
29 m_CurrTime = 0;
30 m_LastIntTime = 0;
31 m_CurrTimeFract = 0.0;
32 m_RefreshRate = 0.0;
33 m_MissedVblanks = 0;
34 m_VblankTime = 0;
35 m_vsyncStopEvent.Reset();
37 Start();
40 CVideoReferenceClock::~CVideoReferenceClock()
42 m_bStop = true;
43 m_vsyncStopEvent.Set();
44 StopThread();
47 void CVideoReferenceClock::Start()
49 if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) && !IsRunning())
50 Create();
53 void CVideoReferenceClock::UpdateClock(int NrVBlanks, uint64_t time)
55 std::unique_lock<CCriticalSection> lock(m_CritSection);
57 m_VblankTime = time;
58 UpdateClockInternal(NrVBlanks, true);
61 void CVideoReferenceClock::Process()
63 bool SetupSuccess = false;
64 int64_t Now;
66 while(!m_bStop)
68 m_pVideoSync = CServiceBroker::GetWinSystem()->GetVideoSync(this);
70 if (m_pVideoSync)
72 SetupSuccess = m_pVideoSync->Setup();
73 UpdateRefreshrate();
76 std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
77 Now = CurrentHostCounter();
78 m_CurrTime = Now;
79 m_LastIntTime = m_CurrTime;
80 m_CurrTimeFract = 0.0;
81 m_ClockSpeed = 1.0;
82 m_TotalMissedVblanks = 0;
83 m_MissedVblanks = 0;
85 if (SetupSuccess)
87 m_UseVblank = true; //tell other threads we're using vblank as clock
88 m_VblankTime = Now; //initialize the timestamp of the last vblank
89 SingleLock.unlock();
91 // we might got signalled while we did not wait
92 if (!m_vsyncStopEvent.Signaled())
94 //run the clock
95 m_pVideoSync->Run(m_vsyncStopEvent);
96 m_vsyncStopEvent.Reset();
99 else
101 SingleLock.unlock();
102 CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setup failed, falling back to CurrentHostCounter()");
105 SingleLock.lock();
106 m_UseVblank = false; //we're back to using the systemclock
107 SingleLock.unlock();
109 //clean up the vblank clock
110 if (m_pVideoSync)
112 m_pVideoSync->Cleanup();
113 m_pVideoSync.reset();
116 if (!SetupSuccess)
117 break;
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
127 CLog::Log(
128 LOGDEBUG,
129 "CVideoReferenceClock: detected {} vblanks, missed {}, refreshrate might have changed",
130 NrVBlanks, m_MissedVblanks);
132 NrVBlanks -= m_MissedVblanks; //subtract the vblanks we missed
133 m_MissedVblanks = 0;
135 else
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
167 if (m_UseVblank)
169 int64_t NextVblank;
170 int64_t Now;
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
181 if (interpolated)
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;
195 else
197 return m_CurrTime;
200 else
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
210 if (m_UseVblank)
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
225 if (m_UseVblank)
226 return m_ClockSpeed;
227 else
228 return 1.0;
231 void CVideoReferenceClock::UpdateRefreshrate()
233 std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
234 m_RefreshRate = static_cast<double>(m_pVideoSync->GetFps());
235 m_ClockSpeed = 1.0;
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);
245 if (m_UseVblank)
247 if (interval)
248 *interval = m_ClockSpeed / m_RefreshRate;
250 return m_RefreshRate;
252 else
253 return -1;
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);
270 if (m_UseVblank)
272 MissedVblanks = m_TotalMissedVblanks;
273 ClockSpeed = m_ClockSpeed;
274 RefreshRate = m_RefreshRate;
275 return true;
277 return false;