# `AudioPlayer` 技术文档 基于 `ffpyplayer` 的 Python 音频播放器类,支持加载音频文件、播放控制(播放/暂停/停止)、循环播放、音量调节和事件回调等功能。 --- ## 📦 概述 `AudioPlayer` 是一个轻量级的音频播放器封装类,利用 `ffpyplayer` 库实现对本地音频文件的播放控制。该类提供了简洁的接口用于常见操作,如播放、暂停、跳转、循环等,并支持自定义事件处理。 --- ## 🧩 依赖库 - `ffpyplayer`: 多媒体播放后端(基于 FFmpeg) - `time`: 用于时间相关操作 ```bash pip install ffpyplayer ``` > ⚠️ 注意:`ffpyplayer` 在某些平台上可能需要手动编译或安装预构建版本。 --- ## 🧱 类定义 ### `class AudioPlayer(source=None, autoplay=False, loop=False, on_stop=None)` 初始化一个音频播放器实例。 #### 参数说明: | 参数 | 类型 | 默认值 | 描述 | |------|------|--------|------| | `source` | `str` 或 `None` | `None` | 音频文件路径(可选,在初始化时设置或后续通过 `set_source()` 设置) | | `autoplay` | `bool` | `False` | 是否在加载音频后自动开始播放 | | `loop` | `bool` | `False` | 是否启用循环播放模式 | | `on_stop` | `callable` 或 `None` | `None` | 当音频停止时调用的回调函数 | --- ## 🔧 属性列表 | 属性名 | 类型 | 描述 | |--------|------|------| | `volume` | `float` | 当前音量(范围 0.0 ~ 1.0),默认为 `1.0` | | `state` | `str` | 当前状态:`'play'`, `'pause'`, `'stop'` | | `source` | `str` | 当前音频源路径 | | `quitted` | `bool` | 标记是否已请求退出(内部使用) | | `loop` | `bool` | 是否开启循环播放 | | `autoplay` | `bool` | 是否自动播放 | | `player` | `MediaPlayer` 实例或 `None` | `ffpyplayer.player.MediaPlayer` 对象 | | `on_stop` | `callable` 或 `None` | 停止播放时触发的用户回调函数 | | `cmds` | `list` | (预留)命令队列(当前未使用) | --- ## 📚 方法说明 ### `set_source(source: str)` 设置音频源并立即加载。 **参数:** - `source` (str): 音频文件路径 **行为:** - 更新 `self.source` - 调用 `load()` 加载新资源 --- ### `load() -> None` 加载当前 `source` 指定的音频文件。 **逻辑流程:** 1. 若无 `source`,直接返回。 2. 卸载已有播放器(调用 `unload()`)。 3. 创建新的 `MediaPlayer` 实例,配置选项: - `vn=True`: 禁用视频流 - `sn=True`: 启用字幕流(不影响音频) 4. 使用 `callback` 和 `loglevel='info'` 5. 等待最多 10 秒获取音频元数据(尤其是 duration) 6. 初始暂停播放器以准备控制 7. 设置音量 8. 若 `autoplay=True`,则调用 `play()` > ⚠️ **注意**:此方法包含阻塞等待(最大 10s),用于确保能读取到音频时长信息。 --- ### `unload() -> None` 释放当前播放器资源,重置状态。 **效果:** - 清空 `player` - 设置状态为 `'stop'` - 重置 `quitted` 标志 --- ### `__del__()` 析构函数,确保对象销毁时调用 `unload()`。 --- ### `play() -> None` 开始播放音频。 **行为:** - 若尚未加载,则先调用 `load()` - 若已在播放状态,直接返回 - 否则调用 `toggle_pause()` 并更新状态为 `'play'` --- ### `pause() -> None` 暂停播放。 **行为:** - 若未加载,尝试加载 - 若已在暂停状态,不执行操作 - 否则调用 `toggle_pause()` 并更新状态为 `'pause'` --- ### `stop() -> None` 停止播放,重置播放位置至开头。 **行为:** - 如果正在播放,先暂停 - 将播放进度跳转至 0(`seek(0)`) - 更新状态为 `'stop'` - 触发 `on_stop` 回调(如果存在) --- ### `seek(pos: float) -> None` 跳转到指定时间点(单位:秒)。 **参数:** - `pos` (float): 目标时间位置(绝对时间) **示例:** ```python p.seek(30.0) # 跳转到第30秒 ``` --- ### `get_pos() -> float` 获取当前播放时间(单位:秒)。 **返回:** - 当前播放时间戳(PTS),若无播放器则返回 `0` --- ### `is_busy() -> bool` 判断是否正在播放中。 **返回:** - `True` 表示处于 `'play'` 状态且有有效播放器 - `False` 表示暂停或停止 --- ### `player_callback(selector: str, value: any)` 内部回调函数,由 `ffpyplayer` 触发。 **触发事件:** - `'quit'`: 播放器退出 → 调用 `close()` 清理资源 - `'eof'`: 播放结束 → 调用 `_do_eos()` > ✅ 日志输出可用于调试。 --- ### `_do_eos(*args) -> None` 处理播放结束(End of Stream)事件。 **行为:** - 若启用 `loop`,则跳回开头继续播放 - 否则调用 `stop()` 结束播放 --- ## 🎯 使用示例 ### 基本使用 ```python from audio_player import AudioPlayer def on_audio_stopped(): print("音频已停止") # 初始化播放器 player = AudioPlayer( source="music.mp3", autoplay=True, loop=True, on_stop=on_audio_stopped ) # 控制播放 player.play() time.sleep(5) player.pause() player.seek(10.0) # 跳转到第10秒 player.play() # 查询位置 print(f"当前时间: {player.get_pos()}s") # 停止 player.stop() ``` ### 手动交互式控制(主程序示例) 运行脚本时传入音频路径: ```bash python audio_player.py /path/to/audio.mp3 ``` 交互命令: - `play` — 开始播放 - `pause` — 暂停 - `stop` — 停止并归零 - `quit` — 退出程序 --- ## ⚙️ 内部机制说明 ### 播放器初始化选项 (`ff_opts`) ```python ff_opts = {'vn': True, 'sn': True} ``` - `vn=True`: 忽略视频流(仅音频) - `sn=True`: 启用字幕流(不影响播放) ### 元数据加载等待机制 使用 `time.perf_counter()` 最多等待 10 秒以获取 `duration`,避免因媒体分析过慢导致后续操作失败。 ### 状态管理 通过 `self.state` 维护播放状态机,防止重复操作。 --- ## ❗ 已知限制与注意事项 1. **线程安全**:未考虑多线程并发访问,建议在单线程环境中使用。 2. **异常处理**:缺少对无效文件路径、格式不支持等情况的捕获,需外部保障输入合法性。 3. **资源释放**:虽然有 `__del__`,但推荐显式调用 `unload()` 或及时删除引用。 4. **性能**:`load()` 中的循环等待可能导致短暂阻塞 UI(适用于非 GUI 场景)。 5. **日志级别**:固定为 `'info'`,可通过扩展参数暴露配置。 --- ## 🛠️ 扩展建议 - 添加音量控制接口(如 `set_volume()`) - 支持获取总时长 `get_duration()` - 引入事件系统替代硬编码回调 - 支持网络流媒体 URL - 增加错误处理与异常通知机制 --- ## 📄 许可与版权 本代码基于 MIT 或类似自由许可发布(具体取决于 `ffpyplayer` 的使用条款)。请遵守其开源协议。 --- > 文档版本:v1.0 > 最后更新:2025年4月5日