PyQt5播放时能否扩展QMediaPlaylist并添加新媒体?
QMediaPlaylist能否在播放中动态追加媒体?
背景
基于离线实时语音合成器(如espeak)搭建TTS GUI,由于合成器速度慢,希望将文本拆分后逐个生成WAV文件,在第一个文件生成完成后立即开始按顺序播放,无需等待所有文件生成完毕。
核心问题
QMediaPlaylist是否支持在播放过程中扩展——也就是不停止播放的前提下,向列表末尾添加新的媒体文件?期望实现的流程是:用单独线程生成WAV文件,每生成一个就追加到播放列表;播放列表非空时自动启动播放,所有文件播放完成后停止。
现有代码片段
from PyQt5.QtMultimedia import QMediaPlayer, QMediaPlaylist import requests # 假设URL已定义 URL = "http://localhost:xxxx/your-api" def read_text(text: str, player: QMediaPlayer) -> None: filepaths = [] for parted_text in text.split('. '): filename = ask(parted_text) filepaths.append(filename) play_files(filepaths, player) return def play_files(filepaths, player): playlist = QMediaPlaylist(player) for filepath in filepaths: url = QUrl.fromLocalFile(filepath) playlist.addMedia(QMediaContent(url)) player.setPlaylist(playlist) player.play() def ask(text: str): """连接本地服务器提取WAV文件""" response = requests.get(URL, params={"text": text}) if response.status_code != 200: raise Exception(f"错误:意外响应 {response}") return response.json()["filename"]
实现方案
可行性确认
QMediaPlaylist完全支持在播放过程中动态追加媒体,无需停止播放。注意:Qt的多媒体组件操作必须在主线程执行,子线程生成文件后需通过信号槽通知主线程完成添加操作。
修改思路
- 将文本拆分后的音频生成任务放到子线程,避免阻塞GUI
- 子线程每生成一个WAV文件,通过信号将路径发送给主线程
- 主线程收到路径后,将其追加到已初始化的QMediaPlaylist中
- 初始化空播放列表并关联到QMediaPlayer,第一个媒体添加后自动启动播放
- 设置播放列表模式为
CurrentItemOnce,确保播放完所有文件后自动停止
修改后的代码示例
from PyQt5.QtCore import QThread, pyqtSignal, QUrl from PyQt5.QtMultimedia import QMediaPlayer, QMediaPlaylist, QMediaContent import requests URL = "http://localhost:xxxx/your-api" class TTSTaskThread(QThread): file_generated = pyqtSignal(str) task_finished = pyqtSignal() def __init__(self, text): super().__init__() self.text = text def run(self): for parted_text in self.text.split('. '): try: filename = self.ask(parted_text) self.file_generated.emit(filename) except Exception as e: print(f"生成音频失败:{e}") self.task_finished.emit() def ask(self, text: str): response = requests.get(URL, params={"text": text}) if response.status_code != 200: raise Exception(f"错误:意外响应 {response}") return response.json()["filename"] def setup_tts_playback(text: str, player: QMediaPlayer): # 初始化播放列表,设置播放模式为逐个播放 playlist = QMediaPlaylist() playlist.setPlaybackMode(QMediaPlaylist.CurrentItemOnce) player.setPlaylist(playlist) # 创建并启动TTS生成线程 tts_thread = TTSTaskThread(text) def on_file_generated(filepath): url = QUrl.fromLocalFile(filepath) playlist.addMedia(QMediaContent(url)) # 若播放器未处于播放状态,启动播放 if player.state() != QMediaPlayer.PlayingState: player.play() def on_task_finished(): # 所有文件生成完成,等待最后一个音频播放完毕后自动停止 pass # 绑定信号槽 tts_thread.file_generated.connect(on_file_generated) tts_thread.task_finished.connect(on_task_finished) tts_thread.start()
关键说明
- 必须在主线程调用
QMediaPlaylist.addMedia(),子线程不能直接操作Qt多媒体组件,通过信号槽实现线程安全通信 CurrentItemOnce模式会让播放列表在当前项目播放完成后自动切换到下一个,直到所有项目播放完毕- 第一个文件生成后自动触发播放,无需等待全部文件生成
内容的提问来源于stack exchange,提问作者Dawid Laszuk
相关产品推荐
相关产品推荐

