You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:24:27