CHANGELOG.md 2.85 KB

更新日志(CHANGELOG)

本文件记录每次功能迭代内容。

2026-11-05 音频播放稳定性修复(app-feature-2610-ios)

本次聚焦 lib/services/player_service.dart(H5 音频播放服务)的异常自愈重写, 解决线上 iOS 报 WAVEAudioFile::OpenFromDataSource Failed 后播放器永久卡死、 无法播放新音频的问题。对外方法签名、H5 消息结构(audioProgress/audioEnd)、 异常契约均保持不变。

问题根因(已核实 flutter_sound 9.30.0 源码)

  • 插件 iOS 远程播放分支:native 异步下载后立即返回成功,下载回调忽略 NSError, 失败分支从不通知 Dart → await startPlayer() 永久悬挂且无任何异常;
  • 悬挂持有插件内部锁,导致实例所有方法(stop/pause/resume/seek/close)全部死锁;
  • 服务端音频格式标注混乱:URL/Content-Type 标 mp3,实际内容为 WAV、 MP3-in-WAV(wFormatTag=0x55)或 WAV 头长度字段损坏,均会导致 AVAudioPlayer 拒绝播放。

修复内容

  1. 播放器异常自愈:startPlayer 失败(抛异常/超时)→ 销毁重建播放器 → 用新实例重试一次 → 仍失败则复位为未初始化态并抛原始异常, 保证"底层报错后不影响下一次新播放";
  2. 远程音频自主下载:http(s) 音频先用 dart:io HttpClient 下载到本地临时文件再播放, 绕开插件远程分支缺陷(含幽灵播放隐患);下载带浏览器 UA、 Content-Type 校验(拦截防盗链/鉴权错误页)、空文件校验、半成品清理;
  3. 全链路超时兜底:下载/建连 10s、startPlayer/openPlayer 10s、 常规操作(暂停/恢复/进度/倍速/停止/关闭)3s,任何悬挂都能在有限时间内 放弃旧实例走重建,不再永久死锁;超时后旧实例接受泄漏;
  4. 音频格式自动识别:按文件头魔数(mp3/wav/ogg/flac/caf/m4a/aac) 修正临时文件扩展名,避免错误扩展名误导 AVAudioPlayer 选错解析器;
  5. WAV 头修复:RIFF/data 长度字段与实际字节不符(流式占位 0xFFFFFFFF、 奇数未对齐等)时按实际字节重写规范头,正常文件不做修改;
  6. MP3-in-WAV 剥离:识别 wFormatTag=0x55(MP3 数据套 WAV 容器, AVAudioPlayer WAVE 解析器不支持)时剥离容器、另存为纯 MP3 播放;
  7. 其他加固:playId + 播放代次双重快照防陈旧 whenFinished 回调误杀新播放; playAudio/stopAudio 串行锁防并发重建;临时文件生命周期管理 (换曲成功后删上一首、清理/复位时删当前);关键路径诊断日志 (下载状态/格式识别/WAV 头详情/失败文件头 16 字节)。

影响范围

  • 仅修改 lib/services/player_service.dart,无新增依赖;
  • Android/iOS 通用:远程音频统一走本地文件播放路径。