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.
11 #include "VideoReferenceClock.h"
12 #include "cores/VideoPlayer/Interface/TimingConstants.h"
13 #include "utils/MathUtils.h"
14 #include "utils/TimeUtils.h"
15 #include "utils/log.h"
22 CDVDClock::CDVDClock()
24 std::unique_lock
<CCriticalSection
> lock(m_systemsection
);
29 m_speedAfterPause
= DVD_PLAYSPEED_PAUSE
;
31 m_maxspeedadjust
= 5.0;
36 m_frameTime
= DVD_TIME_BASE
/ 60.0;
38 m_videoRefClock
= std::make_unique
<CVideoReferenceClock
>();
39 m_lastSystemTime
= m_videoRefClock
->GetTime();
40 m_systemOffset
= m_videoRefClock
->GetTime();
41 m_systemFrequency
= CurrentHostFrequency();
42 m_systemUsed
= m_systemFrequency
;
45 CDVDClock::~CDVDClock() = default;
47 // Returns the current absolute clock in units of DVD_TIME_BASE (usually microseconds).
48 double CDVDClock::GetAbsoluteClock(bool interpolated
/*= true*/)
50 std::unique_lock
<CCriticalSection
> lock(m_systemsection
);
53 current
= m_videoRefClock
->GetTime(interpolated
);
55 return SystemToAbsolute(current
);
58 double CDVDClock::GetClock(bool interpolated
/*= true*/)
60 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
62 int64_t current
= m_videoRefClock
->GetTime(interpolated
);
63 m_systemAdjust
+= m_speedAdjust
* (current
- m_lastSystemTime
);
64 m_lastSystemTime
= current
;
66 return SystemToPlaying(current
);
69 double CDVDClock::GetClock(double& absolute
, bool interpolated
/*= true*/)
71 int64_t current
= m_videoRefClock
->GetTime(interpolated
);
73 std::unique_lock
<CCriticalSection
> lock(m_systemsection
);
74 absolute
= SystemToAbsolute(current
);
76 m_systemAdjust
+= m_speedAdjust
* (current
- m_lastSystemTime
);
77 m_lastSystemTime
= current
;
79 return SystemToPlaying(current
);
82 void CDVDClock::SetVsyncAdjust(double adjustment
)
84 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
85 m_vSyncAdjust
= adjustment
;
88 double CDVDClock::GetVsyncAdjust()
90 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
94 void CDVDClock::Pause(bool pause
)
96 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
98 if (pause
&& !m_paused
)
101 m_speedAfterPause
= m_systemFrequency
* DVD_PLAYSPEED_NORMAL
/ m_systemUsed
;
103 m_speedAfterPause
= DVD_PLAYSPEED_PAUSE
;
105 SetSpeed(DVD_PLAYSPEED_PAUSE
);
108 else if (!pause
&& m_paused
)
111 SetSpeed(m_speedAfterPause
);
115 void CDVDClock::Advance(double time
)
117 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
121 m_pauseClock
+= time
/ DVD_TIME_BASE
* m_systemFrequency
;
125 void CDVDClock::SetSpeed(int iSpeed
)
127 // this will sometimes be a little bit of due to rounding errors, ie clock might jump a bit when changing speed
128 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
132 m_speedAfterPause
= iSpeed
;
136 if (iSpeed
== DVD_PLAYSPEED_PAUSE
)
139 m_pauseClock
= m_videoRefClock
->GetTime();
144 int64_t newfreq
= m_systemFrequency
* DVD_PLAYSPEED_NORMAL
/ iSpeed
;
146 current
= m_videoRefClock
->GetTime();
149 m_startClock
+= current
- m_pauseClock
;
153 m_startClock
= current
- (int64_t)((double)(current
- m_startClock
) * newfreq
/ m_systemUsed
);
154 m_systemUsed
= newfreq
;
157 void CDVDClock::SetSpeedAdjust(double adjust
)
159 CLog::Log(LOGDEBUG
, "CDVDClock::SetSpeedAdjust - adjusted:{:f}", adjust
);
161 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
162 m_speedAdjust
= adjust
;
165 double CDVDClock::GetSpeedAdjust()
167 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
168 return m_speedAdjust
;
171 double CDVDClock::ErrorAdjust(double error
, const char* log
)
173 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
175 double clock
, absolute
, adjustment
;
176 clock
= GetClock(absolute
);
178 // skip minor updates while speed adjust is active
179 // -> adjusting buffer levels
180 if (m_speedAdjust
!= 0 && error
< DVD_MSEC_TO_TIME(100))
187 if (m_vSyncAdjust
!= 0)
189 // Audio ahead is more noticeable then audio behind video.
190 // Correct if aufio is more than 20ms ahead or more then
191 // 27ms behind. In a worst case scenario we switch from
192 // 20ms ahead to 21ms behind (for fps of 23.976)
193 if (error
> 0.02 * DVD_TIME_BASE
)
194 adjustment
= m_frameTime
;
195 else if (error
< -0.027 * DVD_TIME_BASE
)
196 adjustment
= -m_frameTime
;
204 Discontinuity(clock
+adjustment
, absolute
);
206 CLog::Log(LOGDEBUG
, "CDVDClock::ErrorAdjust - {} - error:{:f}, adjusted:{:f}", log
, error
,
211 void CDVDClock::Discontinuity(double clock
, double absolute
)
213 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
214 m_startClock
= AbsoluteToSystem(absolute
);
216 m_pauseClock
= m_startClock
;
223 void CDVDClock::SetMaxSpeedAdjust(double speed
)
225 std::unique_lock
<CCriticalSection
> lock(m_speedsection
);
227 m_maxspeedadjust
= speed
;
230 //returns the refreshrate if the videoreferenceclock is running, -1 otherwise
231 int CDVDClock::UpdateFramerate(double fps
, double* interval
/*= NULL*/)
233 //sent with fps of 0 means we are not playing video
237 m_frameTime
= 1/fps
* DVD_TIME_BASE
;
239 //check if the videoreferenceclock is running, will return -1 if not
240 double rate
= m_videoRefClock
->GetRefreshRate(interval
);
245 std::unique_lock
<CCriticalSection
> lock(m_speedsection
);
247 double weight
= (rate
* 2) / fps
;
249 //set the speed of the videoreferenceclock based on fps, refreshrate and maximum speed adjust set by user
250 if (m_maxspeedadjust
> 0.05)
252 if (weight
/ MathUtils::round_int(weight
) < 1.0 + m_maxspeedadjust
/ 100.0 &&
253 weight
/ MathUtils::round_int(weight
) > 1.0 - m_maxspeedadjust
/ 100.0)
254 weight
= MathUtils::round_int(weight
);
256 double speed
= (rate
* 2.0 ) / (fps
* weight
);
259 m_videoRefClock
->SetSpeed(speed
);
264 bool CDVDClock::GetClockInfo(int& MissedVblanks
, double& ClockSpeed
, double& RefreshRate
) const
266 return m_videoRefClock
->GetClockInfo(MissedVblanks
, ClockSpeed
, RefreshRate
);
269 double CDVDClock::SystemToAbsolute(int64_t system
)
271 return DVD_TIME_BASE
* (double)(system
- m_systemOffset
) / m_systemFrequency
;
274 int64_t CDVDClock::AbsoluteToSystem(double absolute
)
276 return (int64_t)(absolute
/ DVD_TIME_BASE
* m_systemFrequency
) + m_systemOffset
;
279 double CDVDClock::SystemToPlaying(int64_t system
)
285 m_startClock
= system
;
286 m_systemUsed
= m_systemFrequency
;
288 m_pauseClock
= m_startClock
;
297 current
= m_pauseClock
;
301 return DVD_TIME_BASE
* (double)(current
- m_startClock
+ m_systemAdjust
) / m_systemUsed
+ m_iDisc
;
304 double CDVDClock::GetClockSpeed()
306 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
308 double speed
= (double)m_systemFrequency
/ m_systemUsed
;
309 return m_videoRefClock
->GetSpeed() * speed
+ m_speedAdjust
;