下载B站视频音频
以下均为 ChatGPT总结。
从 B站缓存中提取原始音频(Windows + FFmpeg)
最近折腾了一下 B站缓存音频的提取,发现很多所谓“音频下载器”其实会偷偷转码,导致下载后的音频听起来没有在线播放那么“润”。
尤其是:
- 雨声
- 白噪音
- 环境音
- 空间氛围类音频
非常容易听出区别。
于是研究了一下 B站客户端下载后的缓存结构,以及如何直接从缓存中提取原始音轨。
一、B站客户端下载的视频为什么打不开?
B站客户端下载的视频通常长这样:
1 | 173030787-1-30112.m4s |
它们不是普通 mp4,而是:
DASH 分离流
也就是:
- 视频流单独存储
- 音频流单独存储
播放器播放时再动态合并。
因此:
1 | .m4s ≠ 损坏文件 |
而是:
1 | MPEG-DASH 分片媒体流 |
二、为什么 ffmpeg 会报错?
直接:
1 | ffmpeg -i xxx.m4s |
可能出现:
1 | Invalid data found when processing input |
原因并不是文件损坏。
而是:
B站在文件前面插入了额外字节
正常 MP4/M4A 文件开头应该包含:
1 | ftyp |
这是 MP4 容器的文件头标识。
但 B站缓存文件前面可能塞了一些:
- 自定义标记
- metadata
- 校验数据
- 客户端头信息
导致:
1 | ftyp 不在文件开头 |
于是 ffmpeg 无法识别。
三、修复方法:寻找真正的 MP4 文件头
使用 Python 搜索:
1 | b.find(b'ftyp') |
找到真正的 MP4 header。
然后从该位置重新截取文件即可。
四、修复脚本
修复 m4s 文件
1 | python -c "from pathlib import Path; p=Path('173030787_nb2-1-30280.m4s'); b=p.read_bytes(); i=b.find(b'ftyp'); print('ftyp at', i); Path('fixed_audio.m4s').write_bytes(b[i-4:] if i>=4 else b)" |
运行后会输出:
1 | ftyp at 13 |
说明:
真正的 MP4 文件头在第 13 字节附近。
脚本会自动生成:
1 | fixed_audio.m4s |
五、为什么是 i-4?
MP4 的 box 结构:
1 | [4字节大小][4字节类型] |
例如:
1 | 00 00 00 20 ftyp |
因此:
ftyp 前面的 4 字节是 box 长度信息。
不能丢失。
所以需要:
1 | i - 4 |
而不是:
1 | i |
六、使用 ffmpeg 解析修复后的文件
检测音频信息:
1 | ffmpeg -i fixed_audio.m4s |
结果:
1 | Audio: aac (LC), 44100 Hz, stereo, 323 kb/s |
说明:
- AAC-LC
- 44.1kHz
- 323kbps
- 已经是较高质量音轨
并且:
1 | Packed by Bilibili XCoder |
说明这是 B站原始缓存流。
七、无损提取音频
提取为 m4a
1 | ffmpeg -i fixed_audio.m4s -c copy output.m4a |
注意:
1 | -c copy |
表示:
不重新编码
只是:
1 | 重新封装(remux) |
因此不会产生二次音质损失。
八、为什么不要转 MP3?
很多下载器会:
1 | AAC/Opus → MP3 |
属于:
有损转有损
尤其对白噪音、环境音损失明显。
因此:
如果已经是:
- AAC
- Opus
最好直接保留原编码。
九、这次排查得到的结论
很多时候:
1 | 在线播放比下载更好听 |
并不是错觉。
原因可能是:
1 | 在线播放: |
因此:
最稳定的方法
其实是:
1 | 客户端下载 |
而不是依赖各种“音频下载器”。
十、一些额外知识
m4s 不是特殊加密格式
本质仍然是:
1 | ISO BMFF / MP4 |
只是:
- 分片化
- 流媒体化
- header 可能不标准
真正 DRM 加密会复杂得多
例如:
- Widevine
- PlayReady
那种需要:
- license server
- decryption key
并不是简单修 header 就能解决。
而 B站这里更像:
1 | “防止直接拖文件” |
并不是真正 DRM。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 弧光!