diff options
Diffstat (limited to 'video/out/cocoa-cb/video_layer.swift')
-rw-r--r-- | video/out/cocoa-cb/video_layer.swift | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/video/out/cocoa-cb/video_layer.swift b/video/out/cocoa-cb/video_layer.swift new file mode 100644 index 0000000000..05f0894159 --- /dev/null +++ b/video/out/cocoa-cb/video_layer.swift @@ -0,0 +1,210 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +import Cocoa +import OpenGL.GL +import OpenGL.GL3 + +class VideoLayer: CAOpenGLLayer { + + weak var cocoaCB: CocoaCB! + var mpv: MPVHelper! { + get { return cocoaCB == nil ? nil : cocoaCB.mpv } + } + + let videoLock = NSLock() + var hasVideo: Bool = false + var neededFlips: Int = 0 + var cglContext: CGLContextObj? = nil + + var canDrawOffScreen: Bool = false + var lastThread: Thread? = nil + + var needsICCUpdate: Bool = false { + didSet { + if needsICCUpdate == true { + neededFlips += 1 + } + } + } + + let surfaceLock = NSLock() + var surfaceSize: NSSize? + + var inLiveResize: Bool = false { + didSet { + if inLiveResize == false { + isAsynchronous = false + display() + } else { + surfaceLock.lock() + updateSurfaceSize() + surfaceLock.unlock() + isAsynchronous = true + } + } + } + + init(cocoaCB ccb: CocoaCB) { + cocoaCB = ccb + super.init() + autoresizingMask = [.layerWidthSizable, .layerHeightSizable] + backgroundColor = NSColor.black.cgColor + contentsScale = cocoaCB.window.backingScaleFactor + } + + override init(layer: Any) { + let oldLayer = layer as! VideoLayer + cocoaCB = oldLayer.cocoaCB + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setUpGLCB() { + self.mpv.initGLCB() + self.mpv.setGLCBUpdateCallback(self.updateCallback, context: self) + self.mpv.setGLCBControlCallback(self.cocoaCB.controlCallback, context: self.cocoaCB) + } + + override func canDraw(inCGLContext ctx: CGLContextObj, + pixelFormat pf: CGLPixelFormatObj, + forLayerTime t: CFTimeInterval, + displayTime ts: UnsafePointer<CVTimeStamp>?) -> Bool { + return mpv != nil && cocoaCB.backendState == .init + } + + override func draw(inCGLContext ctx: CGLContextObj, + pixelFormat pf: CGLPixelFormatObj, + forLayerTime t: CFTimeInterval, + displayTime ts: UnsafePointer<CVTimeStamp>?) { + neededFlips = 0 + canDrawOffScreen = Thread.current == lastThread + lastThread = Thread.current + draw(ctx) + } + + func draw(_ ctx: CGLContextObj) { + surfaceLock.lock() + if inLiveResize == false { + updateSurfaceSize() + } + + mpv.drawGLCB(surfaceSize!) + surfaceLock.unlock() + CGLFlushDrawable(ctx) + + if needsICCUpdate { + needsICCUpdate = false + cocoaCB.updateICCProfile() + } + } + + func updateSurfaceSize() { + surfaceSize = bounds.size + surfaceSize!.width *= contentsScale + surfaceSize!.height *= contentsScale + } + + override func copyCGLPixelFormat(forDisplayMask mask: UInt32) -> CGLPixelFormatObj { + let glVersions: [CGLOpenGLProfile] = [ + kCGLOGLPVersion_3_2_Core, + kCGLOGLPVersion_Legacy + ] + + var pix: CGLPixelFormatObj? + var err: CGLError = CGLError(rawValue: 0) + var npix: GLint = 0 + + verLoop : for ver in glVersions { + var glAttributes: [CGLPixelFormatAttribute] = [ + kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(ver.rawValue), + kCGLPFAAccelerated, + kCGLPFADoubleBuffer, + kCGLPFABackingStore, + kCGLPFAAllowOfflineRenderers, + kCGLPFASupportsAutomaticGraphicsSwitching, + _CGLPixelFormatAttribute(rawValue: 0) + ] + + for index in stride(from: glAttributes.count-2, through: 4, by: -1) { + err = CGLChoosePixelFormat(glAttributes, &pix, &npix) + if err == kCGLBadAttribute { + glAttributes.remove(at: index) + } else { + break verLoop + } + } + } + + if err != kCGLNoError { + fatalError("Couldn't create CGL pixel format: \(CGLErrorString(err)) (\(err))") + } + return pix! + } + + override func copyCGLContext(forPixelFormat pf: CGLPixelFormatObj) -> CGLContextObj { + let ctx = super.copyCGLContext(forPixelFormat: pf) + var i: GLint = 1 + CGLSetParameter(ctx, kCGLCPSwapInterval, &i) + CGLSetCurrentContext(ctx) + cglContext = ctx + + if let app = NSApp as? Application { + app.initMPVCore() + } + return ctx + } + + let updateCallback: mpv_opengl_cb_update_fn = { (ctx) in + let layer: VideoLayer = MPVHelper.bridge(ptr: ctx!) + layer.neededFlips += 1 + } + + override func display() { + super.display() + if !isAsynchronous { + CATransaction.flush() + } + } + + func setVideo(_ state: Bool) { + videoLock.lock() + hasVideo = state + neededFlips = 0 + videoLock.unlock() + } + + func reportFlip() { + mpv.reportGLCBFlip() + videoLock.lock() + if !isAsynchronous && neededFlips > 0 && hasVideo { + if !cocoaCB.window.occlusionState.contains(.visible) && + neededFlips > 1 && canDrawOffScreen + { + draw(cglContext!) + display() + } else { + display() + } + } + videoLock.unlock() + } + +} |