@audio/encode-mp4

中文文档
1.2.1发布于 2026/10/07MITTypeScriptES Module近 30 天下载 2065

Encode PCM audio samples to MP4/M4A (ISO BMFF) — pure JS muxer, remuxer and iTunes-tag writer

原始使用文档
文档目录7 节
  1. mux(track, opts) → Uint8Array
  2. remux(src, audio, opts) → Uint8Array
  3. writeMeta(bytes, { meta, chapters }) → Uint8Array
  4. mp4(opts) → { encode(channels), flush(), free() }
  5. Spec references
  6. Streaming output
  7. License

@audio/encode-mp4 npm MIT

Pure-JS MP4/M4A/MOV muxer, remuxer, and iTunes-tag writer

npm install @audio/encode-mp4
import mp4 from '@audio/encode-mp4'
import { mux } from '@audio/encode-mp4/mux'
import { remux } from '@audio/encode-mp4/remux'
import { writeMeta } from '@audio/encode-mp4/meta'

No wasm, no codec knowledge beyond wrapping pre-encoded access units in ISO/IEC 14496-12 (ISOBMFF) boxes. mux() wraps AAC, ALAC, Opus, FLAC, MP3, or raw PCM samples into a complete .m4a/.mp4 file (moov before mdat, so it streams/plays progressively). remux() swaps or strips the audio track of an existing MP4/MOV without touching video. writeMeta() rewrites iTunes-style tags in place. The default export drives a codec encoder end to end for the m4a umbrella format. decode-mp4 (this ecosystem's demuxer) and decode-aac/meta are the two things every box this package writes is checked against.

mux(track, opts) → Uint8Array

let file = mux({
  codec: 'aac',              // 'aac' | 'alac' | 'opus' | 'flac' | 'mp3' | 'pcm'
  sampleRate: 44100,
  channels: 2,
  samples,                   // Uint8Array[] — one access unit per entry (raw AAC AU, no ADTS)
  config: audioSpecificConfig,
  durations: 1024,           // per-AU duration in `timescale` units — a number or per-AU array
  priming: 2112,             // encoder delay, PCM samples — edit list + iTunSMPB (aac)
}, { meta: { title: 'Song' }, chapters: [{ time: 0, title: 'Intro' }] })
track fieldDefault
codec—aac | alac | opus | flac | mp3 | pcm (required)
sampleRate, channels—required
samples—Uint8Array[], one access unit per entry (required)
durationscodec-specificAAC 1024; ALAC frame length from the cookie; Opus per-packet (TOC); FLAC per-frame (header); MP3 1152/576; PCM computed from chunk length
timescalesampleRate (Opus: 48000)sample-table timescale
config—AAC: AudioSpecificConfig bytes. ALAC: 24-byte ALACSpecificConfig. Opus: { preSkip, outputGain?, channelMappingFamily?, channelMappingTable? }. FLAC: 34-byte STREAMINFO. PCM: { bits, float?, be? }. MP3: unused
priming, padding0encoder delay / trailing pad, PCM samples — written as an edit-list media-time offset, and (AAC) as iTunSMPB
bitrate—bits/sec, written into esds/btrt
opts fieldDefault
brand'M4A ''M4A ' | 'isom' | 'mp42' | 'qt ' — qt writes sowt/in24/fl32 QuickTime PCM atoms instead of ipcm/fpcm
meta—same keys writeMeta reads back — see below
chapters—[{ time (s), title }] — Nero chpl
creationTimenowDate

Sample tables are compact: stts run-length compressed, stsz uses the constant-size shortcut when every AU is the same length, stsc/stco chunk every ~1s or 128 AUs (whichever comes first), co64 only when an offset would exceed 4GB. Everything is written into one preallocated, growable buffer — no per-box array spreads — so muxing a 1-hour AAC track (~155k AUs) takes ~12ms.

remux(src, audio, opts) → Uint8Array

let strippedVideo = remux(videoBytes, null)                 // drop the audio track
let dubbed = remux(videoBytes, { codec: 'opus', ... })       // replace it (track object, mux() shape)
let dubbed2 = remux(videoBytes, mux({ codec: 'alac', ... })) // or a Uint8Array mux() already produced

Video (and any other non-audio track — subtitles, timecode) is copied through unchanged; only its stco/co64 chunk offsets are rewritten (stco→co64 if the new layout pushes an offset past 4GB). A single fresh mdat interleaves video and audio chunks by presentation time so the result still plays progressively. Handles 64-bit box sizes, uuid boxes, mdat before moov, and multiple mdats. Throws a clear error on a fragmented MP4 (moof) — this is a whole-file muxer, not a fragmented-MP4 writer.

writeMeta(bytes, { meta, chapters }) → Uint8Array

Rewrites moov/udta/meta/ilst (+ chpl for chapters) in an existing MP4/M4A. Moov is kept first — not relocated to the end — so when it grows, every track's chunk offsets are shifted by the same delta (only udta's length changes; nothing else in moov does). meta keys are the exact inverse of decode-aac/meta's parseMeta: title artist album albumartist composer genre year track disc comment lyrics copyright bpm key isrc publisher software pictures. key/isrc/publisher are written as ---- freeform atoms (the com.apple.iTunes convention documented by mutagen/AtomicParsley) — no shipped parser in this ecosystem reads them back yet, so they're write-only.

mp4(opts) → { encode(channels), flush(), free() }

let enc = await mp4({ sampleRate: 44100, channels: 2, codec: 'flac' })
enc.encode(channelData)   // buffers; always returns an empty Uint8Array
let file = await enc.flush()
enc.free()

Drives @audio/encode-aac (AAC, browser-only — needs AudioEncoder), @audio/encode-opus/core (Opus), @audio/encode-flac (FLAC), @audio/encode-mp3 (MP3), or plain interleaving (PCM), then mux()s the result. Whole-file container: encode() buffers, flush() returns the complete file (same shape as @audio/encode-qoa). codec defaults to 'aac' when globalThis.AudioEncoder exists (browser), else 'flac'. AAC's encoder delay isn't reported by WebCodecs, so priming defaults to 2112 samples — the standard AAC-LC value every LC encoder from FAAC to FDK-AAC uses; override it if you know better. encode() split into any number of chunks produces byte-identical output to a single whole-buffer call (PCM, Opus, FLAC, MP3, AAC through FDK; WebCodecs AAC is the browser's encoder).

Not done here: mux() doesn't trim MP3/Opus encoder delay and padding for you the way mp4() does (from LAME's Info tag, from the Opus encoder's lookahead): pass priming/padding yourself if you need bit-exact gapless MP3/Opus from mux() (Opus's own pre-skip is handled automatically by mux(), since it's intrinsic to every Opus packet stream, not an encoder-specific unknown). stco→co64 upgrade during writeMeta isn't implemented — it throws instead of writing a wrong offset.

Use when: writing .m4a/.mp4 output in the browser or Node without wasm; swapping the audio track of a video file (dubbing, replacing a soundtrack) without re-encoding video; tagging M4A files.

Spec references

  • ISO/IEC 14496-12 — ISO Base Media File Format (box structure, sample tables, edit lists)
  • ISO/IEC 14496-14 — MP4 file format
  • ISO/IEC 14496-1 §8.3 — esds descriptors (ES_Descriptor/DecoderConfigDescriptor/SLConfigDescriptor)
  • ISO/IEC 23003-5 — PCM in ISOBMFF (ipcm/fpcm/pcmC)
  • Opus in ISOBMFF — Opus sample entry + dOps
  • FLAC in ISOBMFF — fLaC sample entry + dfLa
  • QuickTime File Format — sound sample description versions 0/1/2, sowt/in24/fl32/enda
  • iTunes metadata atoms — mutagen.mp4, AtomicParsley docs
  • Nero chapters (chpl) — format per ffmpeg libavformat/movenc.c mov_write_chpl_tag

Streaming output

stream: true writes a fragmented file (ISO/IEC 14496-12 §8.8) as it encodes: ftyp + moov (sample entry, empty tables, mvex, tags and chapters) once the first units exist, then moof + mdat per second of media. Only the current fragment's units are held. head() after flush() returns the init segment rebuilt with what only the end knows (FLAC's STREAMINFO), or null.

License

MIT

ॐ

版本与发布记录

@audio/encode-mp4 共发布 6 个版本,首次发布于 2026/08/29;过去 12 个月发布了 6 个版本。

版本发布日期距今
1.2.1latest2026/10/071 个月前
1.2.02026/09/291 个月前
1.1.22026/09/271 个月前
1.1.12026/09/261 个月前
1.1.02026/09/261 个月前
1.0.02026/08/292 个月前

关于 @audio/encode-mp4 的常见问题

@audio/encode-mp4 是什么?

Encode PCM audio samples to MP4/M4A (ISO BMFF) — pure JS muxer, remuxer and iTunes-tag writer

如何安装 @audio/encode-mp4?

在项目目录运行 npm install @audio/encode-mp4,或使用 pnpm add / yarn add / bun add。

@audio/encode-mp4 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@audio/encode-mp4 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@audio/encode-mp4 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@audio/encode-mp4 可以商用吗?

@audio/encode-mp4 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@audio/encode-mp4 的下载量有多少?

根据 npm 官方下载统计,@audio/encode-mp4 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,065 次,日均约 69 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@audio/encode-mp4 还在维护吗?

最新版本 1.2.1 发布于 2026/10/07(1 个月前);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/08/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。