从 MP3 到 OGG-Opus:audio-recorder-polyfill 自定义编码器开发完全指南(init/encode/dump 协议详解)
【免费下载链接】audio-recorder-polyfillMediaRecorder polyfill to record audio in Edge and Safari项目地址: https://gitcode.com/gh_mirrors/au/audio-recorder-polyfill
audio-recorder-polyfill是一个 MediaRecorder polyfill 库:它让 Safari 与旧版 Edge 这类不支持 MediaRecorder 的浏览器,也能用 Web Audio API 录音,并默认输出 WAV / MP3 文件。本文将拆解它的「自定义编码器」机制,详解init / encode / dump消息协议,带你用 3 步开发一个压缩率更好的 OGG-Opus 编码器 🎙️
🎙️ 为什么需要自定义音频编码器?
浏览器对录音格式的支持一直“各说各话”:
- Chrome 原生只录
.webm,Firefox 录.ogg - Safari 与旧版 Edge 干脆没有 MediaRecorder —— 这正是该 polyfill 存在的意义
polyfill 自带两个编码器:
| 编码器 | 文件路径 | 特点 |
|---|---|---|
| WAV(默认) | wave-encoder/index.js | 不压缩,编码极快,但体积大 |
| MP3 | mpeg-encoder/index.js | 128 kbps 单声道,压缩更好 |
但如果你想要体积更小、质量更好的 OGG-Opus,就需要自己写一个编码器——这就是「自定义编码器」。它的设计非常轻巧:polyfill 主文件只有1.11 KB(min+gzip,由 size-limit 严格控制),所有“重活”都扔给一个可替换的编码函数,在独立的 Web Worker 里执行。
🔍 1 分钟了解项目结构
克隆仓库:
git clone https://gitcode.com/gh_mirrors/au/audio-recorder-polyfill核心文件(相对仓库根目录):
index.js:MediaRecorder 类主体。录音开始时把编码器函数转成字符串,装入“内联 Web Worker”(无需单独 worker 文件),再用 ScriptProcessor 每约 50ms 截一段 2048 个采样,postMessage给 workerwave-encoder/index.js:默认 WAV 编码器,最简单的参考实现mpeg-encoder/index.js:MP3 编码器,通过importScripts加载 lamejstest/demo/:可本地运行的交互式演示(index.js/index.pug/polyfill.js)
📡 init / encode / dump 消息协议详解
polyfill 与编码器之间只通过3 种消息通信,理解这张表就理解了整个协议:
| 消息 | 携带参数 | 发送时机 | 编码器要做的事 |
|---|---|---|---|
init | 采样率 | recorder.start()时 | 创建编解码器实例 |
encode | Float32Array 采样 | 录音中每 ~51ms 一次 | 编码当前帧,只缓存不返回 |
dump | 采样率 | stop()/requestData()/ timeslice 定时器触发 | postMessage返回完整文件字节 |
三个关键细节:
- encode 不产出文件。例如 mpeg-encoder 收到
encode后只是encodeBuffer+ 拼接缓存,只有dump才触发postMessage回传 - encode 的负载是单声道 float 采样,数值范围
[-1, 1](对应index.js中的getChannelData(0))。WAV 编码器在dump时才把它转成 16-bit PCM 并写 44 字节 RIFF 头 - 回传的字节流会被主线程包成 Blob(类型为
mimeType)并触发dataavailable事件——所以你的编码器只管吐字节,格式语义由 mimeType 声明
🛠️ 3 步开发自己的 OGG-Opus 编码器
第 1 步:准备 Worker 版 Opus 库
用 Emscripten 把 Opus 编码器编译成 JS(可再封装 OGG 容器),作为静态资源放到项目中,编码器内部用importScripts加载。注意:Worker 里importScripts只能加载同源资源。
第 2 步:编写编码器函数(官方模板)
编码器必须是一个“函数体即 Worker 脚本”的函数:
module.exports = () => { let encoder // Opus 编码器实例 function init (sampleRate) { encoder = new OpusEncoder(sampleRate, 1) // 单声道 } function encode (input) { // input 为 Float32Array,范围 [-1, 1] encoder.push(input) } function dump (sampleRate) { let bytes = encoder.flush() // 完整 OGG 文件 encoder = new OpusEncoder(sampleRate, 1) // 为下一段重置 postMessage(bytes) } onmessage = e => { if (e.data[0] === 'init') init(e.data[1]) else if (e.data[0] === 'encode') encode(e.data[1]) else if (e.data[0] === 'dump') dump(e.data[1]) } }第 3 步:注册编码器与 MIME 类型
import AudioRecorder from 'audio-recorder-polyfill' import oggEncoder from './ogg-opus-encoder' AudioRecorder.encoder = oggEncoder AudioRecorder.prototype.mimeType = 'audio/ogg' window.MediaRecorder = AudioRecorder至此,所有按标准 MediaRecorder API 写的代码(start/stop/pause/dataavailable……)原样可用——接口兼容正是 polyfill 最大的收益 ✅
⚠️ 5 个常见踩坑清单
- 闭包引用主线程变量:polyfill 会把编码器函数转字符串再创建 Worker(见
index.js的createWorker),函数外部的变量在 Worker 里统统不可见,外部依赖只能靠importScripts - 硬编码采样率:一律以
init传入的AudioContext.sampleRate为准(可能是 44100 或 48000),可参考test/index.test.js中 “sends the AudioContext sample rate in the initialize method” 用例 - 采样类型不匹配:lamejs 要求 Int16(乘 32767.5 转换,见 mpeg-encoder),Opus 直接吃 float——两个自带编码器就是最好的对照范例
- timeslice 语义:
start(1000)时每次dump都产出一个独立完整文件(WAV 会重复写文件头,MP3/OGG 可各自独立成段)。需要连续文件就拼接上传或关闭 timeslice - postMessage 转移语义:ArrayBuffer 转移进 Worker 后原 buffer 失效,
dump里要新建缓冲或重置编码器状态(参考 mpeg-encoder 的recorded = new Int8Array())
📁 关键源码与测试位置
index.js:协议三个发送点(['init', …]/['encode', …]/['dump', …])与事件分发wave-encoder/index.js:44 字节 WAV 头 + PCM 写入,最小可读实现mpeg-encoder/index.js:含init状态初始化的完整压缩编码器示例wave-encoder/wave-encoder.test.js、mpeg-encoder/mpeg-encoder.test.js:编码器单测模板,写 OGG-Opus 测试时可直接照抄test/index.test.js:状态机、timeslice 分片、更换编码器等行为测试test/demo/index.js:演示页逻辑,适合验证新编码器输出的e.data.type与大小
✅ 总结
- audio-recorder-polyfill 把“编码”抽象成一个可替换的 Worker 函数,协议固定为
init / encode / dump三消息 - 开发新编码器只需:实现三个函数 +
onmessage分发,再用AudioRecorder.encoder两行注册 - 换上 OGG-Opus 后,同样音质下文件体积可约为 MP3 的 1/10,只需额外留意 timeslice 的“分段独立文件”语义
【免费下载链接】audio-recorder-polyfillMediaRecorder polyfill to record audio in Edge and Safari项目地址: https://gitcode.com/gh_mirrors/au/audio-recorder-polyfill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考