Python无本地存储播放m3u8音频流及PyQt QMediaPlayer对接咨询
PyQt 无本地缓存播放m3u8广播流实现方案
核心逻辑是通过子进程调用ffmpeg拉流转码后直接输出到标准输出,Python读取流后通过内存缓冲区传给QMediaPlayer播放,全程不会写入本地文件。
前置依赖
- 系统已安装ffmpeg且配置到环境变量
- 已安装PyQt多媒体相关组件,PyQt6安装命令:
pip install PyQt6 PyQt6-Multimedia
如果使用PyQt5,替换包名为PyQt5和PyQt5-Multimedia即可。
核心ffmpeg命令调整
需要将原本输出到本地文件的ffmpeg命令调整为输出到标准输出,同时屏蔽多余日志避免污染音频数据流,调整后命令如下:
ffmpeg -i "http://example.com/live/playlist.m3u8" -c:a aac -f adts -hide_banner -loglevel error -
参数说明:
-f adts指定输出为ADTS封装的AAC流,无需本地文件即可被播放器直接识别-hide_banner -loglevel error仅输出错误级别的日志,避免ffmpeg的启动信息、进度信息写入stdout破坏音频数据- 末尾的
-代表将流输出到stdout而非本地文件
完整实现代码
import sys import subprocess from PyQt6.QtCore import QBuffer, QIODevice, QThread, pyqtSignal from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout class StreamReader(QThread): # 读取到音频数据的信号 data_received = pyqtSignal(bytes) def __init__(self, m3u8_url): super().__init__() self.m3u8_url = m3u8_url self.ffmpeg_process = None self.running = False def run(self): self.running = True # 启动ffmpeg子进程 cmd = [ "ffmpeg", "-i", self.m3u8_url, "-c:a", "aac", "-f", "adts", "-hide_banner", "-loglevel", "error", "-" ] self.ffmpeg_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=1024*1024 ) # 循环读取ffmpeg输出的流 while self.running: data = self.ffmpeg_process.stdout.read(4096) if not data: break self.data_received.emit(data) def stop(self): self.running = False if self.ffmpeg_process: self.ffmpeg_process.terminate() self.ffmpeg_process.wait() self.wait() class RadioPlayer(QWidget): def __init__(self, m3u8_url): super().__init__() self.m3u8_url = m3u8_url self.init_ui() self.init_player() self.stream_reader = None self.buffer = QBuffer() self.buffer.open(QIODevice.OpenModeFlag.ReadWrite) def init_ui(self): self.setWindowTitle("网络广播播放器") self.resize(300, 150) layout = QVBoxLayout() self.play_btn = QPushButton("播放") self.play_btn.clicked.connect(self.toggle_play) layout.addWidget(self.play_btn) self.setLayout(layout) def init_player(self): self.audio_output = QAudioOutput() self.player = QMediaPlayer() self.player.setAudioOutput(self.audio_output) def toggle_play(self): if self.play_btn.text() == "播放": self.play_btn.setText("停止") # 启动流读取线程 self.stream_reader = StreamReader(self.m3u8_url) self.stream_reader.data_received.connect(self.write_to_buffer) self.stream_reader.start() # 给player设置缓冲区为播放源 self.player.setSourceDevice(self.buffer) self.player.play() else: self.play_btn.setText("播放") self.player.stop() if self.stream_reader: self.stream_reader.stop() self.stream_reader = None self.buffer.reset() def write_to_buffer(self, data): # 将读取到的音频数据写入缓冲区 pos = self.buffer.pos() self.buffer.seek(self.buffer.size()) self.buffer.write(data) self.buffer.seek(pos) if __name__ == "__main__": app = QApplication(sys.argv) # 替换为你自己的m3u8链接 M3U8_URL = "http://example.com/live/playlist.m3u8" window = RadioPlayer(M3U8_URL) window.show() sys.exit(app.exec())
注意事项
- 运行前请将代码中的
M3U8_URL替换为实际的广播m3u8地址 - 退出程序时会自动终止ffmpeg子进程,不会产生残留进程
- 如果需要调整音频质量,可以修改ffmpeg命令中的
-b:a参数指定比特率,比如添加-b:a 128k即可固定输出128kbps的音频流
内容的提问来源于stack exchange,提问作者AlexMercer
相关产品推荐
相关产品推荐

