yattee/Shared/Player/MPV/MPVOGLView.swift

141 lines
5.0 KiB
Swift
Raw Permalink Normal View History

2022-02-16 20:23:11 +00:00
import GLKit
import Libmpv
2022-03-27 11:42:20 +00:00
import Logging
2023-09-23 14:42:46 +00:00
import OpenGLES
2022-02-16 20:23:11 +00:00
final class MPVOGLView: GLKView {
2022-03-27 11:42:20 +00:00
private var logger = Logger(label: "stream.yattee.mpv.oglview")
2022-02-16 20:23:11 +00:00
private var defaultFBO: GLint?
private var displayLink: CADisplayLink?
2022-02-16 20:23:11 +00:00
var mpvGL: UnsafeMutableRawPointer?
var queue = DispatchQueue(label: "stream.yattee.opengl", qos: .userInteractive)
2022-02-16 20:23:11 +00:00
var needsDrawing = true
override init(frame: CGRect) {
guard let context = EAGLContext(api: .openGLES2) else {
2022-02-16 20:23:11 +00:00
print("Failed to initialize OpenGLES 2.0 context")
exit(1)
}
2022-03-27 11:42:20 +00:00
logger.info("frame size: \(frame.width) x \(frame.height)")
2022-02-16 20:23:11 +00:00
super.init(frame: frame, context: context)
self.context = context
bindDrawable()
2022-02-16 20:23:11 +00:00
defaultFBO = -1
isOpaque = true
enableSetNeedsDisplay = false
2022-02-16 20:23:11 +00:00
fillBlack()
setupDisplayLink()
setupNotifications()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupDisplayLink()
setupNotifications()
}
private func setupDisplayLink() {
displayLink = CADisplayLink(target: self, selector: #selector(updateFrame))
displayLink?.add(to: .main, forMode: .common)
}
// Set up observers to detect display changes and custom refresh rate updates.
private func setupNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(updateDisplayLinkFromNotification(_:)), name: .updateDisplayLinkFrameRate, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(screenDidChange), name: UIScreen.didConnectNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(screenDidChange), name: UIScreen.didDisconnectNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(screenDidChange), name: UIScreen.modeDidChangeNotification, object: nil)
}
@objc private func screenDidChange(_: Notification) {
// Update the display link refresh rate when the screen configuration changes
updateDisplayLinkFrameRate()
}
// Update the display link frame rate from the notification.
@objc private func updateDisplayLinkFromNotification(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let refreshRate = userInfo["refreshRate"] as? Int else { return }
displayLink?.preferredFramesPerSecond = refreshRate
logger.info("Updated CADisplayLink frame rate to: \(refreshRate) from backend notification.")
}
// Update the display link's preferred frame rate based on the current screen refresh rate.
private func updateDisplayLinkFrameRate() {
guard let displayLink else { return }
let refreshRate = getScreenRefreshRate()
displayLink.preferredFramesPerSecond = refreshRate
logger.info("Updated CADisplayLink preferred frames per second to: \(refreshRate)")
}
// Retrieve the screen's current refresh rate dynamically.
private func getScreenRefreshRate() -> Int {
// Use the main screen's maximumFramesPerSecond property
let refreshRate = UIScreen.main.maximumFramesPerSecond
logger.info("Screen refresh rate: \(refreshRate) Hz")
return refreshRate
}
@objc private func updateFrame() {
// Trigger the drawing process if needed
if needsDrawing {
setNeedsDisplay()
}
}
deinit {
// Invalidate the display link and remove observers to avoid memory leaks
displayLink?.invalidate()
NotificationCenter.default.removeObserver(self)
2022-02-16 20:23:11 +00:00
}
func fillBlack() {
glClearColor(0, 0, 0, 0)
glClear(UInt32(GL_COLOR_BUFFER_BIT))
}
2022-03-27 11:42:20 +00:00
override func draw(_: CGRect) {
guard needsDrawing, let mpvGL else { return }
2022-06-16 00:03:15 +00:00
// Bind the default framebuffer
2022-02-16 20:23:11 +00:00
glGetIntegerv(UInt32(GL_FRAMEBUFFER_BINDING), &defaultFBO!)
// Get the current viewport dimensions
2022-03-27 11:42:20 +00:00
var dims: [GLint] = [0, 0, 0, 0]
glGetIntegerv(GLenum(GL_VIEWPORT), &dims)
// Set up the OpenGL FBO data
2022-06-16 00:03:15 +00:00
var data = mpv_opengl_fbo(
fbo: Int32(defaultFBO!),
w: Int32(dims[2]),
h: Int32(dims[3]),
internal_format: 0
)
// Flip Y coordinate for proper rendering
2022-06-16 00:03:15 +00:00
var flip: CInt = 1
// Render with the provided OpenGL FBO parameters
withUnsafeMutablePointer(to: &flip) { flipPtr in
withUnsafeMutablePointer(to: &data) { dataPtr in
2022-06-16 00:03:15 +00:00
var params = [
mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: dataPtr),
mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: flipPtr),
2022-06-16 00:03:15 +00:00
mpv_render_param()
]
mpv_render_context_render(OpaquePointer(mpvGL), &params)
2022-02-16 20:23:11 +00:00
}
}
}
}
2022-02-16 20:23:11 +00:00
extension Notification.Name {
static let updateDisplayLinkFrameRate = Notification.Name("updateDisplayLinkFrameRate")
2022-02-16 20:23:11 +00:00
}