一个 Minecraft mod 如何让我从零开始构建自己的 Java 媒体库
Source: Dev.to
背景
我在制作一个 Minecraft 模组,想直接通过 OpenAL 播放 MP3 文件,而不是使用 Minecraft 内置的音频 API。要以这种方式流式播放音频,我需要一个底层的 MP3 解码器。我喜欢挖掘一些冷门或老派的工具,于是找到了 Zoom JLayer——一个 2000 年代初的纯 Java MP3 解码器。它看起来很完美,但只对某些 MP3 文件有效。相同的扩展名、相同的播放器,却出现完全不同的行为。
发现 JLayer
深入研究 JLayer 的源码(有人把它 dump 到 GitHub 上)时,我发现了以前从未真正思考过的事情:MP3 文件并不都一样。它们有不同的比特率模式(CBR、VBR)、帧头、侧信息以及隐藏在字节中的其他结构。这把我拉进了一个兔子洞——*这些数值到底来自哪里?*答案就是:字节本身。
一旦我开始直接读取原始字节,我就停不下来。我开始研究媒体文件格式在字节层面的工作原理。当时,我发布了一个 JLayer 的自定义分支,让模组能够运行,然后继续前进。
构建 CodecMedia
后来我需要 WAV 支持和格式转换。虽然有 Java 包装的 FFmpeg,但它们显得很臃肿——大多数依赖本地二进制或庞大的外部工具链。我想要点不同的东西:零依赖、纯 Java、尽可能轻量。
于是我创建了 CodecMedia。最初它只是为 Minecraft 模组写的一个小工具,慢慢演变成一个通过直接解析实际字节结构来探测和验证文件格式的媒体库——不依赖任何本地工具。
使用方法
添加依赖
me.tamkungz.codecmedia
codecmedia
1.1.1探测音频文件
CodecMediaEngine engine = CodecMedia.createDefault();
ProbeResult probe = engine.probe(Path.of("song.mp3"));
System.out.println(probe.mimeType()); // audio/mpeg
System.out.println(probe.durationMillis()); // 213000
System.out.println(probe.streams().get(0).codec()); // mp3
System.out.println(probe.streams().get(0).sampleRate()); // 44100使用前进行验证
ValidationResult validation = engine.validate(
Path.of("song.mp3"),
new ValidationOptions(true, 64L * 1024L * 1024L)
);
System.out.println(validation.valid()); // true
System.out.println(validation.errors()); // []支持的格式
CodecMedia 目前支持以下格式的探测:
- MP3
- OGG/Vorbis/Opus
- WAV
- FLAC
- AIFF/AIFC
- …以及更多
它仍在持续开发中,我在实践中不断学习媒体格式的细节。如果你在 Java 环境下需要进行媒体探测且不想引入本地依赖,或许可以考虑使用它。
结论
市面上可能有更好的工具,也有很多替代方案。我构建 CodecMedia 并不是仅仅为了得到一个可用的工具,而是为了理解它是如何工作的——研究格式、读取字节,并通过实际动手构建来学习。CodecMedia 正是这次实验的产物。