-
Notifications
You must be signed in to change notification settings - Fork 73
Description
I was taking a look at the PySide/opengl render example that was linked from the Gtk4/Wayland issue:
https://github.com/trin94/qtquick-mpv
Unfortunately my application doesn't use QQuick, but I was able to figure out how to implement a basic QOpenGLWidget
using the MpvRenderContext
. I wrote this minimal example (it expects a test.mp4
) that maps the fullscreen/window to the left right mouse buttons. When going full screen, on both X11 (i3) and Wayland (KDE), the full screen window is black. mpv is still running and you can hear the audio playing, but there's no video rendering.
import sys
from OpenGL.GL import glClearColor
from PySide6.QtGui import QOpenGLContext, Qt
from PySide6.QtWidgets import QPushButton, QVBoxLayout
from PySide6.QtCore import Signal, Slot
from PySide6.QtOpenGLWidgets import QOpenGLWidget
from PySide6.QtWidgets import QApplication, QWidget
from mpv import MPV, MpvRenderContext, MpvGlGetProcAddressFn
def get_process_address(_, name):
print("get_process_address", name.decode('utf-8'))
glctx = QOpenGLContext.currentContext()
if glctx is None:
return 0
return int(glctx.getProcAddress(name))
class MPVWidget(QOpenGLWidget):
onUpdate = Signal()
def __init__(self):
super().__init__()
self.setWindowTitle("OpenGL ES 2.0, PySide6, Python")
self.resize(350, 350)
print("MpvObject.init")
# This is necessary since PyQT stomps over the locale settings needed by libmpv.
# This needs to happen after importing PyQT before creating the first mpv.MPV instance.
import locale # noqa
locale.setlocale(locale.LC_NUMERIC, 'C')
self.mpv = MPV(vo="libmpv",
log_handler=print,
input_default_bindings=False,
input_vo_keyboard=False,
keep_open='yes',
msg_level="all=v")
self._ctx = None
self._get_proc_address_resolver = MpvGlGetProcAddressFn(get_process_address)
self.onUpdate.connect(self.update_frame)
@Slot(str)
def play(self, url):
print("MpvObject.play")
self.mpv.play(url)
def initializeGL(self):
glClearColor(0.0, 0.0, 0.0, 1.0)
self._ctx = MpvRenderContext(
self.mpv,
api_type="opengl",
opengl_init_params={"get_proc_address": self._get_proc_address_resolver},
)
self._ctx.update_cb = self.onUpdate.emit
def resizeGL(self, w, h):
print(f'MpvObject.resizeGL {w}x{h}')
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.RightButton:
self.showFullScreen()
elif event.button() == Qt.MouseButton.LeftButton:
self.showNormal()
def paintGL(self):
if self._ctx:
rect = self.size()
scale = self.devicePixelRatio()
width = int(rect.width() * scale)
height = int(rect.height() * scale)
fbo = int(self.defaultFramebufferObject())
self._ctx.render(
flip_y=True,
opengl_fbo={"w": width, "h": height, "fbo": fbo},
)
@Slot()
def update_frame(self):
self.update()
class MainWidget(QWidget):
def __init__(self):
super().__init__()
self. play_button = QPushButton("Play")
self.fullscreen_button = QPushButton("Fullscreen")
self.play_button.clicked.connect(self.click_play)
self.fullscreen_button.clicked.connect(self.click_fullscreen)
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
self.mpv_widget = MPVWidget()
layout = QVBoxLayout()
layout.addWidget(self.play_button)
layout.addWidget(self.fullscreen_button)
layout.addWidget(self.mpv_widget)
self.setLayout(layout.layout())
@Slot()
def click_play(self):
print("click")
self.mpv_widget.play("test.mp4")
@Slot()
def click_fullscreen(self):
print("fullscreen")
self.mpv_widget.setParent(None)
self.mpv_widget.showFullScreen()
if __name__ == "__main__":
# Doesn't seem to be needed or have an effect
# QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
app = QApplication(sys.argv)
main_widget = MainWidget()
main_widget.show()
sys.exit(app.exec())
I found this very basic OpenGL box demo and used the same technique to fullscreen it. It seems to render fine, transitioning between fullscreen and window.
import sys
import numpy as np
from OpenGL.GL import (GL_COLOR_BUFFER_BIT, GL_FLOAT, GL_TRIANGLE_STRIP,
glClear, glClearColor, glDrawArrays)
from PySide6.QtCore import Qt
from PySide6.QtOpenGL import QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram
from PySide6.QtOpenGLWidgets import QOpenGLWidget
from PySide6.QtWidgets import QApplication
# Original:
# https://github.com/8Observer8/examples-opengles2-webgl-box2d-bulletphysics-openal-web-audio-api/blob/main/shapes/simple-square/qopenglwidget-pyside6-python/main.py
class OpenGLWindow(QOpenGLWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("OpenGL ES 2.0, PySide6, Python")
self.resize(350, 350)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.RightButton:
self.showFullScreen()
elif event.button() == Qt.MouseButton.LeftButton:
self.showNormal()
def initializeGL(self):
glClearColor(48 / 255, 56 / 255, 65 / 255, 1)
vertShaderSrc = """
attribute vec2 aPosition;
void main()
{
gl_Position = vec4(aPosition, 0.0, 1.0);
}
"""
fragShaderSrc = """
#ifdef GL_ES
precision mediump float;
#endif
void main()
{
gl_FragColor = vec4(0.2, 0.7, 0.3, 1.0);
}
"""
self.program = QOpenGLShaderProgram(self)
self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertShaderSrc)
self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragShaderSrc)
self.program.link()
self.program.bind()
vertPositions = np.array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
0.5, 0.5], dtype=np.float32)
self.vertPosBuffer = QOpenGLBuffer()
self.vertPosBuffer.create()
self.vertPosBuffer.bind()
self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4)
def resizeGL(self, w, h):
pass
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT)
self.program.bind()
self.vertPosBuffer.bind()
self.program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 2)
self.program.enableAttributeArray("aPosition")
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
if __name__ == "__main__":
QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
app = QApplication(sys.argv)
w = OpenGLWindow()
w.show()
sys.exit(app.exec())
I'm not very familiar with OpenGL or rendering contexts. Is there something I need to be doing specifically with the MpvRenderContext
so it can continue rendering video correctly when the widget goes to fullscreen?