pythonqtqthreadpyside6qmediaplayer

Can not change media using setSource() in pyside6


I have some video clips named 0.mp4, 1.mp4, 2.mp4... and I am using QMediaPlayer in PySide6. I want to write a media player which can play videos one by one. At the end of each video clip, I use the 'setSource()' function to transition to the next clip. But the mainwindow stuck every time I execute setSource() in slot function. I guess it is somewhat related to threads, so I tried changing source in a new thread. But it seems to only complete one switch, and have no response at the end of the 1.mp4.

**1. I tried writing setSource() in an ordinary function: **

import sys
from PySide6.QtCore import Slot, QUrl
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self._audio_output = QAudioOutput()
        self._player = QMediaPlayer()
        self._player.setAudioOutput(self._audio_output)

        self._video_widget = QVideoWidget()
        self.setCentralWidget(self._video_widget)
        self._player.setVideoOutput(self._video_widget)
        self.video_now = 0

        self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))
        self.video_now += 1
        self._player.play()

        self._player.mediaStatusChanged.connect(self.change_source)

    @Slot()
    def change_source(self):
        self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))
        self.video_now += 1
        self._player.play()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_win = MainWindow()
    available_geometry = main_win.screen().availableGeometry()
    main_win.resize(available_geometry.width() / 3,
                    available_geometry.height() / 2)
    main_win.show()
    sys.exit(app.exec())

mainwindow will be stuck at the end of the 0.mp4.

2. I tried putting setSource() into a new thread:

import sys
from PySide6.QtCore import Slot, QUrl, QThread
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget

class source_thread(QThread):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def run(self):
        self.func()
        print("source_thread_finished")

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self._audio_output = QAudioOutput()
        self._player = QMediaPlayer()
        self._player.setAudioOutput(self._audio_output)

        self._video_widget = QVideoWidget()
        self.setCentralWidget(self._video_widget)
        self._player.setVideoOutput(self._video_widget)
        self.video_now = 0

        self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))
        self.video_now += 1
        self._player.play()

        self._player.mediaStatusChanged.connect(self.source_thread_slot)
        self.thread_source = source_thread(self.change_source)


    @Slot()
    def source_thread_slot(self, play_status):
        if play_status != QMediaPlayer.MediaStatus.EndOfMedia:
            return
        self.thread_source.start()

    def change_source(self):
        self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))
        self.video_now += 1
        self._player.play()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    main_win = MainWindow()
    available_geometry = main_win.screen().availableGeometry()
    main_win.resize(available_geometry.width() / 3,
                    available_geometry.height() / 2)
    main_win.show()
    sys.exit(app.exec())

the first switch was perfect, but the program didn't response at the end of 1.mp4.

You can download short video clips in pexel for test, and I'm very grateful if you can give me some suggestions.


Solution

  • Check the status, play when media is loaded, set source when end of media is reached.

    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
    
            self._audio_output = QAudioOutput()
            self._player = QMediaPlayer()
            self._player.setAudioOutput(self._audio_output)
    
            self._video_widget = QVideoWidget()
            self.setCentralWidget(self._video_widget)
            self._player.setVideoOutput(self._video_widget)
            self.video_now = 0
    
            self._player.mediaStatusChanged.connect(self.change_source)
    
            self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))
            
        def change_source(self, status):
            if status == QMediaPlayer.LoadedMedia:
                self._player.play()
            elif status == QMediaPlayer.EndOfMedia:
                self.video_now += 1
                self._player.setSource(QUrl.fromLocalFile("./{}.mp4".format(self.video_now)))