just_audio 播放某个文件失败

今天遇到一个十分灵异的问题,使用just audiomacOS上面播放 school.mp3 这个文件的时候死活都会抛异常。

await _player.setAsset('assets/audio/school.mp3');
await _player.setSpeed(speed);
await _player.play();

每次都是:flutter: Error playing audio: (-11849) Operation Stopped

首先想到的就是这个文件发生的错误,于是就重新生成了一个,结果依然是同样的错误。在调试过程中就发现了一些诡异的事情:

  • 只要把这个 school.mp3 文件改成另外一个文件名,就可以正常播放。
  • 其他可以正常播放的文件改成这个 school.mp3 文件名就无法播放。

Antigravity 中,把这些情况告诉 AI,Claude Opus 4.5 (Thinking)Gemini 3 Pro (High) 都无法解决。Opus 运行了一堆 ffmepg 的命令检查这个文件, 最后干脆一直显示 thinking,不回来了。

于是在网页Gemini中继续提问。 把上面的错误信息丢给它,告诉它只有 school.mp3 无法播放。对于错误代码,它马上给出了解释:

错误代码 (-11849) 在 macOS 和 iOS 开发中属于 AVFoundation 错误,对应的描述通常是 AVErrorContentUnsupported 或类似的路径/格式加载失败。

这个 Antigravity 也是这样说的,这部分应该属于标准错误。

网页 Gemini 第一次给出的尝试是怀疑AssetManifest.json

当你添加、删除或重命名资源时,Flutter 虽然会更新自己的 AssetManifest.json,但 macOS 的宿主工程(Runner.app)有时会产生僵尸引用。它认为 school.mp3 还在旧的位置,或者试图从一个已经失效的缓存索引中读取它。改名成 school_1.mp3 会迫使系统创建一个全新的文件索引,从而绕过了错误的旧缓存。

它给出的解决方案是看能否读取到数据:

ByteData data = await rootBundle.load('assets/audio/school.mp3');
print("文件读取成功,大小: ${data.lengthInBytes}");

很不幸,读取成功。然后它开始怀疑 扩展属性,这个也被证明没有关系。

然后我告诉它:把其他文件改为 school.mp3,也会无法播放。它立刻给出了三个原因。
Gemini:

  1. 宿主 App 的沙盒缓存死锁 (最可能)
  2. 隐藏的 Entitlements 权限冲突
  3. Just Audio 的内部代理缓存

我首先尝试了第三个,用它给出的代码

await player.setAsset('assets/audio/school.mp3', preload: false); // 禁用预加载

结果卡死在后续的 play 上。

Gemini:

卡在 await _player.play() 且无法返回,进一步证实了这是一个底层死锁(Deadlock)。请按以下三个步骤进行最后的排查:

  1. 终极手段:重置 macOS App 容器(沙盒)
  2. ……
  3. ……

我没看下面两个,直接尝试终极手段:

rm -rf ~/Library/Containers/your.bundle.id

结果无法删除,上 sudo 也不行:Operation not permitted。

Gemini:

在 macOS 上,如果你对自己的 Containers 目录执行 rm -rf 却提示 Operation not permitted,通常意味着:

  1. 系统级锁死: 某个系统进程(如 bird iCloud 同步、mds 聚焦索引,或者最可能的——**AVFoundation 的音频服务进程**)正牢牢勾住这个文件夹里的某个资源。
  2. SIP 或 TCC 保护: 你的终端(Terminal/iTerm2)没有“完全磁盘访问权限”。

Gemini:

第一步:给终端“最高权力”
第二步:尝试在 Finder 中删除,直接按 Command + Delete 移到废纸篓。
第三步:重启 coreaudiod (音频服务重置): sudo killall coreaudiod
第四步:修改 Bundle ID (终极绕路方案)

我试了第二步+第三步,问题解决!Gemini 的总结是:

果然还是“物理超度”(直接删掉整个沙盒容器)最管用。
总结一下这次“灵异事件”的本质:

  1. 路径诅咒: 你的 macOS 系统音频服务(coreaudiod)在之前的某次运行中,因为某种原因对 school.mp3 这个路径产生了死锁。
  2. 缓存持久化: 即使你重新运行 Flutter、执行 flutter clean,由于 macOS 容器(Containers)是持久存在的,那个损坏的路径状态一直被保留在系统底层的 AVFoundation 缓存里。
  3. 权限锁死: 甚至连 rm -rf 都被拒绝,说明当时系统进程确实正死死地“咬着”那个文件夹。
    通过 Finder 强制删除 加上 重启音频服务,你相当于给 macOS 的音频引擎做了一次“全脑格式化”,它终于忘记了那个让它卡死的 school.mp3

沙盒缓存死锁我还是第一次遇到——第一次听到。我看了一眼,用的是 Gemini 3,Fast 选项。

现在的问题是,我 Android 手机上也是这个 school.mp3 无法播放——为什么会是同一个文件?