@audio/decode-mp4 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/decode-mp4.js +13 -6
  3. package/package.json +20 -2
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @audio/decode-mp4
2
2
 
3
3
  Decode the audio track of MP4, MOV, M4A, M4V and 3GP files to PCM float samples.<br>
4
- A pure-JS ISO BMFF demuxer that skips the video track and routes the audio to the matching codec package, loaded on demand.
4
+ A pure-JS ISO BMFF demuxer that skips the video track and routes the audio (AAC, ALAC, MP3, FLAC, Opus, AC-3, DTS, AMR, PCM) to the matching codec package, loaded on demand.
5
5
 
6
6
  [![npm install @audio/decode-mp4](https://nodei.co/npm/@audio/decode-mp4.png?mini=true)](https://npmjs.org/package/@audio/decode-mp4/)
7
7
 
@@ -29,8 +29,10 @@ dec.free()
29
29
  | `samr`, `sawb` | AMR-NB, AMR-WB | [@audio/decode-amr](../decode-amr) |
30
30
  | `sowt`, `twos`, `in24`, `in32`, `fl32`, `fl64`, `lpcm`, `ipcm`, `fpcm`, `raw `, `NONE` | PCM 8–32 bit int, 32/64 bit float, either endianness | built in |
31
31
  | `ulaw`, `alaw` | G.711 | built in |
32
+ | `ac-3`, `mp4a` (0xA5) | AC-3 | [@audio/decode-ac3](../decode-ac3) |
33
+ | `dtsc`, `dtsh`, `dtsl`, `dtse`, `mp4a` (0xA9) | DTS (core) | [@audio/decode-dts](../decode-dts) |
32
34
 
33
- AC-3, E-AC-3 and DTS tracks throw an error naming the codec. Fragmented MP4 (`moof`) is not supported. QuickTime sound descriptions v0, v1 and v2 are handled, including `wave`-wrapped configs and `enda` endianness flags. When `moov` follows `mdat` (typical of camera and QuickTime output) the file is buffered until the header arrives.
35
+ E-AC-3 tracks throw an error naming the codec. Fragmented MP4 (`moof`) is not supported. QuickTime sound descriptions v0, v1 and v2 are handled, including `wave`-wrapped configs and `enda` endianness flags. When `moov` follows `mdat` (typical of camera and QuickTime output) the file is buffered until the header arrives.
34
36
 
35
37
  Codec packages are imported dynamically, which is unavailable inside an AudioWorklet — decode in the main thread or a Worker.
36
38
 
package/decode-mp4.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * MP4 / MOV / M4A / M4V / 3GP audio decoder — ISO BMFF demuxer that routes the audio track to a codec:
3
3
  * AAC, ALAC → @audio/decode-aac · MP3 → @audio/decode-mp3 · FLAC → @audio/decode-flac
4
- * Opus → @audio/decode-opus/core · AMR → @audio/decode-amr · PCM / µ-law / A-law inline.
4
+ * Opus → @audio/decode-opus/core · AMR → @audio/decode-amr · AC-3 → @audio/decode-ac3 · DTS → @audio/decode-dts
5
+ * PCM / µ-law / A-law inline.
5
6
  *
6
7
  * let { channelData, sampleRate } = await decode(mp4buf)
7
8
  * let dec = await decoder(); let result = await dec.decode(chunk)
@@ -248,13 +249,16 @@ function advance(st) {
248
249
  // ===== codec routing =====
249
250
 
250
251
  const MP3_OTI = new Set([0x69, 0x6B]), AAC_OTI = new Set([0x40, 0x66, 0x67, 0x68])
251
- const UNSUPPORTED = { 0xA5: 'AC-3', 0xA6: 'E-AC-3', 0xA9: 'DTS', 0xE1: 'QCELP', 'ac-3': 'AC-3', 'ec-3': 'E-AC-3', 'dtsc': 'DTS', 'dtsh': 'DTS-HD', 'dtsl': 'DTS-HD', 'dtse': 'DTS Express' }
252
+ const UNSUPPORTED = { 0xA6: 'E-AC-3', 0xE1: 'QCELP', 'ec-3': 'E-AC-3' }
253
+ const DTS_TYPES = new Set(['dtsc', 'dtsh', 'dtsl', 'dtse']) // DTS-HD variants carry a decodable core
252
254
 
253
255
  async function createCodec({ entry, children }) {
254
256
  let { type } = entry
255
257
  if (type === 'mp4a') {
256
258
  let { oti, dsi } = children.esds ? parseEsds(children.esds) : {}
257
- if (MP3_OTI.has(oti)) return mp3()
259
+ if (MP3_OTI.has(oti)) return frames(import('@audio/decode-mp3'))
260
+ if (oti === 0xA5) return frames(import('@audio/decode-ac3'))
261
+ if (oti === 0xA9) return frames(import('@audio/decode-dts'))
258
262
  if (!oti || AAC_OTI.has(oti)) {
259
263
  if (!dsi) throw Error('MP4 AAC track has no AudioSpecificConfig')
260
264
  return aac({ asc: dsi })
@@ -265,7 +269,7 @@ async function createCodec({ entry, children }) {
265
269
  if (!children.alac) throw Error('MP4 ALAC track has no magic cookie')
266
270
  return aac({ alac: children.alac })
267
271
  }
268
- if (type === '.mp3') return mp3()
272
+ if (type === '.mp3') return frames(import('@audio/decode-mp3'))
269
273
  if (type === 'fLaC') {
270
274
  if (!children.dfLa) throw Error('MP4 FLAC track has no dfLa box')
271
275
  return flac(children.dfLa.subarray(4))
@@ -275,6 +279,8 @@ async function createCodec({ entry, children }) {
275
279
  return opus(children.dOps)
276
280
  }
277
281
  if (type === 'samr' || type === 'sawb') return amr(type === 'sawb')
282
+ if (type === 'ac-3') return frames(import('@audio/decode-ac3'))
283
+ if (DTS_TYPES.has(type)) return frames(import('@audio/decode-dts'))
278
284
  let fmt = pcmFormat(entry, children)
279
285
  if (fmt) return pcm(fmt)
280
286
  throw unsupported(UNSUPPORTED[type] || type)
@@ -316,8 +322,9 @@ async function aac(opts) {
316
322
  return { feed: frames => dec.decode(frames), flush: () => dec.flush(), free: () => dec.free() }
317
323
  }
318
324
 
319
- async function mp3() {
320
- let dec = await (await import('@audio/decode-mp3')).decoder()
325
+ // self-synchronizing frame streams (MP3, AC-3, DTS): the codec resyncs on concatenated samples
326
+ async function frames(load) {
327
+ let dec = await (await load).decoder()
321
328
  return { feed: frames => dec.decode(concat(frames)), flush: () => dec.flush?.() ?? EMPTY, free: () => dec.free() }
322
329
  }
323
330
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audio/decode-mp4",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Decode the audio track of MP4, MOV, M4A, M4V and 3GP files (AAC, ALAC, MP3, FLAC, Opus, AMR, PCM) to PCM samples",
5
5
  "type": "module",
6
6
  "main": "decode-mp4.js",
@@ -28,7 +28,9 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@audio/decode-aac": "^1.5.0",
31
+ "@audio/decode-ac3": "^1.0.0",
31
32
  "@audio/decode-amr": "^1.3.0",
33
+ "@audio/decode-dts": "^1.0.0",
32
34
  "@audio/decode-flac": "^1.3.0",
33
35
  "@audio/decode-mp3": "^1.3.0",
34
36
  "@audio/decode-opus": "^1.3.0"
@@ -39,7 +41,23 @@
39
41
  "engines": {
40
42
  "node": ">=18"
41
43
  },
42
- "keywords": ["mp4", "mov", "m4a", "m4v", "3gp", "quicktime", "isobmff", "demux", "video", "audio", "decode", "decoder", "aac", "alac", "pcm"],
44
+ "keywords": [
45
+ "mp4",
46
+ "mov",
47
+ "m4a",
48
+ "m4v",
49
+ "3gp",
50
+ "quicktime",
51
+ "isobmff",
52
+ "demux",
53
+ "video",
54
+ "audio",
55
+ "decode",
56
+ "decoder",
57
+ "aac",
58
+ "alac",
59
+ "pcm"
60
+ ],
43
61
  "license": "MIT",
44
62
  "author": "audiojs",
45
63
  "homepage": "https://github.com/audiojs/decode/tree/main/packages/decode-mp4",