QMediaPlayer.setSource阻塞GUI,移至线程是否合理?有无更优方案?
QMediaPlayer调用setSource导致GUI卡顿的优化方案探讨
问题背景
运行以下代码时,2000毫秒后调用media_player.setSource('')会导致GUI失去响应约1秒:
from PySide6 import QtWidgets as qtw from PySide6 import QtMultimedia as qtm from PySide6 import QtMultimediaWidgets as qtmw from PySide6 import QtCore as qtc app = qtw.QApplication() path = "video.mp4" video_widget = qtmw.QVideoWidget() video_widget.show() media_player = qtm.QMediaPlayer() media_player.setVideoOutput(video_widget) media_player.mediaStatusChanged.connect(media_player.play) media_player.setSource(path) qtc.QTimer.singleShot(2000, lambda: media_player.setSource('')) app.exec()
尝试仅将setSource()移至单独线程时,会触发警告:QObject::killTimer: Timers cannot be stopped from another thread(替换定时器代码为qtc.QThreadPool.globalInstance().start(lambda: media_player.setSource(''))可复现)。
当前变通方案
我将整个QMediaPlayer实例及相关对象移至独立线程,通过QVideoSink的videoFrameChanged信号更新视频部件画面,实现后调用setSource()不再卡顿,但实现较为繁琐:
from PySide6 import QtWidgets as qtw from PySide6 import QtMultimedia as qtm from PySide6 import QtMultimediaWidgets as qtmw from PySide6 import QtCore as qtc path = "video.mp4" class VideoPlayer(qtmw.QVideoWidget): _stop_worker_signal = qtc.Signal() _initialize_media_player_signal = qtc.Signal() _set_media_source_signal = qtc.Signal(str) _play_media_signal = qtc.Signal() _stop_media_signal = qtc.Signal() _pause_media_signal = qtc.Signal() def __init__(self) -> None: super().__init__() self._worker = Worker() self._worker_thread = qtc.QThread() self._worker.moveToThread(self._worker_thread) self._stop_worker_signal.connect(self._worker.stop) self._initialize_media_player_signal.connect(self._worker.create_media_player) self._worker.video_sink_frame_changed_signal.connect(lambda frame: self.videoSink().setVideoFrame(frame)) self._set_media_source_signal.connect(self._worker.set_media_source) self._stop_media_signal.connect(self._worker.stop_media) self._worker_thread.start() self._initialize_media_player_signal.emit() self._set_media_source_signal.emit(path) self.show() def keyPressEvent(self, event): if event.key() == qtc.Qt.Key.Key_Q: self._set_media_source_signal.emit('') def closeEvent(self, event: qtc.QEvent): loop = qtc.QEventLoop() self._worker.stopped_signal.connect(loop.exit) self._stop_worker_signal.emit() loop.exec() self._worker_thread.exit() class Worker(qtc.QObject): video_sink_frame_changed_signal = qtc.Signal(qtm.QVideoFrame) stopped_signal = qtc.Signal() def create_media_player(self): self._audio_output = qtm.QAudioOutput(qtm.QMediaDevices.defaultAudioOutput()) self._video_sink = qtm.QVideoSink() self._video_sink.videoFrameChanged.connect(self.video_sink_frame_changed_signal) self._media_player = qtm.QMediaPlayer() self._media_player.setAudioOutput(self._audio_output) self._media_player.setVideoSink(self._video_sink) self._media_player.mediaStatusChanged.connect(self._media_player.play) def set_media_source(self, source: str) -> None: self._media_player.setSource(source) def stop_media(self) -> None: if not self._media_player.playbackState() == qtm.QMediaPlayer.PlaybackState.StoppedState: self._media_player.stop() def stop(self): self._media_player.stop() self._audio_output.deleteLater() self._video_sink.deleteLater() self._media_player.deleteLater() self.stopped_signal.emit() app = qtw.QApplication() vp = VideoPlayer() app.exec()
问题提问
当前方案虽解决了卡顿问题,但实现复杂度较高,是否存在更简洁高效的优化方案?
内容的提问来源于stack exchange,提问作者Neat
相关产品推荐
相关产品推荐

