/* * 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 "VideoSyncOsx.h" #include "ServiceBroker.h" #include "utils/MathUtils.h" #include "utils/TimeUtils.h" #include "utils/log.h" #include "windowing/GraphicContext.h" #include "windowing/WinSystem.h" #include "platform/darwin/osx/CocoaInterface.h" #include #include #include using namespace std::chrono_literals; bool CVideoSyncOsx::Setup(PUPDATECLOCK func) { CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} setting up OSX", __FUNCTION__); //init the vblank timestamp m_LastVBlankTime = 0; UpdateClock = func; m_displayLost = false; m_displayReset = false; m_lostEvent.Reset(); CServiceBroker::GetWinSystem()->Register(this); return true; } void CVideoSyncOsx::Run(CEvent& stopEvent) { InitDisplayLink(); //because cocoa has a vblank callback, we just keep sleeping until we're asked to stop the thread while(!stopEvent.Signaled() && !m_displayLost && !m_displayReset) { usleep(100000); } m_lostEvent.Set(); while(!stopEvent.Signaled() && m_displayLost && !m_displayReset) { usleep(10000); } DeinitDisplayLink(); } void CVideoSyncOsx::Cleanup() { CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} cleaning up OSX", __FUNCTION__); m_lostEvent.Set(); m_LastVBlankTime = 0; CServiceBroker::GetWinSystem()->Unregister(this); } float CVideoSyncOsx::GetFps() { m_fps = CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS(); CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} Detected refreshrate: {:f} hertz", __FUNCTION__, m_fps); return m_fps; } void CVideoSyncOsx::RefreshChanged() { m_displayReset = true; } void CVideoSyncOsx::OnLostDisplay() { if (!m_displayLost) { m_displayLost = true; m_lostEvent.Wait(1000ms); } } void CVideoSyncOsx::OnResetDisplay() { m_displayReset = true; } void CVideoSyncOsx::VblankHandler(int64_t nowtime, uint32_t timebase) { int NrVBlanks; double VBlankTime; int64_t Now = CurrentHostCounter(); if (m_LastVBlankTime != 0) { VBlankTime = (double)(nowtime - m_LastVBlankTime) / (double)timebase; NrVBlanks = MathUtils::round_int(VBlankTime * static_cast(m_fps)); //update the vblank timestamp, update the clock and send a signal that we got a vblank UpdateClock(NrVBlanks, Now, m_refClock); } //save the timestamp of this vblank so we can calculate how many happened next time m_LastVBlankTime = nowtime; } // Called by the Core Video Display Link whenever it's appropriate to render a frame. static CVReturn DisplayLinkCallBack(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { @autoreleasepool { CVideoSyncOsx* VideoSyncOsx = reinterpret_cast(displayLinkContext); if (inOutputTime->flags & kCVTimeStampHostTimeValid) VideoSyncOsx->VblankHandler(inOutputTime->hostTime, CVGetHostClockFrequency()); else VideoSyncOsx->VblankHandler(CVGetCurrentHostTime(), CVGetHostClockFrequency()); } return kCVReturnSuccess; } bool CVideoSyncOsx::InitDisplayLink() { bool ret = true; CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} setting up displaylink", __FUNCTION__); if (!Cocoa_CVDisplayLinkCreate((void*)DisplayLinkCallBack, reinterpret_cast(this))) { CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} Cocoa_CVDisplayLinkCreate failed", __FUNCTION__); ret = false; } return ret; } void CVideoSyncOsx::DeinitDisplayLink() { Cocoa_CVDisplayLinkRelease(); }