Search code examples
pythonpyqtvideo-player

How to create a video player with 2 windows with the same video?


I need to do a video player in PyQt. What is special at this video player is that I need 2 windows with the same video one in my interface and another in another monitor and both should be controlled from interface. I reused to do one separate widow with the video(that where the bird is), but I can't handle to do another one in that white widget. This is the code:

    self.player = QMediaPlayer()
    self.player.play()

    # Setup the playlist.
    self.playlist = QMediaPlaylist()
    self.player.setPlaylist(self.playlist)
    # # Add viewer for video playback, separate floating window.
    self.viewer = ViewerWindow(self)
    self.viewer.setWindowFlags(self.viewer.windowFlags() | Qt.WindowStaysOnTopHint)
    self.viewer.setMinimumSize(QSize(480, 360))
    self.viewer.setWindowTitle("VR Therapy")

    self.videoWidget = QVideoWidget(self)
    self.viewer.setCentralWidget(self.videoWidget)
    self.player.setVideoOutput(self.videoWidget)

    # Connect control buttons/slides for media player.
    self.playButton_2.pressed.connect(self.player.play)
    self.pauseButton_2.pressed.connect(self.player.pause)
    self.stopButton_2.clicked.connect(self.player.stop)
    self.volumeSlider_2.valueChanged.connect(self.player.setVolume)
    self.viewButton_2.toggled.connect(self.toggle_viewer)
    self.viewer.state.connect(self.viewButton_2.setChecked)


    self.previousButton_2.pressed.connect(self.playlist.previous)
    self.nextButton_2.pressed.connect(self.playlist.next)

    self.model = PlaylistModel(self.playlist)
    self.playlistView_2.setModel(self.model)
    self.playlist.currentIndexChanged.connect(self.playlist_position_changed)
    selection_model = self.playlistView_2.selectionModel()
    selection_model.selectionChanged.connect(self.playlist_selection_changed)


    self.player.durationChanged.connect(self.update_duration)
    self.player.positionChanged.connect(self.update_position)
    self.timeSlider_2.valueChanged.connect(self.player.setPosition)

videoplayer


Solution

  • The QMediaPlayer is the controller of the media source, that is, it controls when it is played, paused, etc. and the QVideoWidget is one of the many viewers of the frames that the QMediaPlayer provides that is internally displayed using the QAbstractVideoSurface. So there should be no problems if a QMediaPlayer provides the frames to N outputs, for this you must use the setVideoOutput() method:

    void QMediaPlayer::setVideoOutput(const QVector<QAbstractVideoSurface *> &surfaces)
    Sets multiple video surfaces as the video output of a media player. This allows the media player to render video frames on different surfaces.

    All video surfaces must support at least one shared QVideoFrame::PixelFormat.

    If a video output has already been set on the media player the new surfaces will replace it.

    This function was introduced in Qt 5.15.

    from functools import cached_property
    import sys
    
    from PyQt5 import QtCore, QtWidgets, QtMultimedia, QtMultimediaWidgets
    
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.setCentralWidget(self.video_widget)
    
            self.viewer.show()
    
            self.player.setVideoOutput(
                [self.video_widget.videoSurface(), self.viewer.videoSurface()]
            )
            self.player.setPlaylist(self.playlist)
    
            self.playlist.addMedia(
                QtMultimedia.QMediaContent(
                    QtCore.QUrl("http://techslides.com/demos/sample-videos/small.mp4")
                )
            )
            self.player.play()
    
        @cached_property
        def player(self):
            return QtMultimedia.QMediaPlayer()
    
        @cached_property
        def video_widget(self):
            return QtMultimediaWidgets.QVideoWidget()
    
        @cached_property
        def viewer(self):
            view = QtMultimediaWidgets.QVideoWidget()
            view.setWindowFlags(view.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
            view.setMinimumSize(480, 360)
            view.setWindowTitle("VR Therapy")
            return view
    
        @cached_property
        def playlist(self):
            return QtMultimedia.QMediaPlaylist()
    
    
    def main():
        app = QtWidgets.QApplication(sys.argv)
    
        w = MainWindow()
        w.resize(640, 480)
        w.show()
    
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()