Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331/* * 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 Cocoaimport OpenGL.GLimport OpenGL.GL3
let glVersions: [CGLOpenGLProfile] = [ kCGLOGLPVersion_3_2_Core, kCGLOGLPVersion_Legacy]
let glFormatBase: [CGLPixelFormatAttribute] = [ kCGLPFAOpenGLProfile, kCGLPFAAccelerated, kCGLPFADoubleBuffer]
let glFormatSoftwareBase: [CGLPixelFormatAttribute] = [ kCGLPFAOpenGLProfile, kCGLPFARendererID, CGLPixelFormatAttribute(UInt32(kCGLRendererGenericFloatID)), kCGLPFADoubleBuffer]
let glFormatOptional: [[CGLPixelFormatAttribute]] = [ [kCGLPFABackingStore], [kCGLPFAAllowOfflineRenderers]]
let glFormat10Bit: [CGLPixelFormatAttribute] = [ kCGLPFAColorSize, _CGLPixelFormatAttribute(rawValue: 64), kCGLPFAColorFloat]
let glFormatAutoGPU: [CGLPixelFormatAttribute] = [ kCGLPFASupportsAutomaticGraphicsSwitching]
let attributeLookUp: [UInt32: String] = [ kCGLOGLPVersion_3_2_Core.rawValue: "kCGLOGLPVersion_3_2_Core", kCGLOGLPVersion_Legacy.rawValue: "kCGLOGLPVersion_Legacy", kCGLPFAOpenGLProfile.rawValue: "kCGLPFAOpenGLProfile", UInt32(kCGLRendererGenericFloatID): "kCGLRendererGenericFloatID", kCGLPFARendererID.rawValue: "kCGLPFARendererID", kCGLPFAAccelerated.rawValue: "kCGLPFAAccelerated", kCGLPFADoubleBuffer.rawValue: "kCGLPFADoubleBuffer", kCGLPFABackingStore.rawValue: "kCGLPFABackingStore", kCGLPFAColorSize.rawValue: "kCGLPFAColorSize", kCGLPFAColorFloat.rawValue: "kCGLPFAColorFloat", kCGLPFAAllowOfflineRenderers.rawValue: "kCGLPFAAllowOfflineRenderers", kCGLPFASupportsAutomaticGraphicsSwitching.rawValue: "kCGLPFASupportsAutomaticGraphicsSwitching"]
class GLLayer: CAOpenGLLayer { unowned var cocoaCB: CocoaCB var libmpv: LibmpvHelper { return cocoaCB.libmpv }
let displayLock = NSLock() let cglContext: CGLContextObj let cglPixelFormat: CGLPixelFormatObj var needsFlip: Bool = false var forceDraw: Bool = false var surfaceSize: NSSize = NSSize(width: 0, height: 0) var bufferDepth: GLint = 8
enum Draw: Int { case normal = 1, atomic, atomicEnd } var draw: Draw = .normal
var needsICCUpdate: Bool = false { didSet { if needsICCUpdate == true { update() } } }
var inLiveResize: Bool = false { didSet { if inLiveResize { isAsynchronous = true } update(force: true) } }
init(cocoaCB ccb: CocoaCB) { cocoaCB = ccb (cglPixelFormat, bufferDepth) = GLLayer.createPixelFormat(ccb) cglContext = GLLayer.createContext(ccb, cglPixelFormat) super.init() autoresizingMask = [.layerWidthSizable, .layerHeightSizable] backgroundColor = NSColor.black.cgColor
if bufferDepth > 8 { contentsFormat = .RGBA16Float }
var i: GLint = 1 CGLSetParameter(cglContext, kCGLCPSwapInterval, &i) CGLSetCurrentContext(cglContext)
libmpv.initRender() libmpv.setRenderUpdateCallback(updateCallback, context: self) libmpv.setRenderControlCallback(cocoaCB.controlCallback, context: cocoaCB) }
// necessary for when the layer containing window changes the screen override init(layer: Any) { guard let oldLayer = layer as? GLLayer else { fatalError("init(layer: Any) passed an invalid layer") } cocoaCB = oldLayer.cocoaCB surfaceSize = oldLayer.surfaceSize cglPixelFormat = oldLayer.cglPixelFormat cglContext = oldLayer.cglContext super.init() }
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func canDraw(inCGLContext ctx: CGLContextObj, pixelFormat pf: CGLPixelFormatObj, forLayerTime t: CFTimeInterval, displayTime ts: UnsafePointer<CVTimeStamp>?) -> Bool { if inLiveResize == false { isAsynchronous = false } return cocoaCB.backendState == .initialized && (forceDraw || libmpv.isRenderUpdateFrame()) }
override func draw(inCGLContext ctx: CGLContextObj, pixelFormat pf: CGLPixelFormatObj, forLayerTime t: CFTimeInterval, displayTime ts: UnsafePointer<CVTimeStamp>?) { needsFlip = false forceDraw = false
if draw.rawValue >= Draw.atomic.rawValue { if draw == .atomic { draw = .atomicEnd } else { atomicDrawingEnd() } }
updateSurfaceSize() libmpv.drawRender(surfaceSize, bufferDepth, ctx)
if needsICCUpdate { needsICCUpdate = false cocoaCB.updateICCProfile() } }
func updateSurfaceSize() { var dims: [GLint] = [0, 0, 0, 0] glGetIntegerv(GLenum(GL_VIEWPORT), &dims) surfaceSize = NSSize(width: CGFloat(dims[2]), height: CGFloat(dims[3]))
if surfaceSize == NSSize.zero { surfaceSize = bounds.size surfaceSize.width *= contentsScale surfaceSize.height *= contentsScale } }
func atomicDrawingStart() { if draw == .normal { NSDisableScreenUpdates() draw = .atomic } }
func atomicDrawingEnd() { if draw.rawValue >= Draw.atomic.rawValue { NSEnableScreenUpdates() draw = .normal } }
func lockCglContext() { CGLLockContext(cglContext) }
func unlockCglContext() { CGLUnlockContext(cglContext) }
override func copyCGLPixelFormat(forDisplayMask mask: UInt32) -> CGLPixelFormatObj { return cglPixelFormat }
override func copyCGLContext(forPixelFormat pf: CGLPixelFormatObj) -> CGLContextObj { contentsScale = cocoaCB.window?.backingScaleFactor ?? 1.0 return cglContext }
let updateCallback: mpv_render_update_fn = { (ctx) in let layer: GLLayer = unsafeBitCast(ctx, to: GLLayer.self) layer.update() }
override func display() { displayLock.lock() let isUpdate = needsFlip super.display() CATransaction.flush() if isUpdate && needsFlip { lockCglContext() CGLSetCurrentContext(cglContext) if libmpv.isRenderUpdateFrame() { libmpv.drawRender(NSSize.zero, bufferDepth, cglContext, skip: true) } unlockCglContext() } displayLock.unlock() }
func update(force: Bool = false) { if force { forceDraw = true } DispatchQueue.main.async { if self.forceDraw || !self.inLiveResize { self.needsFlip = true self.display() } } }
class func createPixelFormat(_ ccb: CocoaCB) -> (CGLPixelFormatObj, GLint) { var pix: CGLPixelFormatObj? var depth: GLint = 8 var err: CGLError = CGLError(rawValue: 0) let swRender = ccb.option.mac.cocoa_cb_sw_renderer
if swRender != 1 { (pix, depth, err) = GLLayer.findPixelFormat(ccb) }
if (err != kCGLNoError || pix == nil) && swRender != 0 { (pix, depth, err) = GLLayer.findPixelFormat(ccb, software: true) }
guard let pixelFormat = pix, err == kCGLNoError else { ccb.log.error("Couldn't create any CGL pixel format") exit(1) }
return (pixelFormat, depth) }
class func findPixelFormat(_ ccb: CocoaCB, software: Bool = false) -> (CGLPixelFormatObj?, GLint, CGLError) { var pix: CGLPixelFormatObj? var err: CGLError = CGLError(rawValue: 0) var npix: GLint = 0
for ver in glVersions { var glBase = software ? glFormatSoftwareBase : glFormatBase glBase.insert(CGLPixelFormatAttribute(ver.rawValue), at: 1)
var glFormat = [glBase] if ccb.option.mac.cocoa_cb_10bit_context { glFormat += [glFormat10Bit] } glFormat += glFormatOptional
if !ccb.option.mac.macos_force_dedicated_gpu { glFormat += [glFormatAutoGPU] }
for index in stride(from: glFormat.count-1, through: 0, by: -1) { let format = glFormat.flatMap { $0 } + [_CGLPixelFormatAttribute(rawValue: 0)] err = CGLChoosePixelFormat(format, &pix, &npix)
if err == kCGLBadAttribute || err == kCGLBadPixelFormat || pix == nil { glFormat.remove(at: index) } else { let attArray = format.map({ (value: _CGLPixelFormatAttribute) -> String in return attributeLookUp[value.rawValue] ?? String(value.rawValue) })
ccb.log.verbose("Created CGL pixel format with attributes: " + "\(attArray.joined(separator: ", "))") return (pix, glFormat.contains(glFormat10Bit) ? 16 : 8, err) } } }
let errS = String(cString: CGLErrorString(err)) ccb.log.warning("Couldn't create a " + "\(software ? "software" : "hardware accelerated") " + "CGL pixel format: \(errS) (\(err.rawValue))") if software == false && ccb.option.mac.cocoa_cb_sw_renderer == -1 { ccb.log.warning("Falling back to software renderer") }
return (pix, 8, err) }
class func createContext(_ ccb: CocoaCB, _ pixelFormat: CGLPixelFormatObj) -> CGLContextObj { var context: CGLContextObj? let error = CGLCreateContext(pixelFormat, nil, &context)
guard let cglContext = context, error == kCGLNoError else { let errS = String(cString: CGLErrorString(error)) ccb.log.error("Couldn't create a CGLContext: " + errS) exit(1) }
return cglContext }}