- 音视频
- 视频处理
- 音频处理
【免费下载链接】mediabunny
Pure TypeScript media toolkit for reading, writing, and converting video and audio files, directly in the browser.
@mediabunny/aac-encoder是 Mediabunny 官方推出的 AAC 编码扩展包:它为 WebCodecs 不支持 AAC 编码的浏览器提供了一致、可靠的 AAC-LC 编码能力,底层基于 Mediabunny 的 custom coder API,并内置了一个经过尺寸优化的 FFmpeg WASM 构建。读完本文,你将掌握该扩展的安装方式、registerAacEncoder注册机制、与Conversion管线联动的完整转换示例、其 worker + WASM 桥接的实现原理,以及从 FFmpeg 源码出发重新构建 WASM 的完整命令流程。
为什么需要 AAC 编码扩展
AAC 是 MP4、ADTS 等容器中最常见的音频编码格式之一,但在实际浏览器环境中,部分浏览器的 WebCodecs 实现并不支持 AAC 编码(AudioEncoder对aac的编码支持并不统一)。Mediabunny 的核心包会优先检测原生能力,检测不到时才需要外部编码器兜底。
@mediabunny/aac-encoder正是为此设计的扩展包:它通过 Mediabunny 公开的 custom coder API(参见 src/custom-coder.ts 中的registerEncoder)把 FFmpeg 的 AAC 编码器注册进 Mediabunny 编码管线。项目官方将其描述为基于 FFmpeg AAC 编码器的快速、尺寸优化(size-optimized)WASM 构建,编译参数中的-Oz、-flto、-msimd128也印证了这一点(见 bridge.c 的编译章节)。
扩展包与 Mediabunny 主包采用 peer dependency 关系,
package.json中声明了"peerDependencies": { "mediabunny": "^1.0.0" },许可证为 MPL-2.0,详见 packages/aac-encoder/package.json。
安装
扩展包与 Mediabunny 主包一起通过 npm 安装:
npm install mediabunny @mediabunny/aac-encoder如果你没有使用模块打包器,也可以通过<script>标签直接引入预构建产物:
<script src="mediabunny.js"></script> <script src="mediabunny-aac-encoder.js"></script>这会暴露两个全局对象:Mediabunny与MediabunnyAacEncoder。若使用这种方式,可用mediabunny-aac-encoder.d.ts为这些全局对象补充 TypeScript 类型。预构建的分发文件可从仓库的 releases 页面下载。
快速上手:注册编码器
使用扩展只需要一行调用:
import { registerAacEncoder } from '@mediabunny/aac-encoder'; registerAacEncoder();注册完成后,Mediabunny 会在需要编码 AAC 时自动使用该编码器,无需再改动任何调用代码。
更严谨的写法是先探测浏览器原生能力,仅在原生不支持时才注册自定义编码器,避免覆盖可能更优的原生实现:
import { canEncodeAudio } from 'mediabunny'; import { registerAacEncoder } from '@mediabunny/aac-encoder'; if (!(await canEncodeAudio('aac'))) { registerAacEncoder(); }从源码看,canEncodeAudio的判定逻辑(src/encode.ts)会先检查是否已注册支持该配置的自定义编码器——即customAudioEncoders.some(x => x.supports(codec, encoderConfig))——命中即返回true,其次才检查 PCM 编码和原生AudioEncoder。而registerEncoder在注册成功后还会清空canEncodeAudioMemo缓存(见 src/custom-coder.ts),确保后续探测立即反映新注册的编码器。测试用例 test/node/aac-encoder-extension.test.ts 也验证了这一点:注册前await canEncode('aac')为false,调用registerAacEncoder()后变为true。
完整示例:将输入文件转换为带 AAC 音频的 MP4
下面把任意输入文件(例如从文件选择器拿到的File)转换为 MP4,并让音频轨使用 AAC 编码:
import { Input, ALL_FORMATS, BlobSource, Output, BufferTarget, Mp4OutputFormat, canEncodeAudio, Conversion, } from 'mediabunny'; import { registerAacEncoder } from '@mediabunny/aac-encoder'; if (!(await canEncodeAudio('aac'))) { // 仅在原生不支持时注册自定义编码器 registerAacEncoder(); } const input = new Input({ source: new BlobSource(file), // 例如来自文件选择器 formats: ALL_FORMATS, }); const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget(), }); const conversion = await Conversion.init({ input, output, audio: { codec: 'aac', }, }); await conversion.execute(); output.target.buffer; // => 包含 MP4 文件的 ArrayBuffer几个要点:
Input负责解封装,ALL_FORMATS表示自动识别所有 Mediabunny 支持的容器格式;Output的format: new Mp4OutputFormat()决定输出封装为 MP4,target: new BufferTarget()让结果直接落到内存中的ArrayBuffer;Conversion.init中的audio: { codec: 'aac' }明确要求音频轨编码为 AAC,配合已注册的扩展即可在无原生支持的环境下完成编码;- 编码质量可通过
Quality指定比特率(如new Quality({ bitrate: 128000 }),即 128 kbps),对应测试用例中的用法可参考 test/node/aac-encoder-extension.test.ts; - 若还需要更丰富的输入输出方式(如自定义媒体源、
AudioSample直灌、分片 MP4 输出等),可进一步阅读 使用指南 与 输出格式。
支持范围与配置约束
编码器在注册后并非对所有 AAC 编码请求都生效,其静态supports判定(见 packages/aac-encoder/src/encoder.ts)限定了以下条件:
| 维度 | 约束 |
|---|---|
| codec | 仅'aac'(AAC-LC,解码器配置 codec 串为mp4a.40.2) |
| 声道数 | 1 至 8 声道(numberOfChannels介于 1 和 8 之间) |
| 采样率 | 必须是 AAC 标准采样率集合中的一员:96000、88200、64000、48000、44100、32000、24000、22050、16000、12000、11025、8000、7350 Hz |
| 比特率 | 必须显式指定(bitrate !== undefined),否则视为不支持 |
因此在实际使用时,请确保通过Quality或编码配置提供比特率,且输入音频的采样率在上述集合内。
另外,扩展还支持以 ADTS 流格式输出:在编码配置中设置aac: { format: 'adts' }时,编码器会把 FFmpeg 初始化阶段返回的 extradata(AudioSpecificConfig)解析出来,通过buildAdtsHeaderTemplate构建 ADTS 头模板,并为每个编码包动态写入帧长度后拼接为完整 ADTS 帧(逻辑见 src/encoder.ts 与 shared/aac-misc.ts);未开启 ADTS 时,则保留原始 AudioSpecificConfig 作为description元数据供 MP4 封装使用。
实现原理:custom coder API + Worker + WASM 桥接
整个扩展可以拆成三层,每层都能在仓库中找到对应源码:
1. 主线程侧:AacEncoder extends CustomAudioEncoder(src/encoder.ts)
- 通过
registerEncoder(AacEncoder)挂载到 Mediabunny 的 custom coder 注册表(src/custom-coder.ts); init()时创建专用 Worker,把声道数、采样率、比特率以init命令发给 Worker,Worker 返回编码器上下文句柄ctx、每帧采样数frameSize和 extradata(AudioSpecificConfig);encode()将AudioSample抽取出交织的 f32 数据后累积到内部缓冲区,凑满一个编码帧(AAC-LC 通常为 1024 采样)才送入 Worker 编码,避免频繁跨线程通信;flush()用静音(0 填充)补齐尾部不足一帧的采样,随后向 Worker 发送flush命令排空编码器内部缓冲,并重置内部状态;- 编码得到的
EncodedPacket统一标记为关键帧('key'),并携带从采样时间戳换算出的秒级时间戳与时长。
2. Worker 侧:Emscripten 模块装载与命令分发(src/encode.worker.ts)
- Worker 使用
createModule()(来自build/aac,即 build/aac.js,构建产物已随仓库提交)加载 WASM; - 通过
cwrap绑定init_encoder、get_encoder_frame_size、send_frame、receive_packet、flush_encoder_start、reset_encoder等 C 导出函数; - 命令协议(
init/encode/flush)与响应结构定义在 src/shared.ts; - 编码结果通过
Transferable零拷贝转移回主线程(postMessage携带transfer数组),大块音频数据同样用转移而非拷贝; - 附带一个细节:Worker 内常驻一个
setInterval(() => {}, 1000)空定时器,用于防止 Firefox 将空闲 Worker 随机回收。
3. WASM 侧:FFmpeg 桥接层(src/bridge.c)
桥接层直接调用 FFmpeg 的libavcodec与libavutil:
init_encoder通过avcodec_find_encoder(AV_CODEC_ID_AAC)找到 AAC 编码器,创建AVCodecContext并设置sample_fmt = AV_SAMPLE_FMT_FLTP、采样率、比特率与声道布局,然后avcodec_open2打开编码器;send_frame负责把 JavaScript 传入的交织 f32数据按声道去交织(deinterleave)写入 AVFrame 的各平面缓冲(planar 布局),再调用avcodec_send_frame;receive_packet通过avcodec_receive_packet取回编码后的 AVPacket,返回包大小与 PTS、时长;flush_encoder_start以NULL帧调用avcodec_send_frame触发编码器排空,reset_encoder则调用avcodec_flush_buffers复位;close_encoder统一释放输入缓冲、AVFrame、AVPacket 与 AVCodecContext。
主线程、Worker 与 WASM 三者通过postMessage形成一条「帧累积 → 转移 → 编码 → 回传」的流水线,这也是该扩展能把浏览器当作 AAC 编码目标的核心机制。
测试验证
扩展的 Node 侧测试集中在 test/node/aac-encoder-extension.test.ts,覆盖三个关键行为:
- 注册生效:注册前
canEncode('aac')为false,注册后为true; - 端到端编码往返:用 48 kHz 双声道正弦波生成
AudioSample,经Output+AudioSampleSource(codec: 'aac'、128 kbps)编码为 MP4,再重新Input读回,断言音频轨 codec 为aac、采样率为 48000、声道数为 2,解码出的关键帧包数量大于时长 × 采样率 / 1024,且总时长与 2 秒接近; - 大时间戳健壮性:对
timestamp = 1e9(约 231 天)的音频样本编码后,回读得到的首包时间戳与原始时间戳一致,验证了 PTS 传递精度。
浏览器侧可参考 test/browser/worker-error.test.ts 对 worker 加载失败等错误路径的覆盖。如果希望在自己的项目中复现上述流程,可以直接把测试中的createSineWave换成你的真实音频数据。
从源码构建 WASM
所有已构建的 WASM 产物(packages/aac-encoder/build/aac.js)都已包含在仓库中,因为它们很少变化,日常开发无需重新构建。但如果你需要定制 FFmpeg 编译选项,可以按以下步骤从零构建:
前置条件:安装 Emscripten(确保emcc、emmake等命令可用),并准备好一份 FFmpeg 源码树。在 Mediabunny 仓库根目录下执行:
export FFMPEG_PATH=/path/to/ffmpeg export MEDIABUNNY_ROOT=$PWD # 1. 以精简配置交叉编译 FFmpeg cd $FFMPEG_PATH emmake make distclean emconfigure ./configure \ --target-os=none \ --arch=x86_32 \ --enable-cross-compile \ --disable-asm \ --disable-x86asm \ --disable-inline-asm \ --disable-programs \ --disable-doc \ --disable-debug \ --disable-all \ --disable-everything \ --disable-autodetect \ --disable-pthreads \ --disable-runtime-cpudetect \ --enable-avcodec \ --enable-encoder=aac \ --cc="emcc" \ --cxx=em++ \ --ar=emar \ --ranlib=emranlib \ --extra-cflags="-DNDEBUG -Oz -flto -msimd128" \ --extra-ldflags="-Oz -flto" emmake make # 2. 编译 JavaScript 与 FFmpeg API 之间的桥接层 cd $MEDIABUNNY_ROOT/packages/aac-encoder emcc src/bridge.c \ $FFMPEG_PATH/libavcodec/libavcodec.a \ $FFMPEG_PATH/libavutil/libavutil.a \ -I$FFMPEG_PATH \ -s MODULARIZE=1 \ -s EXPORT_ES6=1 \ -s SINGLE_FILE=1 \ -s ALLOW_MEMORY_GROWTH=1 \ -s ENVIRONMENT=web,worker \ -s FILESYSTEM=0 \ -s MALLOC=emmalloc \ -s SUPPORT_LONGJMP=0 \ -s EXPORTED_RUNTIME_METHODS=cwrap,HEAPU8 \ -s EXPORTED_FUNCTIONS=_malloc,_free \ -msimd128 \ -flto \ -Oz \ -o build/aac.js对关键参数做一点说明:
- FFmpeg 侧通过
--disable-all+--disable-everything关闭全部模块,再单独--enable-avcodec与--enable-encoder=aac,把产物裁剪到最小;-Oz、-flto、-msimd128用于进一步压缩体积并启用 SIMD 加速; - Emscripten 侧的
-s SINGLE_FILE=1会把编译出的 WASM 二进制以内联方式合并进build/aac.js,这正是该文件“同时包含 JS 胶水代码与内联 WASM”的原因; -s ENVIRONMENT=web,worker限定运行环境为浏览器主线程与 Worker,-s FILESYSTEM=0去掉不需要的文件系统支持,-s MALLOC=emmalloc使用更轻量的内存分配器,这些都与 src/encode.worker.ts 中createModule()的加载方式对应。
构建完成后,build/aac.js即被 src/encode.worker.ts 引用。整个 JavaScript 包随后可以通过在 Mediabunny 根目录运行npm run build,随主项目一起打包发布。
延伸阅读
- 扩展包在官网文档中的对应页面:docs/guide/extensions/aac-encoder.md;
- custom coder API 的完整说明与注册机制:docs/guide/supported-formats-and-codecs.md,实现见 src/custom-coder.ts;
- Mediabunny 使用入门:docs/guide/introduction.md,输出格式与媒体源/汇的更多用法见 docs/guide/output-formats.md 与 docs/guide/media-sources.md。
- 音视频
- 视频处理
- 音频处理
【免费下载链接】mediabunny
Pure TypeScript media toolkit for reading, writing, and converting video and audio files, directly in the browser.
相关推荐
LikeC4 AI 语义布局系统提示词深度解析:让 LLM 为 Graphviz 生成可读、均衡的架构图布局
LikeC4 AI 语义布局系统提示词深度解析:让 LLM 为 Graphviz 生成可读、均衡的架构图布局 LikeC4 通过 packages/layout
音视频视频处理音频处理Mediabunny MP3 编码扩展 @mediabunny/mp3-encoder:基于 LAME WASM 的浏览器与服务器端 MP3 编码方案
Mediabunny MP3 编码扩展 @mediabunny/mp3 encoder:基于 LAME WASM 的浏览器与服务器端 MP3 编码方案 Medi
音视频视频处理音频处理Mediabunny 浏览器端 DTS 音频编解码扩展 @mediabunny/dts 使用与原理指南
Mediabunny 浏览器端 DTS 音频编解码扩展 @mediabunny/dts 使用与原理指南 DTS(Digital Theater Systems
音视频视频处理音频处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考