moonmux

    Audio container toolkit in pure MoonBit: Ogg page/packet read and write, CRC-32 verification, WAV parsing and encoding, and an Ogg-to-WAV transcoding pipeline

    ogg
    container
    mux
    demux
    audio
    codec
    Download zip
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    4 hours ago
    Downloads
    3
    作者:WangYF0829(王轶凡)|创建日期:2026-10-13|用途:moonmux 项目文档

    #moonmux

    CI License Tests Version

    纯 MoonBit 实现的音频容器工具包,补上 MoonBit 生态中「能解码、不能封装」的最后一环。

    #为什么做这个

    截至 2026 年 10 月,MoonBit 生态已经有一批纯 MoonBit 编写的音频解码器(Opus、Vorbis、MP3、WAV 等),也有播放管线,但没有一个可独立复用的容器库:解码器各自在包内附带一份仅供自己使用的 Ogg 代码,外部取不到,而且普遍只读。解出来的 PCM 能存成 WAV,却没法重新封装回 Ogg。

    (边界说清楚:本项目补的是「读写已有压缩包」这一环 —— 把包重新封装成合规的 Ogg、读写注释、定位与诊断。不做 PCM → Ogg 的编码器,那需要 Opus / Vorbis 编码器本体,不在本项目范围;解码产物落盘走 to-wav。)

    这不是「没人想到」,而是价值链的最后一环空缺:

    环节生态现状
    读容器(demux)有:isobmff(MP4 解析)、moontsinspect(TS 检查)
    解码有:moonopus、moonvorbis、mp3.mbt、moon-wav
    播放有:moon_rodio
    写容器(mux)没有
    转码管线没有

    moonmux 补的就是最后两行。

    #功能范围

    模块内容状态
    ogg/页 / 段 / 包解析与序列化;Ogg 专用 CRC-32;多逻辑流交织;物理流链接与拆分;按大小攒页;granule 二分定位;结构体检(分级 + 坏页重同步);页级补丁已完成
    codec/编解码头解析(OpusHead / Vorbis ID)、时长换算、VorbisComment / OpusTags 读写已完成
    manifest/demux/mux 的流目录清单(流号、包数、链序号、逐包粒度)的解析与生成已完成
    wav/WAV(RIFF/PCM)读写已完成
    pipeline/Ogg(Opus / Vorbis)→ WAV 转码管线已完成
    cmd/main/命令行工具:info、demux、mux、cat、split、extract、dump、tags、tag、seek、verify、to-wav已完成

    容器层(ogg/、wav/)不依赖任何三方包:容器格式的正确性应当能被独立验证,不该被解码器的缺陷掩盖。解码环节留给生态中已有的纯 MoonBit 解码器(LL728/moonopus、LL728/moonvorbis),通过 pipeline/ 对接 —— 三方依赖被隔离在这一层,不在容器层。

    #作为库使用

    模块已按 mooncakes.io 规范命名(WangYF0829/moonmux)。发布后可直接引用:

    moon add WangYF0829/moonmux

    容器层(ogg/、wav/、codec/、manifest/)全部是纯函数、零依赖,可以单独复用; 只有 pipeline/ 会引入解码器。

    #退出码约定

    退出码含义
    0成功
    1数据校验未通过(输入不是合法 Ogg、被截断、清单与目录不符、verify 的 error / CRC 失败)
    2用法或 I/O 错误(参数错、文件读不到、拒绝覆盖非空目录等)

    脚本与 CI 可直接据此判断,不必 grep 中文输出。

    写命令要求输入完整:每页 CRC 通过、每条逻辑流以 EOS 收尾、尾部最多 1 MiB 全零填充; 缺 EOS 的直播流会被拒绝(本工具面向静态文件)。verify 对缺 EOS 只报 warn(退出 0), 写命令则按数据错误处理(退出 1)。 输入与输出路径相同(含大小写别名、..、硬链接)会被拒绝;内容与输入完全相同的已有输出也会被拒绝。 mux 还会把已有输出与实际读取的每个包/清单做身份比较,拦截目录外任意文件名的硬链接别名。 写命令面向单写者:不要并发写同一个输出文件。

    只读命令对损坏文件保持宽容:info / dump / tags 在文件有结构问题时尽量给出能读到的信息(info / dump 会提示「只解析到偏移 X」),退出码仍为 0;会写产物的命令(demux / mux / cat / split / extract / tag / to-wav / chop)在输入有 error 级结构问题、被截断或清单不符时拒绝执行并返回 1 —— 不产出半截文件。端到端检查见 scripts/smoke.sh(POSIX 42 项 / Windows 40 项,覆盖往返、退出码、截断、清单异常、FIFO 与输出别名防护)。

    #能力边界

    项上限原因
    单文件大小2 GiBMoonBit Int 是 32 位,页偏移与长度都用它;@fs 也只有整文件 API
    demux 包数999999包文件名固定 6 位,超过后字典序错乱、mux 读回顺序不对
    to-wav PCM 预算256 MiB(MOONMUX_MAX_PCM_MB 可调大)整条链路没有流式写接口,PCM 必须攒齐再落盘;实测峰值约为预算的 3–4 倍。外部解码器一次返回整段 PCM,恶意伪造的 granule 仍可能造成超出预算的瞬时分配
    chop Opus 时间戳页精度,约 ±1 页不重写 OpusHead 的 pre-skip;裁剪后首个数据页可能多带不到一页的前导样本
    to-wav 速度Opus 约 1.5× 实时;Vorbis 约 80× 实时纯 MoonBit 解码器;Vorbis 路径明显更慢,长文件请先 split
    chained 拆分整段都由单页流组成时无法拆分与「一段内交织两条单页流」字节相同,规格上不可判定(保守不切)
    裁剪流时长上界容器层拿不到首个音频页的样本数,精确值需解码

    内存量级(实测,19 MB / 300 段输入):info 约 2.5×、verify 约 2.6×、cat/mux 约 3.5×; to-wav 还要叠加解码器的 Double PCM(约 4–5× 输出)。所有命令整文件读入,没有流式写接口。

    #构建与测试

    需要 MoonBit 工具链。在 PowerShell 中安装:

    irm https://cli.moonbitlang.cn/install/powershell.ps1 | iex

    安装后新开终端,确认可用:

    moon version

    构建与测试:

    moon build moon test

    #使用

    命令行工具的用法:

    # 查看容器信息(自动识别 Ogg 与 WAV) # Ogg:格式、流号、页数、包数、负载字节、各流末粒度;能识别音频流时再给编码/声道/采样率/时长 # WAV:声道、采样率、位深、PCM 字节数、帧数与时长 moonmux info input.ogg moonmux info input.wav moonmux info input.ogg --json # 单行 JSON,便于脚本处理 # 注:时长为「末粒度 − 首粒度 − pre-skip」,对裁剪过的流是上界 # 结构体检:逐页 CRC + 页序号连续性 + BOS / EOS 完备性 + 段表一致性 # 用于判断文件是容器损坏还是编码问题,并给出损坏的具体位置 moonmux verify input.ogg moonmux verify input.ogg --json # 完整 findings(文本只铺前 10 条) # 解复用:按逻辑流拆出包,每条流一个子目录(s1/、s2/……) moonmux demux input.ogg ./packets/ # 复用:把包重新封装成 Ogg # 目录里有子目录 → 每个子目录一条逻辑流,按 Ogg 规则交织(BOS 页置顶、数据页按粒度升序) # 目录里没有子目录 → 整个目录一条逻辑流 moonmux mux ./packets/ output.ogg # 链接:把多个 Ogg 文件首尾相接成一个物理流 # 流号不冲突时逐页原样透传(字节级无损);冲突时把后一个文件相撞的流换新流号 moonmux cat a.ogg b.ogg c.ogg output.ogg # 拆分:把链接流按链接点切回各自独立的 Ogg 文件(输出 part1.ogg、part2.ogg……) # 页原样沿用,字节与 CRC 一字节不改 —— 非链接流得到的 part1 与输入完全相同 moonmux split chained.ogg ./parts/ # 抽流:把第 n 条逻辑流单独取出来(页级原样,可与输入逐字节对账) moonmux extract input.ogg 2 single.ogg # 页级转储:偏移 / 流号 / 页序 / 粒度 / 标志 / 段数 / 负载 moonmux dump input.ogg moonmux dump input.ogg --hex # xxd 兼容,可 `| xxd -r` 还原 # 注释:读取与改写(KEY= 表示删除该键) moonmux tags input.ogg moonmux tag input.ogg tagged.ogg TITLE=月光 ARTIST=王轶凡 # 定位:按毫秒找到不晚于该时刻的页(打印页下标、字节偏移与该页起始时间) # 链接流可用第 3 个参数指定第几段(默认第 1 段) moonmux seek input.ogg 2500 moonmux seek chained.ogg 500 2 # 转码:Ogg(Opus / Vorbis)→ WAV;默认 16 位,可选 24 位 moonmux to-wav input.ogg output.wav moonmux to-wav input.ogg output24.wav 24 # 裁剪:按毫秒区间裁剪(页精度,头页保留,时间轴归零) moonmux chop input.ogg clip.ogg 1000 3000

    构建命令行工具需要 native 后端(MSVC):

    moon build --target native

    #项目结构

    moonmux/ ├── moon.mod 模块配置 ├── moon.pkg 根包 ├── moonmux.mbt 模块级描述 ├── ogg/ Ogg 容器 │ ├── crc.mbt CRC-32(RFC 3533 参数)与页校验 │ ├── page.mbt 页的解析与序列化 │ ├── packet.mbt 包重组与页切分 │ ├── mux.mbt 多逻辑流的分组与交织 │ ├── chain.mbt 物理流链接与拆分(chained bitstream) │ ├── verify.mbt 结构体检(段表 / 页序号 / BOS·EOS) │ └── stream.mbt 字节流 ↔ 页序列 ├── wav/ WAV 容器 ├── pipeline/ 转码管线(唯一引入三方解码器的包) ├── cli/ 命令行参数解析(纯逻辑) ├── cmd/main/ 命令行入口 └── testdata/ 真实音频样本

    #Shell 补全

    source scripts/moonmux.bash # 子命令与文件路径补全

    #参考规范

    • RFC 3533: The Ogg Encapsulation Format Version 0
    • RFC 7845: Ogg Encapsulation for the Opus Audio Codec
    • RFC 3534: The application/ogg Media Type
    • Microsoft RIFF 规范(WAV)

    #参考实现与取舍

    实现全部为 MoonBit 原创,没有移植或复制其他项目的代码。以下项目只在行为与取舍上作为对照:

    项目参考了什么没有照搬什么
    Xiph oggz / liboggz子命令的语义(rip/dump/validate/chop 的职责划分)、多流合并按呈现时间不做 Skeleton,不解析 Theora/FLAC 等头;dump 不可逆
    libogg页大小与 flush 时机(4096 阈值、首页/EOS 强制刷出)不把 BOS 顺序交给调用方;写侧自己维护不变量
    mutagen(Python)注释改写时用 padding 保持旧包等长、复用旧页布局不做 high/low 区间公式
    ffmpeg作为行为对照(转码产物用独立工具回读验证)不用时间驱动的分页策略
    libopusfileseek 在 granule 上插值二分、80 ms pre-roll只定位到页,不解码

    测试样本说明:testdata/ 下的 Opus / Vorbis 文件由 ffmpeg(libopus / libvorbis)生成,仅用于测试, 不包含任何第三方受版权保护的音频内容。

    #开发状态

    本项目为 2026 MoonBit 黑客松十月赛参赛作品,当前版本 0.2.0(2026-10-23)。

    已可用:Ogg 容器读写(含多逻辑流的分组与交织写出、物理流链接与拆分、结构体检)、WAV 读写、 Ogg → WAV 转码管线(Opus 与 Vorbis 均已实测), info / demux / mux / cat / split / extract / dump / tags / tag / seek / verify / to-wav / chop 十三个子命令, 237 个测试(Windows native;Linux native 236,跳过 Windows 专属设备名用例;wasm-gc 224)通过,并通过真实 Opus / Vorbis 文件的逐页 CRC、拆包—重封装往返验证、 双逻辑流交织—还原往返验证、物理流链接—拆分往返验证(含流号冲突场景)与转码验证。

    工程配套:moon check --deny-warn 零警告、moon fmt --check 通过、wasm-gc 与 native 双后端 237 个测试(Windows native;Linux native 236;wasm-gc 224)、POSIX 42 项 / Windows 40 项端到端检查(scripts/smoke.sh)、GitHub Actions CI(ubuntu + windows 矩阵, 外加覆盖率非回归门禁与 native 冒烟)。 变更记录见 GitHub Releases。

    尚未完成:MP4 / ISOBMFF 的封装;WAV → Ogg 的反向管线(生态中尚无纯 MoonBit 编码器)。

    #许可证

    Apache-2.0,见 LICENSE。

    describe

    fn describe() -> String

    模块名称与版本,供命令行工具的输出使用。

    Source Files