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

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的多媒体组件操作必须在主线程执行,子线程生成文件后需通过信号槽通知主线程完成添加操作。

修改思路

  1. 将文本拆分后的音频生成任务放到子线程,避免阻塞GUI
  2. 子线程每生成一个WAV文件,通过信号将路径发送给主线程
  3. 主线程收到路径后,将其追加到已初始化的QMediaPlaylist中
  4. 初始化空播放列表并关联到QMediaPlayer,第一个媒体添加后自动启动播放
  5. 设置播放列表模式为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 11:03:19