Advertisement
mancuser

VideoReferenceClock.cpp

Mar 18th, 2015
226
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.75 KB | None | 0 0
  1. /*
  2. * Copyright (C) 2005-2013 Team XBMC
  3. * http://xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "system.h"
  21. #include <list>
  22. #include "VideoReferenceClock.h"
  23. #include "utils/MathUtils.h"
  24. #include "utils/log.h"
  25. #include "utils/TimeUtils.h"
  26. #include "utils/StringUtils.h"
  27. #include "threads/SingleLock.h"
  28. #include "guilib/GraphicContext.h"
  29. #include "video/videosync/VideoSync.h"
  30. #include "windowing/WindowingFactory.h"
  31.  
  32. #if defined(HAS_GLX)
  33. #include "video/videosync/VideoSyncGLX.h"
  34. #endif
  35. #if defined(HAVE_X11)
  36. #include "video/videosync/VideoSyncDRM.h"
  37. #elif defined(TARGET_RASPBERRY_PI)
  38. #include "video/videosync/VideoSyncPi.h"
  39. #endif
  40. #if defined(TARGET_WINDOWS)
  41. #include "video/videosync/VideoSyncD3D.h"
  42. #endif
  43. #if defined(TARGET_DARWIN_OSX)
  44. #include "video/videosync/VideoSyncOsx.h"
  45. #endif
  46. #if defined(TARGET_DARWIN_IOS)
  47. #include "video/videosync/VideoSyncIos.h"
  48. #endif
  49.  
  50. using namespace std;
  51.  
  52. CVideoReferenceClock::CVideoReferenceClock() : CThread("RefClock")
  53. {
  54. m_SystemFrequency = CurrentHostFrequency();
  55. m_ClockSpeed = 1.0;
  56. m_ClockOffset = 0;
  57. m_TotalMissedVblanks = 0;
  58. m_UseVblank = false;
  59.  
  60. m_CurrTime = 0;
  61. m_LastIntTime = 0;
  62. m_CurrTimeFract = 0.0;
  63. m_fineadjust = 0.0;
  64. m_RefreshRate = 0.0;
  65. m_MissedVblanks = 0;
  66. m_VblankTime = 0;
  67.  
  68. m_pVideoSync = NULL;
  69. }
  70.  
  71. CVideoReferenceClock::~CVideoReferenceClock()
  72. {
  73. }
  74.  
  75. void CVideoReferenceClock::Stop()
  76. {
  77. CSingleExit lock(g_graphicsContext);
  78. StopThread();
  79. }
  80.  
  81. void CVideoReferenceClock::CBUpdateClock(int NrVBlanks, uint64_t time)
  82. {
  83. {
  84. CSingleLock lock(g_VideoReferenceClock.m_CritSection);
  85. g_VideoReferenceClock.m_VblankTime = time;
  86. g_VideoReferenceClock.UpdateClock(NrVBlanks, true);
  87. }
  88.  
  89. g_VideoReferenceClock.SendVblankSignal();
  90. }
  91.  
  92. void CVideoReferenceClock::Process()
  93. {
  94. bool SetupSuccess = false;
  95. int64_t Now;
  96.  
  97. while(!m_bStop)
  98. {
  99. //set up the vblank clock
  100. #if defined(HAVE_X11)
  101. std::string gpuvendor = g_Windowing.GetRenderVendor();
  102. std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
  103. - if ((gpuvendor.compare(0, 5, "intel") == 0 ||
  104. - gpuvendor.compare(0, 5, "x.org") == 0)) // AMD
  105. + if (false) // AMD
  106. m_pVideoSync = new CVideoSyncDRM();
  107. #if defined(HAS_GLX)
  108. else
  109. m_pVideoSync = new CVideoSyncGLX();
  110. #endif
  111. #elif defined(TARGET_WINDOWS)
  112. m_pVideoSync = new CVideoSyncD3D();
  113. #elif defined(TARGET_DARWIN_OSX)
  114. m_pVideoSync = new CVideoSyncOsx();
  115. #elif defined(TARGET_DARWIN_IOS)
  116. m_pVideoSync = new CVideoSyncIos();
  117. #elif defined(TARGET_RASPBERRY_PI)
  118. m_pVideoSync = new CVideoSyncPi();
  119. #endif
  120.  
  121. if (m_pVideoSync)
  122. {
  123. SetupSuccess = m_pVideoSync->Setup(CBUpdateClock);
  124. UpdateRefreshrate();
  125. }
  126.  
  127. CSingleLock SingleLock(m_CritSection);
  128. Now = CurrentHostCounter();
  129. m_CurrTime = Now + m_ClockOffset; //add the clock offset from the previous time we stopped
  130. m_LastIntTime = m_CurrTime;
  131. m_CurrTimeFract = 0.0;
  132. m_ClockSpeed = 1.0;
  133. m_TotalMissedVblanks = 0;
  134. m_fineadjust = 1.0;
  135. m_MissedVblanks = 0;
  136.  
  137. if (SetupSuccess)
  138. {
  139. m_UseVblank = true; //tell other threads we're using vblank as clock
  140. m_VblankTime = Now; //initialize the timestamp of the last vblank
  141. SingleLock.Leave();
  142.  
  143. //run the clock
  144. m_pVideoSync->Run(m_bStop);
  145. }
  146. else
  147. {
  148. SingleLock.Leave();
  149. CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setup failed, falling back to CurrentHostCounter()");
  150. }
  151.  
  152. SingleLock.Enter();
  153. m_UseVblank = false; //we're back to using the systemclock
  154. Now = CurrentHostCounter(); //set the clockoffset between the vblank clock and systemclock
  155. m_ClockOffset = m_CurrTime - Now;
  156. SingleLock.Leave();
  157.  
  158. //clean up the vblank clock
  159. if (m_pVideoSync)
  160. {
  161. m_pVideoSync->Cleanup();
  162. delete m_pVideoSync;
  163. m_pVideoSync = NULL;
  164. }
  165.  
  166. if (!SetupSuccess)
  167. break;
  168. }
  169. }
  170.  
  171. //this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update
  172. void CVideoReferenceClock::UpdateClock(int NrVBlanks, bool CheckMissed)
  173. {
  174. if (CheckMissed) //set to true from the vblank run function, set to false from Wait and GetTime
  175. {
  176. if (NrVBlanks < m_MissedVblanks) //if this is true the vblank detection in the run function is wrong
  177. CLog::Log(LOGDEBUG, "CVideoReferenceClock: detected %i vblanks, missed %i, refreshrate might have changed",
  178. NrVBlanks, m_MissedVblanks);
  179.  
  180. NrVBlanks -= m_MissedVblanks; //subtract the vblanks we missed
  181. m_MissedVblanks = 0;
  182. }
  183. else
  184. {
  185. m_MissedVblanks += NrVBlanks; //tell the vblank clock how many vblanks it missed
  186. m_TotalMissedVblanks += NrVBlanks; //for the codec information screen
  187. m_VblankTime += m_SystemFrequency * (int64_t)NrVBlanks / MathUtils::round_int(m_RefreshRate); //set the vblank time forward
  188. }
  189.  
  190. if (NrVBlanks > 0) //update the clock with the adjusted frequency if we have any vblanks
  191. {
  192. double increment = UpdateInterval() * NrVBlanks;
  193. double integer = floor(increment);
  194. m_CurrTime += (int64_t)(integer + 0.5); //make sure it gets correctly converted to int
  195. {
  196. //set up the vblank clock
  197. #if defined(HAVE_X11)
  198. std::string gpuvendor = g_Windowing.GetRenderVendor();
  199. std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
  200. - if ((gpuvendor.compare(0, 5, "intel") == 0 ||
  201. - gpuvendor.compare(0, 5, "x.org") == 0)) // AMD
  202. + if (false) // AMD
  203. m_pVideoSync = new CVideoSyncDRM();
  204. #if defined(HAS_GLX)
  205. else
  206. m_pVideoSync = new CVideoSyncGLX();
  207. #endif
  208. //accumulate what we lost due to rounding in m_CurrTimeFract, then add the integer part of that to m_CurrTime
  209. m_CurrTimeFract += increment - integer;
  210. integer = floor(m_CurrTimeFract);
  211. m_CurrTime += (int64_t)(integer + 0.5);
  212. m_CurrTimeFract -= integer;
  213. }
  214. }
  215.  
  216. double CVideoReferenceClock::UpdateInterval()
  217. {
  218. return m_ClockSpeed * m_fineadjust / m_RefreshRate * (double)m_SystemFrequency;
  219. }
  220. {
  221. //set up the vblank clock
  222. #if defined(HAVE_X11)
  223. std::string gpuvendor = g_Windowing.GetRenderVendor();
  224. std::transform(gpuvendor.begin(), gpuvendor.end(), gpuvendor.begin(), ::tolower);
  225. - if ((gpuvendor.compare(0, 5, "intel") == 0 ||
  226. - gpuvendor.compare(0, 5, "x.org") == 0)) // AMD
  227. + if (false) // AMD
  228. m_pVideoSync = new CVideoSyncDRM();
  229. #if defined(HAS_GLX)
  230. else
  231. m_pVideoSync = new CVideoSyncGLX();
  232. #endif
  233. //called from dvdclock to get the time
  234. int64_t CVideoReferenceClock::GetTime(bool interpolated /* = true*/)
  235. {
  236. CSingleLock SingleLock(m_CritSection);
  237.  
  238. //when using vblank, get the time from that, otherwise use the systemclock
  239. if (m_UseVblank)
  240. {
  241. int64_t NextVblank;
  242. int64_t Now;
  243.  
  244. Now = CurrentHostCounter(); //get current system time
  245. NextVblank = TimeOfNextVblank(); //get time when the next vblank should happen
  246.  
  247. while(Now >= NextVblank) //keep looping until the next vblank is in the future
  248. {
  249. UpdateClock(1, false); //update clock when next vblank should have happened already
  250. NextVblank = TimeOfNextVblank(); //get time when the next vblank should happen
  251. }
  252.  
  253. if (interpolated)
  254. {
  255. //interpolate from the last time the clock was updated
  256. double elapsed = (double)(Now - m_VblankTime) * m_ClockSpeed * m_fineadjust;
  257. //don't interpolate more than 2 vblank periods
  258. elapsed = min(elapsed, UpdateInterval() * 2.0);
  259.  
  260. //make sure the clock doesn't go backwards
  261. int64_t intTime = m_CurrTime + (int64_t)elapsed;
  262. if (intTime > m_LastIntTime)
  263. m_LastIntTime = intTime;
  264.  
  265. return m_LastIntTime;
  266. }
  267. else
  268. {
  269. return m_CurrTime;
  270. }
  271. }
  272. else
  273. {
  274. return CurrentHostCounter() + m_ClockOffset;
  275. }
  276. }
  277.  
  278. //called from dvdclock to get the clock frequency
  279. int64_t CVideoReferenceClock::GetFrequency()
  280. {
  281. return m_SystemFrequency;
  282. }
  283.  
  284. void CVideoReferenceClock::SetSpeed(double Speed)
  285. {
  286. CSingleLock SingleLock(m_CritSection);
  287. //dvdplayer can change the speed to fit the rereshrate
  288. if (m_UseVblank)
  289. {
  290. if (Speed != m_ClockSpeed)
  291. {
  292. m_ClockSpeed = Speed;
  293. CLog::Log(LOGDEBUG, "CVideoReferenceClock: Clock speed %f%%", GetSpeed() * 100.0);
  294. }
  295. }
  296. }
  297.  
  298. double CVideoReferenceClock::GetSpeed()
  299. {
  300. CSingleLock SingleLock(m_CritSection);
  301.  
  302. //dvdplayer needs to know the speed for the resampler
  303. if (m_UseVblank)
  304. return m_ClockSpeed;
  305. else
  306. return 1.0;
  307. }
  308.  
  309. void CVideoReferenceClock::UpdateRefreshrate()
  310. {
  311. CSingleLock SingleLock(m_CritSection);
  312. m_RefreshRate = m_pVideoSync->GetFps();
  313. m_ClockSpeed = 1.0;
  314.  
  315. CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %.3f hertz", m_RefreshRate);
  316. }
  317.  
  318. //dvdplayer needs to know the refreshrate for matching the fps of the video playing to it
  319. double CVideoReferenceClock::GetRefreshRate(double* interval /*= NULL*/)
  320. {
  321. CSingleLock SingleLock(m_CritSection);
  322.  
  323. if (m_UseVblank)
  324. {
  325. if (interval)
  326. *interval = m_ClockSpeed / m_RefreshRate;
  327.  
  328. return m_RefreshRate;
  329. }
  330. else
  331. return -1;
  332. }
  333.  
  334.  
  335. //this is called from CDVDClock::WaitAbsoluteClock, which is called from CXBMCRenderManager::WaitPresentTime
  336. //it waits until a certain timestamp has passed, used for displaying videoframes at the correct moment
  337. int64_t CVideoReferenceClock::Wait(int64_t Target)
  338. {
  339. int64_t Now;
  340. int SleepTime;
  341.  
  342. CSingleLock SingleLock(m_CritSection);
  343.  
  344. if (m_UseVblank) //when true the vblank is used as clock source
  345. {
  346. while (m_CurrTime < Target)
  347. {
  348. //calculate how long to sleep before we should have gotten a signal that a vblank happened
  349. Now = CurrentHostCounter();
  350. int64_t NextVblank = TimeOfNextVblank();
  351. SleepTime = (int)((NextVblank - Now) * 1000 / m_SystemFrequency);
  352.  
  353. int64_t CurrTime = m_CurrTime; //save current value of the clock
  354.  
  355. bool Late = false;
  356. if (SleepTime <= 0) //if sleeptime is 0 or lower, the vblank clock is already late in updating
  357. {
  358. Late = true;
  359. }
  360. else
  361. {
  362. m_VblankEvent.Reset();
  363. SingleLock.Leave();
  364. if (!m_VblankEvent.WaitMSec(SleepTime)) //if this returns false, it means the vblank event was not set within
  365. Late = true; //the required time
  366. SingleLock.Enter();
  367. }
  368.  
  369. //if the vblank clock was late with its update, we update the clock ourselves
  370. if (Late && CurrTime == m_CurrTime)
  371. UpdateClock(1, false); //update the clock by 1 vblank
  372.  
  373. }
  374. return m_CurrTime;
  375. }
  376. else
  377. {
  378. int64_t ClockOffset = m_ClockOffset;
  379. SingleLock.Leave();
  380. Now = CurrentHostCounter();
  381. //sleep until the timestamp has passed
  382. SleepTime = (int)((Target - (Now + ClockOffset)) * 1000 / m_SystemFrequency);
  383. if (SleepTime > 0)
  384. ::Sleep(SleepTime);
  385.  
  386. Now = CurrentHostCounter();
  387. return Now + ClockOffset;
  388. }
  389. }
  390.  
  391.  
  392. void CVideoReferenceClock::SendVblankSignal()
  393. {
  394. m_VblankEvent.Set();
  395. }
  396.  
  397. #define MAXVBLANKDELAY 13LL
  398. //guess when the next vblank should happen,
  399. //based on the refreshrate and when the previous one happened
  400. //increase that by 30% to allow for errors
  401. int64_t CVideoReferenceClock::TimeOfNextVblank()
  402. {
  403. return m_VblankTime + (m_SystemFrequency / MathUtils::round_int(m_RefreshRate) * MAXVBLANKDELAY / 10LL);
  404. }
  405.  
  406. //for the codec information screen
  407. bool CVideoReferenceClock::GetClockInfo(int& MissedVblanks, double& ClockSpeed, double& RefreshRate)
  408. {
  409. if (m_UseVblank)
  410. {
  411. MissedVblanks = m_TotalMissedVblanks;
  412. ClockSpeed = m_ClockSpeed * 100.0;
  413. RefreshRate = m_RefreshRate;
  414. return true;
  415. }
  416. return false;
  417. }
  418.  
  419. void CVideoReferenceClock::SetFineAdjust(double fineadjust)
  420. {
  421. CSingleLock SingleLock(m_CritSection);
  422. m_fineadjust = fineadjust;
  423. }
  424.  
  425. void CVideoReferenceClock::RefreshChanged()
  426. {
  427. CSingleLock SingleLock(m_CritSection);
  428. if (m_pVideoSync)
  429. {
  430. m_pVideoSync->RefreshChanged();
  431. }
  432. }
  433.  
  434. CVideoReferenceClock g_VideoReferenceClock;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement