diff options
Diffstat (limited to 'video')
46 files changed, 1435 insertions, 724 deletions
diff --git a/video/mp_image.c b/video/mp_image.c index eadff83e5b..92c3e57b95 100644 --- a/video/mp_image.c +++ b/video/mp_image.c @@ -812,6 +812,12 @@ void mp_image_params_guess_csp(struct mp_image_params *params) } } + if (!mp_trc_is_hdr(params->color.gamma)) { + // Some clips have leftover HDR metadata after conversion to SDR, so to + // avoid blowing up the tone mapping code, strip/sanitize it + params->color.sig_peak = 1.0; + } + if (params->chroma_location == MP_CHROMA_AUTO) { if (params->color.levels == MP_CSP_LEVELS_TV) params->chroma_location = MP_CHROMA_LEFT; diff --git a/video/out/cocoa-cb/events_view.swift b/video/out/cocoa-cb/events_view.swift index 7cc6a4022d..5a84d27b2b 100644 --- a/video/out/cocoa-cb/events_view.swift +++ b/video/out/cocoa-cb/events_view.swift @@ -31,9 +31,9 @@ class EventsView: NSView { override var acceptsFirstResponder: Bool { return true } - init(frame frameRect: NSRect, cocoaCB ccb: CocoaCB) { + init(cocoaCB ccb: CocoaCB) { cocoaCB = ccb - super.init(frame: frameRect) + super.init(frame: NSMakeRect(0, 0, 960, 480)) autoresizingMask = [.viewWidthSizable, .viewHeightSizable] wantsBestResolutionOpenGLSurface = true register(forDraggedTypes: [NSFilenamesPboardType, NSURLPboardType]) @@ -233,7 +233,7 @@ class EventsView: NSView { topMargin = cocoaCB.window.titleBarHeight + 1 + menuBarHeight } - var vF = window!.screen!.frame + guard var vF = window?.screen?.frame else { return false } vF.size.height -= topMargin let vFW = window!.convertFromScreen(vF) @@ -249,6 +249,7 @@ class EventsView: NSView { } func canHideCursor() -> Bool { + if cocoaCB.window == nil { return false } return !hasMouseDown && containsMouseLocation() && window!.isKeyWindow } diff --git a/video/out/cocoa-cb/video_layer.swift b/video/out/cocoa-cb/video_layer.swift index 2e347ba22e..ccc84f8fa8 100644 --- a/video/out/cocoa-cb/video_layer.swift +++ b/video/out/cocoa-cb/video_layer.swift @@ -28,10 +28,10 @@ class VideoLayer: CAOpenGLLayer { let videoLock = NSLock() let displayLock = NSLock() - var hasVideo: Bool = false var needsFlip: Bool = false - var canDrawOffScreen: Bool = false + var forceDraw: Bool = false var cglContext: CGLContextObj? = nil + var cglPixelFormat: CGLPixelFormatObj? = nil var surfaceSize: NSSize? enum Draw: Int { case normal = 1, atomic, atomicEnd } @@ -52,7 +52,7 @@ class VideoLayer: CAOpenGLLayer { if inLiveResize { isAsynchronous = true } - update() + update(force: true) } } @@ -61,7 +61,16 @@ class VideoLayer: CAOpenGLLayer { super.init() autoresizingMask = [.layerWidthSizable, .layerHeightSizable] backgroundColor = NSColor.black.cgColor - contentsScale = cocoaCB.window.backingScaleFactor + + cglPixelFormat = copyCGLPixelFormat(forDisplayMask: 0) + CGLCreateContext(cglPixelFormat!, nil, &cglContext) + var i: GLint = 1 + CGLSetParameter(cglContext!, kCGLCPSwapInterval, &i) + CGLSetCurrentContext(cglContext!) + + mpv.initRender() + mpv.setRenderUpdateCallback(updateCallback, context: self) + mpv.setRenderControlCallback(cocoaCB.controlCallback, context: cocoaCB) } override init(layer: Any) { @@ -74,12 +83,6 @@ class VideoLayer: CAOpenGLLayer { fatalError("init(coder:) has not been implemented") } - func setUpRender() { - mpv.initRender() - mpv.setRenderUpdateCallback(updateCallback, context: self) - mpv.setRenderControlCallback(cocoaCB.controlCallback, context: cocoaCB) - } - override func canDraw(inCGLContext ctx: CGLContextObj, pixelFormat pf: CGLPixelFormatObj, forLayerTime t: CFTimeInterval, @@ -87,7 +90,8 @@ class VideoLayer: CAOpenGLLayer { if inLiveResize == false { isAsynchronous = false } - return mpv != nil && cocoaCB.backendState == .init + return mpv != nil && cocoaCB.backendState == .initialized && + (forceDraw || mpv.isRenderUpdateFrame()) } override func draw(inCGLContext ctx: CGLContextObj, @@ -95,11 +99,8 @@ class VideoLayer: CAOpenGLLayer { forLayerTime t: CFTimeInterval, displayTime ts: UnsafePointer<CVTimeStamp>?) { needsFlip = false - canDrawOffScreen = true - draw(ctx) - } + forceDraw = false - func draw(_ ctx: CGLContextObj) { if draw.rawValue >= Draw.atomic.rawValue { if draw == .atomic { draw = .atomicEnd @@ -131,7 +132,7 @@ class VideoLayer: CAOpenGLLayer { } func atomicDrawingStart() { - if draw == .normal && hasVideo { + if draw == .normal { NSDisableScreenUpdates() draw = .atomic } @@ -145,6 +146,20 @@ class VideoLayer: CAOpenGLLayer { } override func copyCGLPixelFormat(forDisplayMask mask: UInt32) -> CGLPixelFormatObj { + if cglPixelFormat != nil { return cglPixelFormat! } + + let attributeLookUp: [UInt32:String] = [ + kCGLOGLPVersion_3_2_Core.rawValue: "kCGLOGLPVersion_3_2_Core", + kCGLOGLPVersion_Legacy.rawValue: "kCGLOGLPVersion_Legacy", + kCGLPFAOpenGLProfile.rawValue: "kCGLPFAOpenGLProfile", + kCGLPFAAccelerated.rawValue: "kCGLPFAAccelerated", + kCGLPFADoubleBuffer.rawValue: "kCGLPFADoubleBuffer", + kCGLPFABackingStore.rawValue: "kCGLPFABackingStore", + kCGLPFAAllowOfflineRenderers.rawValue: "kCGLPFAAllowOfflineRenderers", + kCGLPFASupportsAutomaticGraphicsSwitching.rawValue: "kCGLPFASupportsAutomaticGraphicsSwitching", + 0: "" + ] + let glVersions: [CGLOpenGLProfile] = [ kCGLOGLPVersion_3_2_Core, kCGLOGLPVersion_Legacy @@ -155,6 +170,8 @@ class VideoLayer: CAOpenGLLayer { var npix: GLint = 0 verLoop : for ver in glVersions { + if mpv.macOpts!.cocoa_cb_sw_renderer == 1 { break } + var glAttributes: [CGLPixelFormatAttribute] = [ kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(ver.rawValue), kCGLPFAAccelerated, @@ -170,30 +187,48 @@ class VideoLayer: CAOpenGLLayer { if err == kCGLBadAttribute || err == kCGLBadPixelFormat || pix == nil { glAttributes.remove(at: index) } else { + var attArray = glAttributes.map({ (value: _CGLPixelFormatAttribute) -> String in + return attributeLookUp[value.rawValue]! + }) + attArray.removeLast() + + mpv.sendVerbose("Created CGL pixel format with attributes: " + + "\(attArray.joined(separator: ", "))") break verLoop } } } + if (err != kCGLNoError || pix == nil) && mpv.macOpts!.cocoa_cb_sw_renderer != 0 { + if mpv.macOpts!.cocoa_cb_sw_renderer == -1 { + let errS = String(cString: CGLErrorString(err)) + mpv.sendWarning("Couldn't create hardware accelerated CGL " + + "pixel format, falling back to software " + + "renderer: \(errS) (\(err.rawValue))") + } + + let glAttributes: [CGLPixelFormatAttribute] = [ + kCGLPFAOpenGLProfile, CGLPixelFormatAttribute(kCGLOGLPVersion_3_2_Core.rawValue), + kCGLPFARendererID, CGLPixelFormatAttribute(UInt32(kCGLRendererGenericFloatID)), + kCGLPFADoubleBuffer, + kCGLPFABackingStore, + _CGLPixelFormatAttribute(rawValue: 0) + ] + + err = CGLChoosePixelFormat(glAttributes, &pix, &npix) + } + if err != kCGLNoError || pix == nil { let errS = String(cString: CGLErrorString(err)) - print("Couldn't create CGL pixel format: \(errS) (\(err.rawValue))") + mpv.sendError("Couldn't create any CGL pixel format: \(errS) (\(err.rawValue))") exit(1) } 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 + contentsScale = cocoaCB.window.backingScaleFactor + return cglContext! } let updateCallback: mpv_render_update_fn = { (ctx) in @@ -206,33 +241,22 @@ class VideoLayer: CAOpenGLLayer { let isUpdate = needsFlip super.display() CATransaction.flush() - if isUpdate { - if !cocoaCB.window.occlusionState.contains(.visible) && - needsFlip && canDrawOffScreen - { - CGLSetCurrentContext(cglContext!) - draw(cglContext!) - } else if needsFlip { - update() + if isUpdate && needsFlip { + CGLSetCurrentContext(cglContext!) + if mpv.isRenderUpdateFrame() { + mpv.drawRender(NSZeroSize, skip: true) } } displayLock.unlock() } - func setVideo(_ state: Bool) { - videoLock.lock() - hasVideo = state - videoLock.unlock() - } - - func update() { + func update(force: Bool = false) { + if force { forceDraw = true } queue.async { - self.videoLock.lock() - if !self.inLiveResize && self.hasVideo { + if self.forceDraw || !self.inLiveResize { self.needsFlip = true self.display() } - self.videoLock.unlock() } } diff --git a/video/out/cocoa-cb/window.swift b/video/out/cocoa-cb/window.swift index b3db7aeb8f..d11706f38b 100644 --- a/video/out/cocoa-cb/window.swift +++ b/video/out/cocoa-cb/window.swift @@ -17,6 +17,31 @@ import Cocoa +class CustomTtitleBar: NSVisualEffectView { + + // catch these events so they are not propagated to the underlying view + override func mouseDown(with event: NSEvent) { } + + override func mouseUp(with event: NSEvent) { + if event.clickCount > 1 { + let def = UserDefaults.standard + var action = def.string(forKey: "AppleActionOnDoubleClick") + + // macOS 10.10 and earlier + if action == nil { + action = def.bool(forKey: "AppleMiniaturizeOnDoubleClick") == true ? + "Minimize" : "Maximize" + } + + if action == "Minimize" { + window!.miniaturize(self) + } else if action == "Maximize" { + window!.zoom(self) + } + } + } +} + class Window: NSWindow, NSWindowDelegate { weak var cocoaCB: CocoaCB! = nil @@ -78,28 +103,23 @@ class Window: NSWindow, NSWindowDelegate { } } - convenience init(cocoaCB ccb: CocoaCB) { - self.init(contentRect: NSMakeRect(0, 0, 960, 480), - styleMask: [.titled, .closable, .miniaturizable, .resizable], - backing: .buffered, defer: false, screen: NSScreen.main()) - cocoaCB = ccb - title = "mpv" - } - - convenience init(contentRect: NSRect, styleMask style: NSWindowStyleMask, - screen: NSScreen?, cocoaCB ccb: CocoaCB) - { - self.init(contentRect: contentRect, styleMask: style, + convenience init(contentRect: NSRect, screen: NSScreen?, view: NSView, cocoaCB ccb: CocoaCB) { + self.init(contentRect: contentRect, + styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false, screen: screen) cocoaCB = ccb + title = cocoaCB.title minSize = NSMakeSize(160, 90) collectionBehavior = .fullScreenPrimary delegate = self + contentView!.addSubview(view) + view.frame = contentView!.frame unfsContentFrame = convertToScreen(contentView!.frame) targetScreen = screen! currentScreen = screen! unfScreen = screen! + initTitleBar() if let app = NSApp as? Application { app.menuBar.register(#selector(setHalfWindowSize), for: MPM_H_SIZE) @@ -118,19 +138,36 @@ class Window: NSWindow, NSWindowDelegate { styleMask.insert(.fullSizeContentView) titleBar.alphaValue = 0 titlebarAppearsTransparent = true - titleBarEffect = NSVisualEffectView(frame: f) + titleBarEffect = CustomTtitleBar(frame: f) titleBarEffect!.alphaValue = 0 titleBarEffect!.blendingMode = .withinWindow titleBarEffect!.autoresizingMask = [.viewWidthSizable, .viewMinYMargin] - setTitleBarStyle(mpv.getStringProperty("macos-title-bar-style") ?? "dark") + setTitleBarStyle(Int(mpv.macOpts!.macos_title_bar_style)) contentView!.addSubview(titleBarEffect!, positioned: .above, relativeTo: nil) - - border = mpv.getBoolProperty("border") } - func setTitleBarStyle(_ style: String) { - var effect = style + func setTitleBarStyle(_ style: Any) { + var effect: String + + if style is Int { + switch style as! Int { + case 4: + effect = "auto" + case 3: + effect = "mediumlight" + case 2: + effect = "light" + case 1: + effect = "ultradark" + case 0: fallthrough + default: + effect = "dark" + } + } else { + effect = style as! String + } + if effect == "auto" { let systemStyle = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") effect = systemStyle == nil ? "mediumlight" : "ultradark" @@ -167,6 +204,7 @@ class Window: NSWindow, NSWindowDelegate { titleBar.animator().alphaValue = 1 if !isInFullscreen && !isAnimating { titleBarEffect!.animator().alphaValue = 1 + titleBarEffect!.isHidden = false } }, completionHandler: nil ) @@ -181,6 +219,7 @@ class Window: NSWindow, NSWindowDelegate { if titleBarEffect == nil { return } if isInFullscreen && !isAnimating { titleBarEffect!.alphaValue = 0 + titleBarEffect!.isHidden = true return } NSAnimationContext.runAnimationGroup({ (context) -> Void in @@ -189,6 +228,7 @@ class Window: NSWindow, NSWindowDelegate { titleBarEffect!.animator().alphaValue = 0 }, completionHandler: { self.titleButtons.forEach { $0.isHidden = true } + self.titleBarEffect!.isHidden = true }) } @@ -297,7 +337,7 @@ class Window: NSWindow, NSWindowDelegate { } func endAnimation(_ newFrame: NSRect = NSZeroRect) { - if !NSEqualRects(newFrame, NSZeroRect) { + if !NSEqualRects(newFrame, NSZeroRect) && isAnimating { NSAnimationContext.runAnimationGroup({ (context) -> Void in context.duration = 0.01 self.animator().setFrame(newFrame, display: true) @@ -339,16 +379,26 @@ class Window: NSWindow, NSWindowDelegate { } } - func setOnTop(_ state: Bool) { + func setOnTop(_ state: Bool, _ ontopLevel: Any) { if state { - let ontopLevel = mpv.getStringProperty("ontop-level") ?? "window" - switch ontopLevel { - case "window": - level = Int(CGWindowLevelForKey(.floatingWindow)) - case "system": - level = Int(CGWindowLevelForKey(.statusWindow))+1 - default: - level = Int(ontopLevel)! + if ontopLevel is Int { + switch ontopLevel as! Int { + case -1: + level = Int(CGWindowLevelForKey(.floatingWindow)) + case -2: + level = Int(CGWindowLevelForKey(.statusWindow))+1 + default: + level = ontopLevel as! Int + } + } else { + switch ontopLevel as! String { + case "window": + level = Int(CGWindowLevelForKey(.floatingWindow)) + case "system": + level = Int(CGWindowLevelForKey(.statusWindow))+1 + default: + level = Int(ontopLevel as! String)! + } } collectionBehavior.remove(.transient) collectionBehavior.insert(.managed) @@ -563,6 +613,12 @@ class Window: NSWindow, NSWindowDelegate { cocoaCB.updateCusorVisibility() } + func windowDidChangeOcclusionState(_ notification: Notification) { + if occlusionState.contains(.visible) { + cocoaCB.layer.update(force: true) + } + } + func windowWillMove(_ notification: Notification) { isMoving = true } diff --git a/video/out/cocoa/events_view.m b/video/out/cocoa/events_view.m index 4a839b727e..2cfe3f2fda 100644 --- a/video/out/cocoa/events_view.m +++ b/video/out/cocoa/events_view.m @@ -46,6 +46,12 @@ return self; } +- (void)drawRect:(NSRect)rect +{ + [[NSColor blackColor] setFill]; + NSRectFill(rect); +} + // mpv uses flipped coordinates, because X11 uses those. So let's just use them // as well without having to do any coordinate conversion of mouse positions. - (BOOL)isFlipped { return YES; } diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 659f9f84ce..355fa537e1 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -30,12 +30,16 @@ class CocoaCB: NSObject { var cursorVisibilityWanted: Bool = true var isShuttingDown: Bool = false + var title: String = "mpv" { + didSet { if window != nil { window.title = title } } + } + enum State { - case uninit + case uninitialized case needsInit - case `init` + case initialized } - var backendState: State = .uninit + var backendState: State = .uninitialized let eventsLock = NSLock() var events: Int = 0 @@ -47,73 +51,61 @@ class CocoaCB: NSObject { let queue: DispatchQueue = DispatchQueue(label: "io.mpv.queue") - override init() { - super.init() - window = Window(cocoaCB: self) - - view = EventsView(frame: window.contentView!.bounds, cocoaCB: self) - window.contentView!.addSubview(view) - + convenience init(_ mpvHandle: OpaquePointer) { + self.init() + mpv = MPVHelper(mpvHandle) layer = VideoLayer(cocoaCB: self) - view.layer = layer - view.wantsLayer = true - view.layerContentsPlacement = .scaleProportionallyToFit - } - - func setMpvHandle(_ ctx: OpaquePointer) { - mpv = MPVHelper(ctx) - layer.setUpRender() } - func preinit() { - if backendState == .uninit { + func preinit(_ vo: UnsafeMutablePointer<vo>) { + if backendState == .uninitialized { backendState = .needsInit - DispatchQueue.main.async { - self.updateICCProfile() - } - startDisplayLink() + + view = EventsView(cocoaCB: self) + view.layer = layer + view.wantsLayer = true + view.layerContentsPlacement = .scaleProportionallyToFit + startDisplayLink(vo) + initLightSensor() + addDisplayReconfigureObserver() } } func uninit() { - layer.setVideo(false) window.orderOut(nil) } - func reconfig() { + func reconfig(_ vo: UnsafeMutablePointer<vo>) { if backendState == .needsInit { - initBackend() + DispatchQueue.main.sync { self.initBackend(vo) } } else { - layer.setVideo(true) - updateWindowSize() - layer.update() + DispatchQueue.main.async { + self.updateWindowSize(vo) + self.layer.update() + } } } - func initBackend() { + func initBackend(_ vo: UnsafeMutablePointer<vo>) { + let opts: mp_vo_opts = vo.pointee.opts.pointee NSApp.setActivationPolicy(.regular) + setAppIcon() - let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main() - let wr = getWindowGeometry(forScreen: targetScreen!, videoOut: mpv.mpctx!.pointee.video_out) - let win = Window(contentRect: wr, styleMask: window.styleMask, - screen: targetScreen, cocoaCB: self) - win.title = window.title - win.setOnTop(mpv.getBoolProperty("ontop")) - win.keepAspect = mpv.getBoolProperty("keepaspect-window") - window.close() - window = win - window.contentView!.addSubview(view) - view.frame = window.contentView!.frame - window.initTitleBar() + let targetScreen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main() + let wr = getWindowGeometry(forScreen: targetScreen!, videoOut: vo) + window = Window(contentRect: wr, screen: targetScreen, view: view, cocoaCB: self) + updateICCProfile() + window.setOnTop(Bool(opts.ontop), Int(opts.ontop_level)) + window.keepAspect = Bool(opts.keepaspect_window) + window.title = title + window.border = Bool(opts.border) - setAppIcon() window.isRestorable = false window.makeMain() window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) - layer.setVideo(true) - if mpv.getBoolProperty("fullscreen") { + if Bool(opts.fullscreen) { DispatchQueue.main.async { self.window.toggleFullScreen(nil) } @@ -121,14 +113,13 @@ class CocoaCB: NSObject { window.isMovableByWindowBackground = true } - initLightSensor() - addDisplayReconfigureObserver() - backendState = .init + backendState = .initialized } - func updateWindowSize() { - let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main() - let wr = |