@audio/decode-mp4 1.0.0 → 1.1.1

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 +5 -2
  2. package/decode-mp4.js +15 -6
  3. package/package.json +22 -3
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,11 @@ 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) |
34
+ | `ec-3`, `mp4a` (0xA6) | E-AC-3 (Dolby Digital Plus) | [@audio/decode-eac3](../decode-eac3) |
32
35
 
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.
36
+ TrueHD 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
37
 
35
38
  Codec packages are imported dynamically, which is unavailable inside an AudioWorklet — decode in the main thread or a Worker.
36
39
 
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 · E-AC-3 → @audio/decode-eac3
5
+ * DTS → @audio/decode-dts · 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,17 @@ 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 = { 0xE1: 'QCELP' }
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'))
262
+ if (oti === 0xA6) return frames(import('@audio/decode-eac3'))
258
263
  if (!oti || AAC_OTI.has(oti)) {
259
264
  if (!dsi) throw Error('MP4 AAC track has no AudioSpecificConfig')
260
265
  return aac({ asc: dsi })
@@ -265,7 +270,7 @@ async function createCodec({ entry, children }) {
265
270
  if (!children.alac) throw Error('MP4 ALAC track has no magic cookie')
266
271
  return aac({ alac: children.alac })
267
272
  }
268
- if (type === '.mp3') return mp3()
273
+ if (type === '.mp3') return frames(import('@audio/decode-mp3'))
269
274
  if (type === 'fLaC') {
270
275
  if (!children.dfLa) throw Error('MP4 FLAC track has no dfLa box')
271
276
  return flac(children.dfLa.subarray(4))
@@ -275,6 +280,9 @@ async function createCodec({ entry, children }) {
275
280
  return opus(children.dOps)
276
281
  }
277
282
  if (type === 'samr' || type === 'sawb') return amr(type === 'sawb')
283
+ if (type === 'ac-3') return frames(import('@audio/decode-ac3'))
284
+ if (type === 'ec-3') return frames(import('@audio/decode-eac3'))
285
+ if (DTS_TYPES.has(type)) return frames(import('@audio/decode-dts'))
278
286
  let fmt = pcmFormat(entry, children)
279
287
  if (fmt) return pcm(fmt)
280
288
  throw unsupported(UNSUPPORTED[type] || type)
@@ -316,8 +324,9 @@ async function aac(opts) {
316
324
  return { feed: frames => dec.decode(frames), flush: () => dec.flush(), free: () => dec.free() }
317
325
  }
318
326
 
319
- async function mp3() {
320
- let dec = await (await import('@audio/decode-mp3')).decoder()
327
+ // self-synchronizing frame streams (MP3, AC-3, DTS): the codec resyncs on concatenated samples
328
+ async function frames(load) {
329
+ let dec = await (await load).decoder()
321
330
  return { feed: frames => dec.decode(concat(frames)), flush: () => dec.flush?.() ?? EMPTY, free: () => dec.free() }
322
331
  }
323
332
 
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.1",
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,10 +28,13 @@
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
- "@audio/decode-opus": "^1.3.0"
36
+ "@audio/decode-opus": "^1.3.0",
37
+ "@audio/decode-eac3": "^1.0.0"
35
38
  },
36
39
  "devDependencies": {
37
40
  "tst": "^9.2.2"
@@ -39,7 +42,23 @@
39
42
  "engines": {
40
43
  "node": ">=18"
41
44
  },
42
- "keywords": ["mp4", "mov", "m4a", "m4v", "3gp", "quicktime", "isobmff", "demux", "video", "audio", "decode", "decoder", "aac", "alac", "pcm"],
45
+ "keywords": [
46
+ "mp4",
47
+ "mov",
48
+ "m4a",
49
+ "m4v",
50
+ "3gp",
51
+ "quicktime",
52
+ "isobmff",
53
+ "demux",
54
+ "video",
55
+ "audio",
56
+ "decode",
57
+ "decoder",
58
+ "aac",
59
+ "alac",
60
+ "pcm"
61
+ ],
43
62
  "license": "MIT",
44
63
  "author": "audiojs",
45
64
  "homepage": "https://github.com/audiojs/decode/tree/main/packages/decode-mp4",