summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/osx/VideoSyncOsx.mm
blob: d19e724bc5fdad9be336997bca9dc9224d4c1569 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 *  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 <CoreVideo/CVHostTime.h>
#include <QuartzCore/CVDisplayLink.h>
#include <unistd.h>

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<double>(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<CVideoSyncOsx*>(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<void*>(this)))
  {
    CLog::Log(LOGDEBUG, "CVideoSyncOsx::{} Cocoa_CVDisplayLinkCreate failed", __FUNCTION__);
    ret = false;
  }
  return ret;
}

void CVideoSyncOsx::DeinitDisplayLink()
{
  Cocoa_CVDisplayLinkRelease();
}