Repository navigation
Description
[Bug] 云盘歌曲带 ID3 前缀的 MP4/M4A 无法播放:FFmpeg 报 "moov atom not found"
问题描述
播放网易云音乐云盘中的部分歌曲时无法播放,报错:
[error] (player) NETWORK_ERROR: Error: [DecodeFailed] decode failed:
打开网络音频失败: http://m703.music.126.net/.../jd-musicrep-privatecloud-audio-public/obj/.../xxx.mp3?vuutv=...
FFmpeg error -1094995529: Invalid data found when processing input
-1094995529 = AVERROR_INVALIDDATA
复现步骤
- 向网易云音乐云盘上传一个 M4A / AAC(MP4 容器) 格式的音频文件
- 在 SPlayer-Next 中登录网易云账号,进入音乐云盘
- 播放该歌曲 → 失败
特征:与该歌曲的音频内容、时长、码率无关;换成同账号云盘里上传格式为 MP3/FLAC 的歌曲则一切正常。
根本原因
⚠️ 2026-10-01 更正:本节原先对机制的描述("id3v2demuxer 不会把数据接力给movdemuxer")不准确。经 @apoint123 指正并独立验证,真正原因是 FFmpeg 上游 2025-07 引入的回归(FFmpeg@9d037c54):该提交引入AVFMT_FLAG_ID3V2_AUTO,仅对mp3/aac/tta/wav剥离 ID3v2,mov/mp4不在白名单内,于是 Rewind 回偏移 0 后 ID3 不再被跳过,mov_read_default把'ID3\x03'当作 box 长度(约 1.14 GiB)解析,Seek 越界后扫不到moov。本节以下的事实描述仍然成立(文件确实是 ID3+MP4、剥掉即可播放、报错文本一致),仅"为什么"的归因以本条更正为准。完整分析与独立验证见下方评论。
网易云在云盘文件最前面附加了一个 ID3v2.3 标签(含内嵌 JPEG 封面,约 35 KB)。文件实际结构变成:
[ID3v2.3 标签 (35 KB)] + [MP4/M4A 容器 (ftyp / moov / sidx / moof / mdat, HE-AAC 音频)]
mov demuxer 从偏移 0 开始按 MP4 box 解析,读到的前 8 字节 49 44 33 03 00 00 00 00 被当作 box 长度 0x49443303(约 1.14 GiB),尝试跳过该 payload 导致 Seek 越界至文件末尾,最终报:
[mov,mp4,m4a,3gp,3g2,mj2] moov atom not found
Invalid data found when processing input
验证证据
1. 完整下载该 URL(806606 字节,HTTP 200 / Content-Type: audio/mpeg)
$ file song.mp3
song.mp3: Audio file with ID3 version 2.3.0, contains:
- ISO Media, MP4 Base Media v5
$ ffprobe song.mp3
[mov,mp4,m4a,3gp,3g2,mj2] moov atom not found
song.mp3: Invalid data found when processing input
2. 切掉前 35326 字节(ID3 标签)后,同一文件完美解码
$ dd if=song.mp3 of=song.m4a bs=1 skip=35326
$ ffprobe song.m4a
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'song.m4a':
Duration: 00:02:18.20, start: 0.000000, bitrate: 44 kb/s
Stream #0:0[0x2](und): Audio: aac (HE-AAC) (mp4a / 0x6134706D),
48000 Hz, stereo, fltp, 50 kb/s (default)
3. 剥离 ID3 后的 MP4 atom 结构(文件是完好的分片 MP4)
偏移 35326 大小 36 ftyp
偏移 35362 大小 8 free
偏移 35370 大小 74 free
偏移 35444 大小 826 moov ← 索引头存在
偏移 36270 大小 368 sidx
偏移 36638 大小 1048 moof
偏移 37686 大小 31477 mdat
偏移 69163 大小 580 moof
→ 文件本身完全正常,纯粹是 ID3 前缀导致 FFmpeg 无法识别。
4. 原生音频引擎日志佐证
app-data/logs/native/audio-engine.*.log 中,同一首歌每次加载后都没有后续的"打开音频输出配置":
22:28:24 INFO audio_engine::bindings::player: 加载音频源 source=http://.../obj/woDDmMOBw6PClWzCnMK-/... auto_play=true
(无后续 → 加载失败)
22:28:25 INFO audio_engine::bindings::player: 加载音频源 source=http://.../obj/woHCgMKXw6XCmDjDj8Oj/... auto_play=true
22:28:26 INFO audio_engine::audio_output: 打开音频输出配置 id="pipewire:output_default" ... ← 正常的歌能走到这一步
(ID3 解析失败后,插件兜底 chksz.splayer-source 也因未配置 API Key 而失败,所以没有任何备用音源可用。)
影响范围
| 上传到云盘的原始格式 | 网易附加 ID3 后 | SPlayer-Next | 官方客户端 |
|---|---|---|---|
| MP3 | ID3 + MP3 | ✅ 正常(ID3 是 MP3 原生的一部分) | ✅ |
| FLAC | ID3 + FLAC | ✅ 正常 | ✅ |
| M4A / AAC(MP4 容器) | ID3 + MP4 | ❌ 无法播放 | ✅ |
因此用户会观察到"有的云盘歌能听、有的听不了",且与音频内容无关,只取决于上传时的原始文件格式。
建议修复方向
在把 URL 交给 FFmpeg 之前,检测并跳过 ID3v2 标签。可选方案:
- 自定义 AVIOContext:在 read 回调中读取前 10 字节,若为
ID3,用 synchsafe 整数解析出标签长度(4 字节,每字节低 7 位有效)并跳过,其余数据透传给 FFmpeg - 预处理:先拉取并剥离 ID3 头,再交给 FFmpeg 打开
- 若能判断内容为 fMP4,也可配合
-f mp4强制指定 demuxer(仍需解决偏移问题)
环境
- SPlayer-Next: 1.1.0-1(AUR
splayer-next-bin,Electron 构建) - 系统: CachyOS(Arch Linux 衍生),GNOME 50.5 / Wayland,PipeWire
- FFmpeg: Lavc63.1.102(系统版本,用于独立对比验证)
- 账号: 网易云音乐(云盘歌曲,均为本人上传)
补充说明
该现象不影响官方客户端,说明网易返回的数据本身没有问题;问题出在解码链路的 ID3 处理上。附带说明:所有失败 URL 的路径均含 jd-musicrep-privatecloud-audio-public,即未匹配到曲库的私有云文件(本人原创歌曲必然属于此类)。
Activity
github-actions commentedon Sep 27, 2026
👋 @acgmnc
感谢提交 Issue!我们已经收到您的反馈,会尽快确认您的问题。
Thanks for opening this issue! We have received your report and will review it as soon as possible.
acgmnc commentedon Sep 27, 2026
补充:最小复现(无需网易云账号)
已拿到该曲目的上传原文件,确认网易云只是在其前面附加了 ID3v2.3 标签,音频数据一个字节都没改;并进一步验证出最小复现——只要在任意 MP4/M4A 前拼一个 ID3v2.3 头(哪怕只有 10 字节的空标签),FFmpeg 就会报完全相同的错。
与标签内容、编码参数都无关:真实案例的标签含 35 KB 封面图、音频是 HE-AAC 48kHz 立体声;最小复现用空标签 + AAC-LC 44.1kHz 单声道,行为完全一致。只要
AudioReader::new(http)读到的字节以ID3开头、其后是 MP4 容器,就会落入同一个失败分支。真实案例的字节级对照
d19d65e514f6a474a0fa137b9e46b1e01e4a2555908fdc79d445ba7758c222b8d19d65e514f6a474a0fa137b9e46b1e01e4a2555908fdc79d445ba7758c222b8剥离 ID3 后的 SHA256 与原文件完全一致,即网易云未做任何转码或再封装,问题纯粹由「ID3 前缀 + MP4 容器」这个组合引起。
对正文「影响范围」表的一处精确化
正文表格中「上传到云盘的原始格式」这一列,更准确的说法是文件的真实容器格式(与扩展名无关)——本例的原文件扩展名是
.mp3,但容器实际是 MP4,同样触发该问题。修正后的表述:.mp3.flac.mp3的情况)fix: 修复 ID3 前缀 MP4 音频解码失败 fixed SPlayer-Dev#329
apoint123 commentedon Oct 1, 2026
经过测试,这个问题的根本原因是 FFmpeg 上游在 2025 年 7 月引入的一处回归
在旧版 FFmpeg 中,
avformat_open_input遇到输入流时默认会无条件探测并读走 ID3v2 标签,流指针会自动 Seek 至 ID3 之后;而 FFmpeg 在提交 FFmpeg/FFmpeg@9d037c5 中引入了AVFMT_FLAG_ID3V2_AUTO格式白名单机制(仅包含mp3、aac、tta、wav、flac),将mov/mp4排除在外。虽然 MP4 本身不应使用 ID3 标签,但这一改动导致了探测阶段与打开阶段的割裂:av_probe_input_format3探测时仍保留了跳过 ID3 的逻辑,因此mov_probe看到的切片是合法的ftypbox,并给出了匹配满分0。但由于mov不在is_id3v2_format白名单中,FFmpeg 不再自动剥离 ID3 头部mov_read_default从偏移量0开始解析 MP4 Box,读取前 8 字节49 44 33 03 00 00 00 00:0x49443303(ASCII'ID3\x03')被识别为 Box 长度:1229206275字节(约 1.14 GiB)0x00000000被识别为未知 Box 类型,demuxer 尝试跳过该 Box 的 payload,导致直接 Seek 越界至文件末尾moovbox,抛出moov atom not found(-1094995529/AVERROR_INVALIDDATA)受影响环境信息:
ffmpeg-audio打包的 8.1.2 版本,因此触发该回归提交 181640d 所做的修复目测是没问题的,但它本质上是对网易云不规范行为和 FFmpeg 回归的 Workaround,建议在注释中补充上下文,避免其他人对这段防御性代码产生困惑;长远来看也可以考虑向 FFmpeg 上游报告或提交修复
acgmnc commentedon Oct 1, 2026
感谢指正 —— 这个分析比 issue 正文准确得多。正文里对机制的描述("id3v2 解析器不会把数据接力给 mov 解析器")是错的,真正的原因是
AVFMT_FLAG_ID3V2_AUTO白名单把 mov/mp4 排除在外,导致 Rewind 后不再剥离 ID3。抱歉把方向带偏了一步。在你的结论基础上补一个独立验证:
系统 FFmpeg(n9.0.2 / libavformat 63)同样复现:
即:该回归在目前最新的 n9.0.2 上仍未修复,与 SPlayer-Next 内 8.1.2 的行为一致。