首先,为什么要切成小文件:如果你像我一样将音乐储存在文件服务器上, 通过映射驱动器然后在本地播放。那么切分对于文件服务器具有极大的优势, 文件服务器可以有效地缓存小文件。例如使用ZFS作为储存后端, 启用ARC+L2ARC后可以最大限度地利用缓存,从而获得更短的响应时间,同时也减少机械磁盘的读写。

也许大家知道ARC和L2ARC都是块级的缓存机制,切不切开来好像也没太大所谓。 但大家也应该知道并不是所有协议都支持随机读取的,比如基于WebDAV的网络共享在处理大型文件上就比较尴尬了。

快速上手

首先,介绍一下我写的一个Python小工具cuecut, 用pip安装后直接就可以在命令行中使用cuecut命令根据cue文件切分对应的音频文件了。

要是对原理不是很感兴趣的话,那么把FFmpeg的可执行文件复制到PATH中的任一目录, 假设cue文件的位置是/a/b.cue,希望切分出来的音频文件是flac格式,那么打开命令行,输入如下命令

cuecut /a/b.cue -c flac

就可以了。音频文件将会自动根据cue文件中的演奏者,单曲名称命名为演奏者 - 单曲名。 当然目前还不支持配置自动命名的样式,也许以后会有吧。

原理与方法论

技术上还是在使用ffmpeg的命令行,因此难点还是主要集中在ffmpeg的命令行参数的选取上。 首先一些无关痛痒的参数,诸如是否显示banner(--hide_banner)、输入文件是什么(-i FILE)、 输出编码是什么(-c:a codec)、日志级别(-loglevel levelname)、元数据(-metadata xxx=yyy)就不再展开讨论了。 接下来主要讨论一些比较重要的参数:

移除原有的元数据

对于有一些音频镜像文件,原作者会将cue文件作为元数据写入音频文件中,如果不移除这些元数据, 可能会导致播放音频文件时出现分章。因此需要用参数 -map_metadata -1来移除这些元数据。

切取位置

既然是切分出音轨,那么就要确定切分位置,音轨的开始位置(或者说偏移量)用-ss where参数描述, 其中where是一个浮点数(建议只取小数点后两位),结束位置则用-to where参数描述。

元数据写入

既然要写入一些诸如演奏者、音轨名或者专辑名称的元数据,那么就要让FFmpeg写入ID3数据块, 这一点可以用 -write_id3v1 1参数来指定。

移除视频流

部分音频镜像会把专辑封面作为一个视频流插入文件中,在切分这样的文件时, 往往第一个音轨是能够正常获得这个视频流的。然而后续的音轨都将无法获得, 同时由于这个问题,还进一步导致后续音轨丢失长度信息(duration),会使部分播放器无法seek到某一具体位置。 目前没有什么特别好的方法让所有音轨都正确地获取视频流,因此较为实际的方案就是扔掉所有视频流, 用参数 -vn 来完成。

平台依赖的非法文件名

这个问题是可以通过修改输出文件名的模式来规避的,主要是我的cuecut中的文件名模式中存在演奏者和单曲名, 因此对于一些奇怪的名字还是要加以处理的,例如名字中不能出现 \/:*?<>| 这些字符。 此外在Windows平台上AUX,CON,COM[1-9],NUL,LPT[1-9],PRN都是保留名称, 即使带扩展名也不能创建这类文件。