Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicsScene and QGraphicsVideoItem extreme high CPU use on Mac



  • Hi all,
    Trying to do a pixmap overlay on video.
    It works fine on Windows, but is extremely CPU hungry on Mac.
    Then, depending on the video codec, I get black flashes.
    Here is my stripped down code:

    from PyQt5 import QtWidgets, QtGui, QtCore, QtMultimediaWidgets, QtMultimedia
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtMultimedia import *
    from PyQt5.QtMultimediaWidgets import *
    import sys
    
    VIDEO_WIDTH = 1280
    VIDEO_HEIGHT = 720
    
    ########################################
    #
    ########################################
    class MyVideoView(QGraphicsView):
        def __init__(self, parent=None):
            super(QGraphicsView, self).__init__(parent)
            # self._main = parent
    
            self.setBackgroundBrush(Qt.black)
    
            self._scene = QGraphicsScene(self)
            self.setScene(self._scene)
    
            self._videoItem = QGraphicsVideoItem()
            self._videoItem.setPos(0, 0)
            self._videoItem.setSize(QSizeF(VIDEO_WIDTH, VIDEO_HEIGHT))
            self._scene.addItem(self._videoItem)
    
            self._overlay = QPixmap(VIDEO_WIDTH, VIDEO_HEIGHT)
            self._overlay.fill(QColor(0, 150, 0, 150))
            self._graphicsPixmapItem = self._scene.addPixmap(self._overlay)
    
            self._player = QMediaPlayer(self)
            self._player.setVideoOutput(self._videoItem)
    
        def loadVideo(self, fn):
            self._player.setMedia(QMediaContent(QUrl.fromLocalFile(fn)))
            self._player.play()
    
    class Video(QMainWindow):
    
        def __init__(self, parent=None):
            super(QMainWindow, self).__init__(parent)
            self.setWindowTitle("Video window")
            # self.setContentsMargins(-2, -2, -2, -2)  # get rid of black lines around the vid
            self._splitter = QSplitter(Qt.Vertical)
            self.setCentralWidget(self._splitter)
            self._videoView = MyVideoView(self)
            self._splitter.addWidget(self._videoView)
            self._videoView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self._videoView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.show()
            self._videoView.loadVideo('/Volumes/Data/videofiles/demo_BITC 2.mov')
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        app.setQuitOnLastWindowClosed(True)
        w = Video()
        sys.exit(app.exec_())
    
    

    What am I missing here?
    Thx,
    Bouke


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    What version of PyQt5 are you using ?
    How did you install it ?
    What version of macOS are you running ?
    Can you tell what model is you machine ?



  • Hi:
    PyQt5 5.9.2, installed with Pip in a Python 3.6.8 venv
    But just updated to 5.14.2, does not seem to make a difference.

    Running OS 10.15.4 on an Imac (Retina, quad core i5, Radeon Pro 555 2 GB)

    Does that help?


  • Lifetime Qt Champion

    I forgot, what video codec has issues ?



  • You did not forget, I did not told you :-)
    H264 (x264) does play, although suffering from the high CPU consumption.
    ProRes flickers (black frames alternating video frames).

    The CPU use increases when the window gets bigger.

    I suspect there are too many updates or something like that, Qmediaplayer in a 'bare' surrounding does not have these issues.


  • Lifetime Qt Champion

    Try using a QOpenGLWidget as viewport of your QGraphicsView.



  • @SGaist
    Well, I would if I could, but I'm new to Python (and not a real coder, I'm coming from Adobe Director...)
    Do you have any pointers?
    thx,


  • Lifetime Qt Champion

    Something like:

    my_scene.setViewport(new QGLWidget())
    


  • @SGaist
    Ok, I've added to my QGraphicsView:

    self.widget = glWidget(self)
    self.setViewport(self.widget)

    class glWidget(QGLWidget):
    def init(self, parent=None):
    QGLWidget.init(self, parent)



  • that was send out too fast... (Can't I edit my own posts?)
    CPU use is now reduced to a quarter of what it was before, so that's the good news.
    The bad news is that the video is displayed twice too big. (Due to my Retina screen I think...)



  • Dirty hack that sorta kinda fixes stuff:

    from PyQt5 import QtWidgets, QtGui, QtCore, QtMultimediaWidgets, QtMultimedia
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    from PyQt5.QtMultimedia import *
    from PyQt5.QtMultimediaWidgets import *
    import sys
    
    
    from PyQt5.QtOpenGL import *
    
    VIDEO_WIDTH = 1200
    VIDEO_HEIGHT = int(VIDEO_WIDTH / 16 * 9)å
    
    ########################################
    #
    ########################################
    class MyVideoView(QGraphicsView):
        def __init__(self, parent=None):
            super(QGraphicsView, self).__init__(parent)
            # self._main = parent
    
            # 9% CPU use, rem out this to get high CPU use (depends on window size, up to 100%)
            self.widget = glWidget()
            self.setViewport(self.widget)
    
    
            self.setBackgroundBrush(Qt.black)
            self._scene = QGraphicsScene(self)
            self.setScene(self._scene)
            self._videoItem = QGraphicsVideoItem()
            # self._videoItem.setPos(0, 0)
            self._scene.addItem(self._videoItem)
    
            self._overlay = QPixmap(VIDEO_WIDTH, VIDEO_HEIGHT)
            self._overlay.fill(QColor(0, 150, 0, 150))
            self._graphicsPixmapItem = self._scene.addPixmap(self._overlay)
    
            self._player = QMediaPlayer(self)
            self._player.setVideoOutput(self._videoItem)
    
        def loadVideo(self, fn):
            self._player.setMedia(QMediaContent(QUrl.fromLocalFile(fn)))
            self._player.play()
            # self._player.setPlaybackRate(0.5)
    
    class glWidget(QGLWidget):
        def __init__(self, parent=None):
            QGLWidget.__init__(self, parent)
    
    class Video(QMainWindow):
    
        def __init__(self, parent=None):
            super(QMainWindow, self).__init__(parent)
            self.setWindowTitle("Video window")
            self._splitter = QSplitter(Qt.Vertical)
            self.setCentralWidget(self._splitter)
    
            self._videoView = MyVideoView(self)
    
            self._splitter.addWidget(self._videoView)
            self._videoView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self._videoView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.show()
            self._videoView.loadVideo('/Volumes/Data/videofiles/demo_BITC 2.mov')
            self.resizeEvent(False)
    
        def resizeEvent(self, e):
            # print(dir(QResizeEvent))
            if not e:
                _w = self.geometry().width()
                _h = self.geometry().height()
            else:
                _w = e.size().width()
                _h = e.size().height()
    
    
            self._videoView._videoItem.setSize(QSizeF(_w / 2, _h / 2))
    
            # do not get a feedback loop
            try:
                if self.ignorscaling:
                    self.ignorscaling = False
                    return
            except:
                pass
            self.ignorscaling = True
    
            # order of stuff is important here!
            self._videoView._videoItem.setSize(QSizeF(_w, _h))
            _w = self._videoView._videoItem.sceneBoundingRect().right()
            _h = self._videoView._videoItem.sceneBoundingRect().bottom() - self._videoView._videoItem.sceneBoundingRect().top()
    
            self.resize(_w, _h)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        app.setQuitOnLastWindowClosed(True)
        w = Video()
        sys.exit(app.exec_())
    

Log in to reply