summaryrefslogtreecommitdiffstats
path: root/xbmc/cores/VideoPlayer/VideoReferenceClock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/cores/VideoPlayer/VideoReferenceClock.cpp')
-rw-r--r--xbmc/cores/VideoPlayer/VideoReferenceClock.cpp280
1 files changed, 280 insertions, 0 deletions
diff --git a/xbmc/cores/VideoPlayer/VideoReferenceClock.cpp b/xbmc/cores/VideoPlayer/VideoReferenceClock.cpp
new file mode 100644
index 0000000..7508978
--- /dev/null
+++ b/xbmc/cores/VideoPlayer/VideoReferenceClock.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+#include "VideoReferenceClock.h"
+
+#include "ServiceBroker.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/MathUtils.h"
+#include "utils/TimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/VideoSync.h"
+#include "windowing/WinSystem.h"
+
+#include <mutex>
+
+CVideoReferenceClock::CVideoReferenceClock() : CThread("RefClock")
+{
+ m_SystemFrequency = CurrentHostFrequency();
+ m_ClockSpeed = 1.0;
+ m_TotalMissedVblanks = 0;
+ m_UseVblank = false;
+
+ m_CurrTime = 0;
+ m_LastIntTime = 0;
+ m_CurrTimeFract = 0.0;
+ m_RefreshRate = 0.0;
+ m_MissedVblanks = 0;
+ m_VblankTime = 0;
+ m_vsyncStopEvent.Reset();
+
+ Start();
+}
+
+CVideoReferenceClock::~CVideoReferenceClock()
+{
+ m_bStop = true;
+ m_vsyncStopEvent.Set();
+ StopThread();
+}
+
+void CVideoReferenceClock::Start()
+{
+ if(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) && !IsRunning())
+ Create();
+}
+
+void CVideoReferenceClock::CBUpdateClock(int NrVBlanks, uint64_t time, void *clock)
+{
+ {
+ CVideoReferenceClock *refClock = static_cast<CVideoReferenceClock*>(clock);
+ std::unique_lock<CCriticalSection> lock(refClock->m_CritSection);
+ refClock->m_VblankTime = time;
+ refClock->UpdateClock(NrVBlanks, true);
+ }
+}
+
+void CVideoReferenceClock::Process()
+{
+ bool SetupSuccess = false;
+ int64_t Now;
+
+ while(!m_bStop)
+ {
+ m_pVideoSync = CServiceBroker::GetWinSystem()->GetVideoSync(this);
+
+ if (m_pVideoSync)
+ {
+ SetupSuccess = m_pVideoSync->Setup(CBUpdateClock);
+ UpdateRefreshrate();
+ }
+
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+ Now = CurrentHostCounter();
+ m_CurrTime = Now;
+ m_LastIntTime = m_CurrTime;
+ m_CurrTimeFract = 0.0;
+ m_ClockSpeed = 1.0;
+ m_TotalMissedVblanks = 0;
+ m_MissedVblanks = 0;
+
+ if (SetupSuccess)
+ {
+ m_UseVblank = true; //tell other threads we're using vblank as clock
+ m_VblankTime = Now; //initialize the timestamp of the last vblank
+ SingleLock.unlock();
+
+ // we might got signalled while we did not wait
+ if (!m_vsyncStopEvent.Signaled())
+ {
+ //run the clock
+ m_pVideoSync->Run(m_vsyncStopEvent);
+ m_vsyncStopEvent.Reset();
+ }
+ }
+ else
+ {
+ SingleLock.unlock();
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setup failed, falling back to CurrentHostCounter()");
+ }
+
+ SingleLock.lock();
+ m_UseVblank = false; //we're back to using the systemclock
+ SingleLock.unlock();
+
+ //clean up the vblank clock
+ if (m_pVideoSync)
+ {
+ m_pVideoSync->Cleanup();
+ m_pVideoSync.reset();
+ }
+
+ if (!SetupSuccess)
+ break;
+ }
+}
+
+//this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update
+void CVideoReferenceClock::UpdateClock(int NrVBlanks, bool CheckMissed)
+{
+ if (CheckMissed) //set to true from the vblank run function, set to false from Wait and GetTime
+ {
+ if (NrVBlanks < m_MissedVblanks) //if this is true the vblank detection in the run function is wrong
+ CLog::Log(
+ LOGDEBUG,
+ "CVideoReferenceClock: detected {} vblanks, missed {}, refreshrate might have changed",
+ NrVBlanks, m_MissedVblanks);
+
+ NrVBlanks -= m_MissedVblanks; //subtract the vblanks we missed
+ m_MissedVblanks = 0;
+ }
+ else
+ {
+ m_MissedVblanks += NrVBlanks; //tell the vblank clock how many vblanks it missed
+ m_TotalMissedVblanks += NrVBlanks; //for the codec information screen
+ m_VblankTime += m_SystemFrequency * static_cast<int64_t>(NrVBlanks) / MathUtils::round_int(m_RefreshRate); //set the vblank time forward
+ }
+
+ if (NrVBlanks > 0) //update the clock with the adjusted frequency if we have any vblanks
+ {
+ double increment = UpdateInterval() * NrVBlanks;
+ double integer = floor(increment);
+ m_CurrTime += static_cast<int64_t>(integer + 0.5); //make sure it gets correctly converted to int
+
+ //accumulate what we lost due to rounding in m_CurrTimeFract, then add the integer part of that to m_CurrTime
+ m_CurrTimeFract += increment - integer;
+ integer = floor(m_CurrTimeFract);
+ m_CurrTime += static_cast<int64_t>(integer + 0.5);
+ m_CurrTimeFract -= integer;
+ }
+}
+
+double CVideoReferenceClock::UpdateInterval() const
+{
+ return m_ClockSpeed / m_RefreshRate * static_cast<double>(m_SystemFrequency);
+}
+
+//called from dvdclock to get the time
+int64_t CVideoReferenceClock::GetTime(bool interpolated /* = true*/)
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+
+ //when using vblank, get the time from that, otherwise use the systemclock
+ if (m_UseVblank)
+ {
+ int64_t NextVblank;
+ int64_t Now;
+
+ Now = CurrentHostCounter(); //get current system time
+ NextVblank = TimeOfNextVblank(); //get time when the next vblank should happen
+
+ while(Now >= NextVblank) //keep looping until the next vblank is in the future
+ {
+ UpdateClock(1, false); //update clock when next vblank should have happened already
+ NextVblank = TimeOfNextVblank(); //get time when the next vblank should happen
+ }
+
+ if (interpolated)
+ {
+ //interpolate from the last time the clock was updated
+ double elapsed = static_cast<double>(Now - m_VblankTime) * m_ClockSpeed;
+ //don't interpolate more than 2 vblank periods
+ elapsed = std::min(elapsed, UpdateInterval() * 2.0);
+
+ //make sure the clock doesn't go backwards
+ int64_t intTime = m_CurrTime + static_cast<int64_t>(elapsed);
+ if (intTime > m_LastIntTime)
+ m_LastIntTime = intTime;
+
+ return m_LastIntTime;
+ }
+ else
+ {
+ return m_CurrTime;
+ }
+ }
+ else
+ {
+ return CurrentHostCounter();
+ }
+}
+
+void CVideoReferenceClock::SetSpeed(double Speed)
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+ //VideoPlayer can change the speed to fit the rereshrate
+ if (m_UseVblank)
+ {
+ if (Speed != m_ClockSpeed)
+ {
+ m_ClockSpeed = Speed;
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Clock speed {:0.2f} %", m_ClockSpeed * 100.0);
+ }
+ }
+}
+
+double CVideoReferenceClock::GetSpeed()
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+
+ //VideoPlayer needs to know the speed for the resampler
+ if (m_UseVblank)
+ return m_ClockSpeed;
+ else
+ return 1.0;
+}
+
+void CVideoReferenceClock::UpdateRefreshrate()
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+ m_RefreshRate = static_cast<double>(m_pVideoSync->GetFps());
+ m_ClockSpeed = 1.0;
+
+ CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: {:.3f} hertz", m_RefreshRate);
+}
+
+//VideoPlayer needs to know the refreshrate for matching the fps of the video playing to it
+double CVideoReferenceClock::GetRefreshRate(double* interval /*= NULL*/)
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+
+ if (m_UseVblank)
+ {
+ if (interval)
+ *interval = m_ClockSpeed / m_RefreshRate;
+
+ return m_RefreshRate;
+ }
+ else
+ return -1;
+}
+
+#define MAXVBLANKDELAY 13LL
+//guess when the next vblank should happen,
+//based on the refreshrate and when the previous one happened
+//increase that by 30% to allow for errors
+int64_t CVideoReferenceClock::TimeOfNextVblank() const
+{
+ return m_VblankTime + (m_SystemFrequency / MathUtils::round_int(m_RefreshRate) * MAXVBLANKDELAY / 10LL);
+}
+
+//for the codec information screen
+bool CVideoReferenceClock::GetClockInfo(int& MissedVblanks, double& ClockSpeed, double& RefreshRate) const
+{
+ std::unique_lock<CCriticalSection> SingleLock(m_CritSection);
+
+ if (m_UseVblank)
+ {
+ MissedVblanks = m_TotalMissedVblanks;
+ ClockSpeed = m_ClockSpeed;
+ RefreshRate = m_RefreshRate;
+ return true;
+ }
+ return false;
+}