2022-02-27 20:31:17 +00:00
|
|
|
import Cocoa
|
2023-09-23 14:42:46 +00:00
|
|
|
import MPVKit
|
2022-02-27 20:31:17 +00:00
|
|
|
import OpenGL.GL
|
|
|
|
import OpenGL.GL3
|
|
|
|
|
|
|
|
final class VideoLayer: CAOpenGLLayer {
|
|
|
|
var client: MPVClient!
|
|
|
|
|
|
|
|
override init() {
|
|
|
|
super.init()
|
|
|
|
autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
|
|
|
|
backgroundColor = NSColor.black.cgColor
|
|
|
|
}
|
|
|
|
|
|
|
|
@available(*, unavailable)
|
|
|
|
required init?(coder _: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func canDraw(
|
|
|
|
inCGLContext _: CGLContextObj,
|
|
|
|
pixelFormat _: CGLPixelFormatObj,
|
|
|
|
forLayerTime _: CFTimeInterval,
|
|
|
|
displayTime _: UnsafePointer<CVTimeStamp>?
|
|
|
|
) -> Bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
override func draw(
|
|
|
|
inCGLContext ctx: CGLContextObj,
|
|
|
|
pixelFormat _: CGLPixelFormatObj,
|
|
|
|
forLayerTime _: CFTimeInterval,
|
|
|
|
displayTime _: UnsafePointer<CVTimeStamp>?
|
|
|
|
) {
|
|
|
|
var i: GLint = 0
|
|
|
|
var flip: CInt = 1
|
|
|
|
var ditherDepth = 8
|
|
|
|
glGetIntegerv(GLenum(GL_DRAW_FRAMEBUFFER_BINDING), &i)
|
|
|
|
|
|
|
|
if client.mpvGL != nil {
|
|
|
|
var data = mpv_opengl_fbo(fbo: Int32(i),
|
|
|
|
w: Int32(bounds.size.width),
|
|
|
|
h: Int32(bounds.size.height),
|
|
|
|
internal_format: 0)
|
|
|
|
var params: [mpv_render_param] = [
|
|
|
|
mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: &data),
|
|
|
|
mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: &flip),
|
|
|
|
mpv_render_param(type: MPV_RENDER_PARAM_DEPTH, data: &ditherDepth),
|
|
|
|
mpv_render_param()
|
|
|
|
]
|
|
|
|
mpv_render_context_render(client.mpvGL, ¶ms)
|
|
|
|
} else {
|
|
|
|
glClearColor(0, 0, 0, 1)
|
|
|
|
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
|
|
|
|
}
|
|
|
|
|
|
|
|
CGLFlushDrawable(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
override func copyCGLPixelFormat(forDisplayMask _: UInt32) -> CGLPixelFormatObj {
|
|
|
|
let attrs: [CGLPixelFormatAttribute] = [
|
|
|
|
kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_3_2_Core.rawValue),
|
|
|
|
kCGLPFADoubleBuffer,
|
|
|
|
kCGLPFAAllowOfflineRenderers,
|
|
|
|
kCGLPFABackingStore,
|
|
|
|
kCGLPFAAccelerated,
|
|
|
|
kCGLPFASupportsAutomaticGraphicsSwitching,
|
|
|
|
_CGLPixelFormatAttribute(rawValue: 0)
|
|
|
|
]
|
|
|
|
|
|
|
|
var npix: GLint = 0
|
|
|
|
var pix: CGLPixelFormatObj?
|
|
|
|
CGLChoosePixelFormat(attrs, &pix, &npix)
|
|
|
|
|
|
|
|
return pix!
|
|
|
|
}
|
|
|
|
|
|
|
|
override func copyCGLContext(forPixelFormat pf: CGLPixelFormatObj) -> CGLContextObj {
|
|
|
|
let ctx = super.copyCGLContext(forPixelFormat: pf)
|
|
|
|
|
|
|
|
var i: GLint = 1
|
|
|
|
CGLSetParameter(ctx, kCGLCPSwapInterval, &i)
|
|
|
|
CGLEnable(ctx, kCGLCEMPEngine)
|
|
|
|
CGLSetCurrentContext(ctx)
|
|
|
|
|
|
|
|
client.create()
|
|
|
|
initDisplayLink()
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
override func display() {
|
|
|
|
super.display()
|
|
|
|
CATransaction.flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
let displayLinkCallback: CVDisplayLinkOutputCallback = { _, _, _, _, _, displayLinkContext -> CVReturn in
|
|
|
|
let layer: VideoLayer = unsafeBitCast(displayLinkContext, to: VideoLayer.self)
|
|
|
|
if layer.client?.mpvGL != nil {
|
|
|
|
mpv_render_context_report_swap(layer.client.mpvGL)
|
|
|
|
}
|
|
|
|
return kCVReturnSuccess
|
|
|
|
}
|
|
|
|
|
|
|
|
func initDisplayLink() {
|
|
|
|
let displayId = UInt32(NSScreen.main?.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! Int)
|
|
|
|
|
|
|
|
CVDisplayLinkCreateWithCGDisplay(displayId, &client.link)
|
|
|
|
CVDisplayLinkSetOutputCallback(client.link!, displayLinkCallback,
|
|
|
|
UnsafeMutableRawPointer(Unmanaged.passUnretained(client.layer).toOpaque()))
|
|
|
|
CVDisplayLinkStart(client.link!)
|
|
|
|
}
|
|
|
|
|
|
|
|
func uninitDisplaylink() {
|
|
|
|
if CVDisplayLinkIsRunning(client.link!) {
|
|
|
|
CVDisplayLinkStop(client.link!)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|