Search code examples
pythonpyqtpyqt5qgraphicssceneqmediaplayer

PyQt video frame update signal (Trigger function after each video frame)


I am creating a video player and I need to draw some polygons on top of it. I am using a QGraphicsScene to create this and I need to update the polygons on screen after each frame. I am currently using the QMediaPlayer paired up with a QGraphicsVideoItem to create this. The problem I am having is that the QMediaPlayer doesn't have a signal that activates on each frame. It has positionChanged(), but this only seems to trigger once every second.

I tried using QMovie since it does send updates on every frame, but it did not display anything. This is the code I used to implement this.

    video_view = QGraphicsView()#view to hold video
    video_item = QGraphicsVideoItem()#video item for scene
    video_scene = QGraphicsScene()#scene for Qgraphics view
    video_view.setScene(video_scene)

    label = QLabel()
    movie = QMovie(self.video_paths[index]) #contains file path
    label.setMovie(movie)
    video_scene.addWidget(label)
    self.vlayout_main_video.addWidget(video_view)

The video file I am using is a .avi file and it is 72Mb large.

I would really appreciate it if somebody could point me in the right direction on how I could do this. I am currently using PyQt5.

Thank you


Solution

  • There are 2 options:

    • positionChanged is emited every second because the notifyInterval property of QMediaPlayer is set in that period. So you can change that property, for example to 60 ms.

    from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            scene = QtWidgets.QGraphicsScene(self)
            self.video_view = QtWidgets.QGraphicsView(scene)
            self.setCentralWidget(self.video_view)
    
            self.player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
            self.video_item = QtMultimediaWidgets.QGraphicsVideoItem()
            self.player.setVideoOutput(self.video_item)
            scene.addItem(self.video_item)
            file = "/path/of/video"
            self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
            self.player.positionChanged.connect(self.on_positionChanged)
            self.player.setNotifyInterval(60)
            self.player.play()
    
        @QtCore.pyqtSlot('qint64')
        def on_positionChanged(self, p):
            print(p, QtCore.QTime.currentTime().toString("hh:mm:ss.zzz"))
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.resize(640, 480)
        w.show()
        sys.exit(app.exec_())
    
    • Use the VideoFrameProbed signal from QVideoProbe:

    from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            scene = QtWidgets.QGraphicsScene(self)
            self.video_view = QtWidgets.QGraphicsView(scene)
            self.setCentralWidget(self.video_view)
    
            self.player = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
            self.video_item = QtMultimediaWidgets.QGraphicsVideoItem()
            self.player.setVideoOutput(self.video_item)
            scene.addItem(self.video_item)
            file = "/path/of/video"
            self.player.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file)))
            self.player.play()
    
            probe = QtMultimedia.QVideoProbe(self)
            probe.videoFrameProbed.connect(self.on_videoFrameProbed)
            probe.setSource(self.player)
    
        @QtCore.pyqtSlot()
        def on_videoFrameProbed(self):
            print(QtCore.QTime.currentTime().toString("hh:mm:ss.zzz"))
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.resize(640, 480)
        w.show()
        sys.exit(app.exec_())